#![allow(dead_code)]
use core::arch::asm;
use core::marker::PhantomData;
#[derive(Copy, Clone, Debug)]
pub struct Port<T: PortValue>(pub u16, PhantomData<T>);
impl<T: PortValue> Port<T> {
pub const fn new(addr: u16) -> Self {
Self(addr, PhantomData)
}
pub unsafe fn read(self) -> T {
T::I::read(self.0).into()
}
pub unsafe fn write(self, val: T) {
T::I::write(val.into(), self.0)
}
pub unsafe fn update(self, f: impl FnOnce(T) -> T) {
self.write(f(self.read()))
}
}
pub trait PortValue: Sized + Copy + From<Self::I> + Into<Self::I> {
type I: PortRaw;
}
impl<I: PortRaw> PortValue for I {
type I = Self;
}
pub trait PortRaw: Sized + Copy {
unsafe fn read(addr: u16) -> Self;
unsafe fn write(self, addr: u16);
}
impl PortRaw for u8 {
unsafe fn write(self, addr: u16) {
asm!("out dx, al", in("dx") addr, in("al") self, options(nomem, nostack));
}
unsafe fn read(addr: u16) -> Self {
let out: Self;
asm!("in al, dx", out("al") out, in("dx") addr, options(nomem, nostack));
out
}
}
impl PortRaw for u16 {
unsafe fn write(self, addr: u16) {
asm!("out dx, ax", in("dx") addr, in("ax") self, options(nomem, nostack));
}
unsafe fn read(addr: u16) -> Self {
let out: Self;
asm!("in ax, dx", out("ax") out, in("dx") addr, options(nomem, nostack));
out
}
}
impl PortRaw for u32 {
unsafe fn write(self, addr: u16) {
asm!("out dx, eax", in("dx") addr, in("eax") self, options(nomem, nostack));
}
unsafe fn read(addr: u16) -> Self {
let out: Self;
asm!("in eax, dx", out("eax") out, in("dx") addr, options(nomem, nostack));
out
}
}
pub trait IOMem {
const OFFSET: usize;
}
pub trait VolatileUpdate<T> {
unsafe fn update_volatile(&self, f: impl FnOnce(T) -> T);
}
impl<T> VolatileUpdate<T> for *mut T {
unsafe fn update_volatile(&self, f: impl FnOnce(T) -> T) {
self.write_volatile(f(self.read_volatile()))
}
}