use core::fmt::{self, Write};
use core::slice;
const ADDRESS_BASE: usize = 0xE000_0000;
pub struct Port(usize);
pub trait Transmit: Copy {
fn transmit(self, address: usize);
}
impl Port {
pub fn new(port: usize) -> Port {
assert!(port < 0x20);
Port(ADDRESS_BASE + port)
}
pub fn write_stream(&self, buffer: &[u8]) {
let mut end = buffer.len();
if end < 4 {
return self.write_all(buffer);
}
let mut start = buffer.as_ptr() as usize;
let mut rem = start & 0b11;
end += start;
if rem != 0 {
rem = 0b100 - rem;
self.write_all(unsafe { slice::from_raw_parts(start as *const u8, rem) });
start += rem;
}
rem = end & 0b11;
end -= rem;
self.write_all(unsafe {
slice::from_raw_parts(start as *const u32, end - start >> 2)
});
self.write_all(unsafe { slice::from_raw_parts(end as *const u8, rem) });
}
pub fn write_all<T: Transmit>(&self, buffer: &[T]) {
for item in buffer {
self.write(*item);
}
}
pub fn write<T: Transmit>(&self, value: T) {
value.transmit(self.0);
}
}
impl Write for Port {
fn write_str(&mut self, string: &str) -> fmt::Result {
self.write_stream(string.as_bytes());
Ok(())
}
}
impl Transmit for u8 {
fn transmit(self, address: usize) {
unsafe {
asm!("
0:
ldrexb r0, [$1]
cmp r0, #0
itt ne
strexbne r0, $0, [$1]
cmpne r0, #1
beq 0b
" :
: "r"(self), "r"(address as *mut u8)
: "r0", "cc"
: "volatile");
}
}
}
impl Transmit for u16 {
fn transmit(self, address: usize) {
unsafe {
asm!("
0:
ldrexh r0, [$1]
cmp r0, #0
itt ne
strexhne r0, $0, [$1]
cmpne r0, #1
beq 0b
" :
: "r"(self), "r"(address as *mut u16)
: "r0", "cc"
: "volatile");
}
}
}
impl Transmit for u32 {
fn transmit(self, address: usize) {
unsafe {
asm!("
0:
ldrex r0, [$1]
cmp r0, #0
itt ne
strexne r0, $0, [$1]
cmpne r0, #1
beq 0b
" :
: "r"(self), "r"(address as *mut u32)
: "r0", "cc"
: "volatile");
}
}
}