#![allow(missing_docs)]
use crate::hw::dma;
use crate::hw::dma::{cdrom, gpu, mdec_in, mdec_out, otc, pio, spu};
use crate::hw::dma::{BlockControl, ChannelControl, MemoryAddress};
use crate::hw::Register;
use core::arch::asm;
use core::convert::TryInto;
type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Error {
OversizedBlock,
BadBlockPartition,
}
#[derive(Debug)]
pub enum BlockMode {
Single(u32),
Multi {
words: u16,
blocks: u16,
},
LinkedList,
}
impl From<u32> for BlockMode {
fn from(words: u32) -> BlockMode {
BlockMode::Single(words)
}
}
impl From<usize> for BlockMode {
fn from(words: usize) -> BlockMode {
BlockMode::Single(words as u32)
}
}
#[derive(Debug)]
pub enum TransferMode {
Immediate = 0,
Request,
LinkedList,
}
#[derive(Debug)]
pub enum Direction {
ToMemory = 0,
FromMemory,
}
#[derive(Debug)]
pub enum Step {
Forward = 0,
Backward,
}
#[derive(Debug)]
pub struct Chop {
pub dma_window: u32,
pub cpu_window: u32,
}
pub trait LinkedList {
fn address(&self) -> Option<&u32>;
}
pub struct Channel<A: MemoryAddress, B: BlockControl, C: ChannelControl> {
madr: A,
bcr: B,
control: C,
}
pub type GPU = Channel<gpu::Address, gpu::Block, gpu::Control>;
pub type MDECIn = Channel<mdec_in::Address, mdec_in::Block, mdec_in::Control>;
pub type MDECOut = Channel<mdec_out::Address, mdec_out::Block, mdec_out::Control>;
pub type CDROM = Channel<cdrom::Address, cdrom::Block, cdrom::Control>;
pub type SPU = Channel<spu::Address, spu::Block, spu::Control>;
pub type PIO = Channel<pio::Address, pio::Block, pio::Control>;
pub type OTC = Channel<otc::Address, otc::Block, otc::Control>;
impl<A: MemoryAddress, B: BlockControl, C: ChannelControl> Channel<A, B, C> {
pub fn new() -> Self {
let mut ctrl = dma::GlobalControl::new();
if !ctrl.enabled(C::NAME) {
ctrl.enable(C::NAME).store();
}
Channel {
madr: A::skip_load(),
bcr: B::skip_load(),
control: C::new(),
}
}
fn block_address<'b>(&self, block: &'b [u32]) -> Option<&'b u32> {
match self.control.get_step() {
Step::Forward => block.first(),
Step::Backward => block.last(),
}
}
pub fn send_and<F: FnOnce() -> R, R>(&mut self, block: &[u32], f: F) -> Result<R> {
let addr = match self.block_address(block) {
Some(addr) => addr,
None => return Ok(f()),
};
self.madr.set_address(addr).store();
self.bcr.set_block(block.len())?.store();
self.control
.set_mode(TransferMode::Immediate)
.start()
.store();
unsafe {
asm!("nop");
}
let res = f();
self.control.wait();
unsafe {
asm!("nop");
}
Ok(res)
}
pub fn send_blocks_and<F: FnOnce() -> R, R>(
&mut self, block: &[u32], size: usize, f: F,
) -> Result<R> {
let addr = match self.block_address(block) {
Some(addr) => addr,
None => return Ok(f()),
};
self.madr.set_address(addr).store();
if block.len() % size != 0 {
return Err(Error::BadBlockPartition)
}
let words = (block.len() / size)
.try_into()
.map_err(|_| Error::OversizedBlock)?;
let block_len = BlockMode::Multi {
words,
blocks: size.try_into().map_err(|_| Error::OversizedBlock)?,
};
self.bcr.set_block(block_len)?.store();
self.control.start().store();
unsafe {
asm!("nop");
}
let res = f();
self.control.wait();
unsafe {
asm!("nop");
}
Ok(res)
}
pub fn send_list_and<F: FnOnce() -> R, R, L: LinkedList + ?Sized>(
&mut self, list: &L, f: F,
) -> R {
let ptr = match list.address() {
Some(ptr) => ptr,
None => return f(),
};
self.madr.set_address(ptr).store();
self.control
.set_direction(Direction::FromMemory)
.set_step(Step::Forward)
.set_mode(TransferMode::LinkedList)
.start()
.store();
unsafe {
asm!("nop");
}
let res = f();
self.control.wait();
unsafe {
asm!("nop");
}
res
}
pub fn send_list<L: LinkedList + ?Sized>(&mut self, list: &L) {
self.send_list_and(list, || ())
}
}