mirror of
https://github.com/italicsjenga/gba.git
synced 2025-01-26 01:16:33 +11:00
commit
f603cdbcce
6 changed files with 405 additions and 3 deletions
|
@ -17,6 +17,8 @@ before_script:
|
||||||
- cargo install-update -a
|
- cargo install-update -a
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
# Travis seems to cache for some dumb reason, but we don't want that at all.
|
||||||
|
- rm -fr target
|
||||||
# Obtain the devkitPro tools, using `target/` as a temp directory
|
# Obtain the devkitPro tools, using `target/` as a temp directory
|
||||||
- mkdir -p target
|
- mkdir -p target
|
||||||
- cd target
|
- cd target
|
||||||
|
|
|
@ -1 +1,133 @@
|
||||||
# Direct Memory Access
|
# Direct Memory Access
|
||||||
|
|
||||||
|
The GBA has four Direct Memory Access (DMA) units that can be utilized. They're
|
||||||
|
mostly the same in terms of overall operation, but each unit has special rules
|
||||||
|
that make it better suited to a particular task.
|
||||||
|
|
||||||
|
**Please Note:** TONC and GBATEK have slightly different concepts of how a DMA
|
||||||
|
unit's registers should be viewed. I've chosen to go by what GBATEK uses.
|
||||||
|
|
||||||
|
## General DMA
|
||||||
|
|
||||||
|
A single DMA unit is controlled through four different IO Registers.
|
||||||
|
|
||||||
|
* **Source:** (`DMAxSAD`, read only) A `*const` pointer that the DMA reads from.
|
||||||
|
* **Destination:** (`DMAxDAD`, read only) A `*mut` pointer that the DMA writes
|
||||||
|
to.
|
||||||
|
* **Count:** (`DMAxCNT_L`, read only) How many transfers to perform.
|
||||||
|
* **Control:** (`DMAxCNT_H`, read/write) A register full of bit-flags that
|
||||||
|
controls all sorts of details.
|
||||||
|
|
||||||
|
Here, the `x` is replaced with 0 through 3 when utilizing whichever particular
|
||||||
|
DMA unit.
|
||||||
|
|
||||||
|
### Source Address
|
||||||
|
|
||||||
|
This is either a `u32` or `u16` address depending on the unit's assigned
|
||||||
|
transfer mode (see Control). The address MUST be aligned.
|
||||||
|
|
||||||
|
With DMA0 the source must be internal memory. With other DMA units the source
|
||||||
|
can be any non-`SRAM` location.
|
||||||
|
|
||||||
|
### Destination Address
|
||||||
|
|
||||||
|
As with the Source, this is either a `u32` or `u16` address depending on the
|
||||||
|
unit's assigned transfer mode (see Control). The address MUST be aligned.
|
||||||
|
|
||||||
|
With DMA0/1/2 the destination must be internal memory. With DMA3 the destination
|
||||||
|
can be any non-`SRAM` memory (allowing writes into Game Pak ROM / FlashROM,
|
||||||
|
assuming that your Game Pak hardware supports that).
|
||||||
|
|
||||||
|
### Count
|
||||||
|
|
||||||
|
This is a `u16` that says how many transfers (`u16` or `u32`) to make.
|
||||||
|
|
||||||
|
DMA0/1/2 will only actually accept a 14-bit value, while DMA3 will accept a full
|
||||||
|
16-bit value. A value of 0 instead acts as if you'd used the _maximum_ value for
|
||||||
|
the DMA in question. Put another way, DMA0/1/2 transfer `1` through `0x4000`
|
||||||
|
words, with `0` as the `0x4000` value, and DMA3 transfers `1` through `0x1_0000`
|
||||||
|
words, with `0` as the `0x1_0000` value.
|
||||||
|
|
||||||
|
The maximum value isn't a very harsh limit. Even in just `u16` mode, `0x4000`
|
||||||
|
transfers is 32k, which would for example be all 32k of `IWRAM` (including your
|
||||||
|
own user stack). If you for some reason do need to transfer more than a single
|
||||||
|
DMA use can move around at once then you can just setup the DMA a second time
|
||||||
|
and keep going.
|
||||||
|
|
||||||
|
### Control
|
||||||
|
|
||||||
|
This `u16` bit-flag field is where things get wild.
|
||||||
|
|
||||||
|
* Bits 0-4 do nothing
|
||||||
|
* Bit 5-6 control how the destination address changes per transfer:
|
||||||
|
* 0: Offset +1
|
||||||
|
* 1: Offset -1
|
||||||
|
* 2: No Change
|
||||||
|
* 3: Offset +1 and reload when a Repeat starts (below)
|
||||||
|
* Bit 7-8 similarly control how the source address changes per transfer:
|
||||||
|
* 0: Offset +1
|
||||||
|
* 1: Offset -1
|
||||||
|
* 2: No Change
|
||||||
|
* 3: Prohibited
|
||||||
|
* Bit 9: enables Repeat mode.
|
||||||
|
* Bit 10: Transfer `u16` (false) or `u32` (true) data.
|
||||||
|
* Bit 11: "Game Pak DRQ" flag. GBATEK says that this is only allowed for DMA3,
|
||||||
|
and also your Game Pak hardware must be equipped to use DRQ mode. I don't even
|
||||||
|
know what DRQ mode is all about, and GBATEK doesn't say much either. If DRQ is
|
||||||
|
set then you _must not_ set the Repeat bit as well. The `gba` crate simply
|
||||||
|
doesn't bother to expose this flag to users.
|
||||||
|
* Bit 12-13: DMA Start:
|
||||||
|
* 0: "Immediate", which is 2 cycles after requested.
|
||||||
|
* 1: VBlank
|
||||||
|
* 2: HBlank
|
||||||
|
* 3: Special, depending on what DMA unit is involved:
|
||||||
|
* DMA0: Prohibited.
|
||||||
|
* DMA1/2: Sound FIFO (see the [Sound](04-sound.md) section)
|
||||||
|
* DMA3: Video Capture, intended for use with the Repeat flag, performs a
|
||||||
|
transfer per scanline (similar to HBlank) starting at `VCOUNT` 2 and
|
||||||
|
stopping at `VCOUNT` 162. Intended for copying things from ROM or camera
|
||||||
|
into VRAM.
|
||||||
|
* Bit 14: Interrupt upon DMA complete.
|
||||||
|
* Bit 15: Enable this DMA unit.
|
||||||
|
|
||||||
|
## DMA Life Cycle
|
||||||
|
|
||||||
|
The general technique for using a DMA unit involves first setting the relevent
|
||||||
|
source, destination, and count registers, then setting the appropriate control
|
||||||
|
register value with the Enable bit set.
|
||||||
|
|
||||||
|
Once the Enable flag is set the appropriate DMA unit will trigger at the
|
||||||
|
assigned time (Bit 12-13). The CPU's operation is halted while any DMA unit is
|
||||||
|
active, until the DMA completes its task. If more than one DMA unit is supposed
|
||||||
|
to be active at once, then the DMA unit with the lower number will activate and
|
||||||
|
complete before any others.
|
||||||
|
|
||||||
|
When the DMA triggers via _Enable_, the `Source`, `Destination`, and `Count`
|
||||||
|
values are copied from the GBA's registers into the DMA unit's internal
|
||||||
|
registers. Changes to the DMA unit's internal copy of the data don't affect the
|
||||||
|
values in the GBA registers. Another _Enable_ will read the same values as
|
||||||
|
before.
|
||||||
|
|
||||||
|
If DMA is triggered via having _Repeat_ active then _only_ the Count is copied
|
||||||
|
in to the DMA unit registers. The `Source` and `Destination` are unaffected
|
||||||
|
during a Repeat. The exception to this is if the destination address control
|
||||||
|
value (Bits 5-6) are set to 3 (`0b11`), in which case a _Repeat_ will also
|
||||||
|
re-copy the `Destination` as well as the `Count`.
|
||||||
|
|
||||||
|
Once a DMA operation completes, the Enable flag of its Control register will
|
||||||
|
automatically be disabled, _unless_ the Repeat flag is on, in which case the
|
||||||
|
Enable flag is left active. You will have to manually disable it if you don't
|
||||||
|
want the DMA to kick in again over and over at the specified starting time.
|
||||||
|
|
||||||
|
## DMA Limitations
|
||||||
|
|
||||||
|
The DMA units cannot access `SRAM` at all.
|
||||||
|
|
||||||
|
If you're using HBlank to access any part of the memory that the display
|
||||||
|
controller utilizes (`OAM`, `PALRAM`, `VRAM`), you need to have enabled the
|
||||||
|
"HBlank Interval Free" bit in the Display Control Register (`DISPCNT`).
|
||||||
|
|
||||||
|
Whenever DMA is active the CPU is _not_ active, which means that
|
||||||
|
[Interrupts](05-interrupts.md) will not fire while DMA is happening. This can
|
||||||
|
cause any number of hard to track down bugs. Try to limit your use of the DMA
|
||||||
|
units if you can.
|
||||||
|
|
|
@ -46,7 +46,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
let color_here = Mode3::read_pixel(px, py);
|
let color_here = Mode3::read_pixel(px, py);
|
||||||
if color_here != Some(BLACK) {
|
if color_here != Some(BLACK) {
|
||||||
// crashed into our own line, reset the screen
|
// crashed into our own line, reset the screen
|
||||||
Mode3::clear_to(BLACK);
|
Mode3::dma_clear_to(BLACK);
|
||||||
color = color.rotate_left(5);
|
color = color.rotate_left(5);
|
||||||
} else {
|
} else {
|
||||||
// draw the new part of the line
|
// draw the new part of the line
|
||||||
|
|
|
@ -11,4 +11,5 @@ use super::*;
|
||||||
use gba_proc_macro::register_bit;
|
use gba_proc_macro::register_bit;
|
||||||
|
|
||||||
pub mod display;
|
pub mod display;
|
||||||
|
pub mod dma;
|
||||||
pub mod keypad;
|
pub mod keypad;
|
||||||
|
|
258
src/io/dma.rs
Normal file
258
src/io/dma.rs
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
//! Module for using the four Direct Memory Access (DMA) units.
|
||||||
|
//!
|
||||||
|
//! The GBA has four DMA units, numbered 0 through 3. If you ever try to have
|
||||||
|
//! more than one active at once the lowest numbered DMA will take priority and
|
||||||
|
//! complete first. Any use of DMA halts the CPU's operation. DMA can also be
|
||||||
|
//! configured to activate automatically at certain times, and when configured
|
||||||
|
//! like that the CPU runs in between the automatic DMA activations. (This is
|
||||||
|
//! actually the intended method for doing sound.) Each DMA unit has an intended
|
||||||
|
//! use:
|
||||||
|
//!
|
||||||
|
//! * DMA0: highest priority, but can only read from internal memory.
|
||||||
|
//! * DMA1/DMA2: Intended for sound transfers.
|
||||||
|
//! * DMA3: Can be used to write into Game Pak ROM / FlashROM (not SRAM).
|
||||||
|
//!
|
||||||
|
//! ## DMA Anatomy
|
||||||
|
//!
|
||||||
|
//! Each DMA is utilized via a combination four IO registers:
|
||||||
|
//!
|
||||||
|
//! * **Source Address:** (`*const u32`) Where to read from. DMA0 can only read
|
||||||
|
//! from internal memory, the other units can read from any non-SRAM memory.
|
||||||
|
//! * **Destination Address:** (`*mut u32`) Where to write to. DMA0/1/2 can only
|
||||||
|
//! write to internal memory, DMA3 can write to any non-SRAM memory.
|
||||||
|
//! * **Word Count:** (`u16`) How many units to transfer. Despite being called
|
||||||
|
//! "word count" you can also use DMA to transfer half-words. DMA0/1/2 are
|
||||||
|
//! limited to a 14-bit counter value, DMA3 allowed the full 16-bit range to
|
||||||
|
//! be used for the counter. Note that even when transferring half-words you
|
||||||
|
//! MUST have both Source and Destination be 32-bit aligned.
|
||||||
|
//! * **Control:** (`DMAControlSetting`) This is one of those fiddly bit-flag
|
||||||
|
//! registers with all sorts of settings. See the type for more info.
|
||||||
|
//!
|
||||||
|
//! Note that Source, Destination, and Count are all read-only, while the
|
||||||
|
//! Control is read/write. When a DMA unit is _Enabled_ it copies the relevent
|
||||||
|
//! Source, Destination, and Count values into its own internal registers (so a
|
||||||
|
//! second Enable will reuse the old values). If the DMA _Repeats_ it re-copies
|
||||||
|
//! the Count, and also the Destination if
|
||||||
|
//! `DMADestAddressControl::IncrementReload` is configured in the Control, but
|
||||||
|
//! not the Source.
|
||||||
|
//!
|
||||||
|
//! When the DMA completes the Enable bit will be cleared from the Control,
|
||||||
|
//! unless the Repeat bit is set in the Control, in which case the Enable bit is
|
||||||
|
//! left active and the DMA will automatically activate again at the right time
|
||||||
|
//! (depending on the Start Timing setting). You have to manually turn off the
|
||||||
|
//! correct bit to stop the DMA unit.
|
||||||
|
//!
|
||||||
|
//! ## Safety
|
||||||
|
//!
|
||||||
|
//! As you might have noticed by now, utilizing DMA can be very fiddly. It moves
|
||||||
|
//! around bytes with no concern for the type system, including the `Clone` and
|
||||||
|
//! `Copy` traits that Rust relies on. Use of DMA can be made _somewhat safe_
|
||||||
|
//! via wrapper methods (such as those we've provided), but it's fundamentally
|
||||||
|
//! an unsafe thing to use.
|
||||||
|
//!
|
||||||
|
//! ## DMA Can Cause Subtle Bugs
|
||||||
|
//!
|
||||||
|
//! Since the CPU is halted while DMA is active you can miss out on interrupts
|
||||||
|
//! that should have fired. This can cause any number of unintended effects. DMA
|
||||||
|
//! is primarily intended for loading large amounts of graphical data from ROM,
|
||||||
|
//! or loading sound data at regulated intervals to avoid pops and crackles. It
|
||||||
|
//! _can_ be used for general purpose bulk transfers but you are advised to use
|
||||||
|
//! restraint.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
newtype! {
|
||||||
|
/// Allows you to configure a DMA unit.
|
||||||
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||||
|
DMAControlSetting, u16
|
||||||
|
}
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
impl DMAControlSetting {
|
||||||
|
pub const DEST_ADDR_CONTROL_MASK: u16 = 0b11 << 5;
|
||||||
|
pub fn dest_address_control(self) -> DMADestAddressControl {
|
||||||
|
// TODO: constify
|
||||||
|
match (self.0 & Self::DEST_ADDR_CONTROL_MASK) >> 5 {
|
||||||
|
0 => DMADestAddressControl::Increment,
|
||||||
|
1 => DMADestAddressControl::Decrement,
|
||||||
|
2 => DMADestAddressControl::Fixed,
|
||||||
|
3 => DMADestAddressControl::IncrementReload,
|
||||||
|
_ => unsafe { core::hint::unreachable_unchecked() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn with_dest_address_control(self, new_control: DMADestAddressControl) -> Self {
|
||||||
|
Self((self.0 & !Self::DEST_ADDR_CONTROL_MASK) | ((new_control as u16) << 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SRC_ADDR_CONTROL_MASK: u16 = 0b11 << 7;
|
||||||
|
pub fn src_address_control(self) -> DMASrcAddressControl {
|
||||||
|
// TODO: constify
|
||||||
|
match (self.0 & Self::SRC_ADDR_CONTROL_MASK) >> 7 {
|
||||||
|
0 => DMASrcAddressControl::Increment,
|
||||||
|
1 => DMASrcAddressControl::Decrement,
|
||||||
|
2 => DMASrcAddressControl::Fixed,
|
||||||
|
_ => unreachable!(), // TODO: custom error message?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn with_src_address_control(self, new_control: DMASrcAddressControl) -> Self {
|
||||||
|
Self((self.0 & !Self::SRC_ADDR_CONTROL_MASK) | ((new_control as u16) << 7))
|
||||||
|
}
|
||||||
|
|
||||||
|
register_bit!(REPEAT, u16, 1 << 9, repeat);
|
||||||
|
register_bit!(TRANSFER_U32, u16, 1 << 10, transfer_u32);
|
||||||
|
// TODO: Game Pak DRQ? (bit 11) DMA3 only, and requires specific hardware
|
||||||
|
|
||||||
|
pub const START_TIMING_MASK: u16 = 0b11 << 12;
|
||||||
|
pub fn start_timing(self) -> DMAStartTiming {
|
||||||
|
// TODO: constify
|
||||||
|
match (self.0 & Self::DEST_ADDR_CONTROL_MASK) >> 12 {
|
||||||
|
0 => DMAStartTiming::Immediate,
|
||||||
|
1 => DMAStartTiming::VBlank,
|
||||||
|
2 => DMAStartTiming::HBlank,
|
||||||
|
3 => DMAStartTiming::Special,
|
||||||
|
_ => unsafe { core::hint::unreachable_unchecked() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn with_start_timing(self, new_control: DMAStartTiming) -> Self {
|
||||||
|
Self((self.0 & !Self::START_TIMING_MASK) | ((new_control as u16) << 12))
|
||||||
|
}
|
||||||
|
|
||||||
|
register_bit!(IRQ_AT_END, u16, 1 << 14, irq_at_end);
|
||||||
|
register_bit!(ENABLE, u16, 1 << 15, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets how the destination address should be adjusted per data transfer.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DMADestAddressControl {
|
||||||
|
/// Offset +1
|
||||||
|
Increment = 0,
|
||||||
|
/// Offset -1
|
||||||
|
Decrement = 1,
|
||||||
|
/// No change
|
||||||
|
Fixed = 2,
|
||||||
|
/// Offset +1 per transfer and auto-reset to base when the DMA repeats.
|
||||||
|
IncrementReload = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets how the source address should be adjusted per data transfer.
|
||||||
|
///
|
||||||
|
/// Note that only 0,1,2 are allowed, 3 is prohibited.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DMASrcAddressControl {
|
||||||
|
/// Offset +1
|
||||||
|
Increment = 0,
|
||||||
|
/// Offset -1
|
||||||
|
Decrement = 1,
|
||||||
|
/// No change
|
||||||
|
Fixed = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets when the DMA should activate.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DMAStartTiming {
|
||||||
|
/// Causes the DMA to start as soon as possible (2 wait cycles after enabled)
|
||||||
|
Immediate = 0,
|
||||||
|
/// Start at VBlank
|
||||||
|
VBlank = 1,
|
||||||
|
/// Start at HBlank
|
||||||
|
HBlank = 2,
|
||||||
|
/// The special timing depends on the DMA it's used with:
|
||||||
|
/// * 0: Prohibited
|
||||||
|
/// * 1/2: Sound FIFO,
|
||||||
|
/// * 3: Video Capture, for transferring from memory/camera into VRAM
|
||||||
|
Special = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the "general purpose" DMA unit, with the fewest limits.
|
||||||
|
pub struct DMA3;
|
||||||
|
impl DMA3 {
|
||||||
|
/// DMA 3 Source Address, read only.
|
||||||
|
const DMA3SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00D4) };
|
||||||
|
/// DMA 3 Destination Address, read only.
|
||||||
|
const DMA3DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00D8) };
|
||||||
|
/// DMA 3 Word Count, read only.
|
||||||
|
const DMA3CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00DC) };
|
||||||
|
/// DMA 3 Control, read/write.
|
||||||
|
const DMA3CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00DE) };
|
||||||
|
|
||||||
|
/// Assigns the source register.
|
||||||
|
///
|
||||||
|
/// This register is read only, so it is not exposed directly.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The source pointer must be aligned and valid to read from.
|
||||||
|
pub unsafe fn set_source(src: *const u32) {
|
||||||
|
Self::DMA3SAD.write(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assigns the destination register.
|
||||||
|
///
|
||||||
|
/// This register is read only, so it is not exposed directly.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The source pointer must be aligned and valid to write to.
|
||||||
|
pub unsafe fn set_dest(dest: *mut u32) {
|
||||||
|
Self::DMA3DAD.write(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assigns the count register.
|
||||||
|
///
|
||||||
|
/// This register is read only, so it is not exposed directly.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The count given must specify a valid number of units to write, starting at
|
||||||
|
/// the assigned destination address.
|
||||||
|
pub unsafe fn set_count(count: u16) {
|
||||||
|
Self::DMA3CNT_L.write(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the current control setting.
|
||||||
|
pub fn control() -> DMAControlSetting {
|
||||||
|
Self::DMA3CNT_H.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the control setting given.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// You must ensure that the Source, Destination, and Count values are set
|
||||||
|
/// correctly **before** you activate the Enable bit.
|
||||||
|
pub unsafe fn set_control(setting: DMAControlSetting) {
|
||||||
|
Self::DMA3CNT_H.write(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills `count` slots (starting at `dest`) with the value at `src`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Both pointers must be aligned, and all positions specified for writing
|
||||||
|
/// must be valid for writing.
|
||||||
|
pub unsafe fn fill32(src: *const u32, dest: *mut u32, count: u16) {
|
||||||
|
const FILL_CONTROL: DMAControlSetting = DMAControlSetting::new()
|
||||||
|
.with_src_address_control(DMASrcAddressControl::Fixed)
|
||||||
|
.with_transfer_u32(true)
|
||||||
|
.with_enable(true);
|
||||||
|
// TODO: destination checking against SRAM
|
||||||
|
Self::DMA3SAD.write(src);
|
||||||
|
Self::DMA3DAD.write(dest);
|
||||||
|
Self::DMA3CNT_L.write(count);
|
||||||
|
Self::DMA3CNT_H.write(FILL_CONTROL);
|
||||||
|
// Note(Lokathor): Once DMA is set to activate it takes 2 cycles for it to
|
||||||
|
// kick in. You can do any non-DMA thing you like before that, but since
|
||||||
|
// it's only two cycles we just insert two NOP instructions to ensure that
|
||||||
|
// successive calls to `fill32` or other DMA methods don't interfere with
|
||||||
|
// each other.
|
||||||
|
asm!(/* ASM */ "NOP
|
||||||
|
NOP"
|
||||||
|
:/* OUT */ // none
|
||||||
|
:/* INP */ // none
|
||||||
|
:/* CLO */ // none
|
||||||
|
:/* OPT */ "volatile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
13
src/video.rs
13
src/video.rs
|
@ -48,9 +48,11 @@ impl Mode3 {
|
||||||
pub const VRAM: VolAddressBlock<Color> =
|
pub const VRAM: VolAddressBlock<Color> =
|
||||||
unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_WIDTH * Self::SCREEN_HEIGHT) };
|
unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_WIDTH * Self::SCREEN_HEIGHT) };
|
||||||
|
|
||||||
|
const MODE3_U32_COUNT: u16 = (Self::SCREEN_WIDTH * Self::SCREEN_HEIGHT / 2) as u16;
|
||||||
|
|
||||||
/// private iterator over the pixels, two at a time
|
/// private iterator over the pixels, two at a time
|
||||||
const BULK_ITER: VolAddressIter<u32> =
|
const BULK_ITER: VolAddressIter<u32> =
|
||||||
unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_WIDTH * Self::SCREEN_HEIGHT / 2).iter() };
|
unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::MODE3_U32_COUNT as usize).iter() };
|
||||||
|
|
||||||
/// Reads the pixel at the given (col,row).
|
/// Reads the pixel at the given (col,row).
|
||||||
///
|
///
|
||||||
|
@ -79,5 +81,12 @@ impl Mode3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: dma_clear_to?
|
/// Clears the whole screen to the desired color using DMA3.
|
||||||
|
pub fn dma_clear_to(color: Color) {
|
||||||
|
use crate::io::dma::DMA3;
|
||||||
|
|
||||||
|
let color32 = color.0 as u32;
|
||||||
|
let bulk_color = color32 << 16 | color32;
|
||||||
|
unsafe { DMA3::fill32(&bulk_color, VRAM_BASE_USIZE as *mut u32, Self::MODE3_U32_COUNT) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue