use core::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Sub, SubAssign};
use avr_oxide::concurrency::{interrupt, Isolated};
#[repr(C)]
pub struct Volatile<T>(T);
#[derive(Copy,Clone,Eq,PartialEq)]
#[repr(C)]
pub struct BitField(u8);
pub trait BoundedIncDec {
fn binc_isolated<const BOUND: usize>(&mut self, isotoken: Isolated);
fn bdec_isolated<const BOUND: usize>(&mut self, isotoken: Isolated);
fn binc<const BOUND: usize>(&mut self){
interrupt::isolated(|isotoken| {
self.binc_isolated::<BOUND>(isotoken)
})
}
fn bdec<const BOUND: usize>(&mut self) {
interrupt::isolated(|isotoken|{
self.bdec_isolated::<BOUND>(isotoken)
})
}
}
pub trait BoundedMaths<T,R> {
fn bsub<const BOUND: usize>(&self, rhs: T) -> R {
interrupt::isolated(|isotoken|{
self.bsub_isolated::<BOUND>(isotoken, rhs)
})
}
fn bsub_isolated<const BOUND: usize>(&self, isotoken: Isolated, rhs: T) -> R;
}
#[derive(PartialEq)]
#[repr(C)]
pub struct VolatileBitField(Volatile<u8>);
#[derive(Copy,Clone,Eq,PartialEq)]
#[repr(C)]
pub struct BitIndex(usize);
pub trait BitFieldAccess {
fn read_byte(&self) -> u8;
fn write_byte(&mut self, val: u8);
fn is_set(&self, bit: BitIndex) -> bool {
(self.read_byte() & bit.positive_byte_mask()) > 0
}
fn is_clr(&self, bit: BitIndex) -> bool {
!self.is_set(bit)
}
fn set_isolated(&mut self, _isotoken: Isolated, bit: BitIndex) {
self.set(bit)
}
fn set(&mut self, bit: BitIndex) {
self.write_byte(self.read_byte() | bit.positive_byte_mask());
}
fn set_all(&mut self) {
self.write_byte(0xff);
}
fn clr_all(&mut self) {
self.write_byte(0x00);
}
fn exc_set(&mut self, bit: BitIndex) {
self.write_byte(bit.positive_byte_mask());
}
fn clr_isolated(&mut self, _isotoken: Isolated, bit: BitIndex) {
self.clr(bit)
}
fn clr(&mut self, bit: BitIndex) {
self.write_byte(self.read_byte() & bit.negative_byte_mask());
}
fn exc_clr(&mut self, bit: BitIndex) {
self.write_byte(bit.negative_byte_mask());
}
fn set_or_clr(&mut self, bit: BitIndex, set: bool) {
match set {
true => self.set(bit),
false => self.clr(bit)
}
}
}
impl BitIndex {
pub fn bit(b: usize) -> Self {
#[cfg(feature="runtime_checks")]
if b >= 8 {
avr_oxide::oserror::halt(avr_oxide::oserror::OsError::BadParams);
}
Self {
0: b
}
}
pub const fn bit_c(b: usize) -> Self {
assert!(b < 8);
Self {
0: b
}
}
pub fn bit_index_in_byte(self) -> u8 {
self.0 as u8
}
pub fn positive_byte_mask(self) -> u8 {
0x01u8 << self.bit_index_in_byte()
}
pub fn negative_byte_mask(self) -> u8 {
! self.positive_byte_mask()
}
}
impl BitField {
pub const fn with_initial(vals: u8) -> Self {
BitField {
0: vals
}
}
pub fn with_bits_set(bits: &[BitIndex]) -> Self {
let mut new = Self::all_clr();
for bit in bits {
new.set(*bit);
}
new
}
pub const fn all_clr() -> Self {
BitField {
0: 0u8
}
}
pub const fn all_set() -> Self {
BitField {
0: 0xffu8
}
}
}
impl BitFieldAccess for BitField {
fn read_byte(&self) -> u8 {
self.0
}
fn write_byte(&mut self, val: u8) {
self.0 = val
}
}
impl BitFieldAccess for VolatileBitField {
fn read_byte(&self) -> u8 {
self.0.read()
}
fn write_byte(&mut self, val: u8) {
self.0.write(val);
}
fn set_isolated(&mut self, _isotoken: Isolated, bit: BitIndex) {
self.write_byte(self.read_byte() | bit.positive_byte_mask());
}
fn set(&mut self, bit: BitIndex) {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
self.set_isolated(isotoken, bit)
})
}
fn clr_isolated(&mut self, _isotoken: Isolated, bit: BitIndex) {
self.write_byte(self.read_byte() & bit.negative_byte_mask());
}
fn clr(&mut self, bit: BitIndex) {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
self.clr_isolated(isotoken, bit)
});
}
}
impl VolatileBitField {
pub fn all_clr() -> Self {
VolatileBitField(Volatile::<u8>::zero())
}
pub fn all_set() -> Self {
VolatileBitField(Volatile::<u8>::from(0xff))
}
pub fn snapshot(&self) -> BitField {
BitField::with_initial(self.0.snapshot())
}
}
impl From<u8> for Volatile<u8> {
fn from(val: u8) -> Self {
Volatile(val)
}
}
impl Volatile<u8> {
pub const fn zero() -> Self {
Volatile(0)
}
pub fn snapshot(&self) -> u8 {
unsafe {
core::ptr::read_volatile(&self.0 as *const u8)
}
}
pub fn read(&self) -> u8 {
self.snapshot()
}
pub fn write(&mut self, val: u8) {
unsafe {
core::ptr::write_volatile(&mut self.0 as *mut u8, val)
}
}
}
impl Add<u8> for Volatile<u8> {
type Output = u8;
fn add(self, rhs: u8) -> Self::Output {
self.read() + rhs
}
}
impl AddAssign<u8> for Volatile<u8> {
fn add_assign(&mut self, rhs: u8) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() + rhs);
});
}
}
impl Sub<u8> for Volatile<u8> {
type Output = u8;
fn sub(self, rhs: u8) -> Self::Output {
self.read() - rhs
}
}
impl SubAssign<u8> for Volatile<u8> {
fn sub_assign(&mut self, rhs: u8) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() - rhs);
});
}
}
impl BitAnd<u8> for Volatile<u8> {
type Output = u8;
fn bitand(self, rhs: u8) -> Self::Output {
self.read() & rhs
}
}
impl BitAndAssign<u8> for Volatile<u8> {
fn bitand_assign(&mut self, rhs: u8) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() & rhs);
});
}
}
impl BitOr<u8> for Volatile<u8> {
type Output = u8;
fn bitor(self, rhs: u8) -> Self::Output {
self.read() | rhs
}
}
impl BitOrAssign<u8> for Volatile<u8> {
fn bitor_assign(&mut self, rhs: u8) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() | rhs);
});
}
}
impl BitXor<u8> for Volatile<u8> {
type Output = u8;
fn bitxor(self, rhs: u8) -> Self::Output {
self.read() ^ rhs
}
}
impl BitXorAssign<u8> for Volatile<u8> {
fn bitxor_assign(&mut self, rhs: u8) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() ^ rhs);
});
}
}
impl PartialEq<u8> for Volatile<u8> {
fn eq(&self, other: &u8) -> bool {
self.read() == *other
}
}
impl PartialEq<Volatile<u8>> for Volatile<u8> {
fn eq(&self, other: &Volatile<u8>) -> bool {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.read() == other.read()
})
}
}
impl BoundedIncDec for Volatile<u8> {
fn binc_isolated<const BOUND: usize>(&mut self, _isotoken: Isolated) {
self.write((self.read()+1) % BOUND as u8);
}
fn bdec_isolated<const BOUND: usize>(&mut self, _isotoken: Isolated) {
let val = self.read();
if val > 0 {
self.write(val - 1);
} else {
self.write((BOUND-1) as u8);
}
}
}
impl BoundedMaths<u8,u8> for Volatile<u8> {
fn bsub_isolated<const BOUND: usize>(&self, _isotoken: Isolated, rhs: u8) -> u8 {
let val = self.read();
if val >= rhs {
val - rhs
} else {
(BOUND as u8 - (rhs % BOUND as u8)) + val
}
}
}
impl BoundedMaths<&Volatile<u8>,u8> for Volatile<u8> {
fn bsub_isolated<const BOUND: usize>(&self, _isotoken: Isolated, rhs: &Volatile<u8>) -> u8 {
let val = self.read();
let rhs = rhs.read();
if val >= rhs {
val - rhs
} else {
(BOUND as u8 - (rhs % BOUND as u8)) + val
}
}
}
impl BoundedMaths<u8,u8> for u8 {
fn bsub<const BOUND: usize>(&self, rhs: u8) -> u8 {
let val = *self;
if val >= rhs {
val - rhs
} else {
(BOUND as u8 - (rhs % BOUND as u8)) + val
}
}
fn bsub_isolated<const BOUND: usize>(&self, _isotoken: Isolated, rhs: u8) -> u8 {
self.bsub::<BOUND>(rhs)
}
}
macro_rules! volatile_multibyte_impl {
($innertype:ty) => {
impl From<$innertype> for Volatile<$innertype> {
fn from(val: $innertype) -> Self {
Volatile(val)
}
}
impl Volatile<$innertype> {
pub const fn zero() -> Self {
Self(0 as $innertype)
}
pub fn snapshot(&self) -> $innertype {
unsafe {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
core::ptr::read_volatile(&self.0 as *const $innertype)
})
}
}
pub fn read(&self) -> $innertype {
self.snapshot()
}
pub fn write(&mut self, val: $innertype) {
unsafe {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
core::ptr::write_volatile(&mut self.0 as *mut $innertype, val)
})
}
}
pub fn write_isolated(&mut self, _isotoken: avr_oxide::concurrency::Isolated, val: $innertype) {
unsafe {
core::ptr::write_volatile(&mut self.0 as *mut $innertype, val)
}
}
}
impl Add<$innertype> for Volatile<$innertype> {
type Output = $innertype;
fn add(self, rhs: $innertype) -> Self::Output {
self.read() + rhs
}
}
impl AddAssign<$innertype> for Volatile<$innertype> {
fn add_assign(&mut self, rhs: $innertype) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() + rhs);
});
}
}
impl Sub<$innertype> for Volatile<$innertype> {
type Output = $innertype;
fn sub(self, rhs: $innertype) -> Self::Output {
self.read() - rhs
}
}
impl SubAssign<$innertype> for Volatile<$innertype> {
fn sub_assign(&mut self, rhs: $innertype) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() - rhs);
});
}
}
impl BitAnd<$innertype> for Volatile<$innertype> {
type Output = $innertype;
fn bitand(self, rhs: $innertype) -> Self::Output {
self.read() & rhs
}
}
impl BitAndAssign<$innertype> for Volatile<$innertype> {
fn bitand_assign(&mut self, rhs: $innertype) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() & rhs);
});
}
}
impl BitOr<$innertype> for Volatile<$innertype> {
type Output = $innertype;
fn bitor(self, rhs: $innertype) -> Self::Output {
self.read() | rhs
}
}
impl BitOrAssign<$innertype> for Volatile<$innertype> {
fn bitor_assign(&mut self, rhs: $innertype) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() | rhs);
});
}
}
impl BitXor<$innertype> for Volatile<$innertype> {
type Output = $innertype;
fn bitxor(self, rhs: $innertype) -> Self::Output {
self.read() ^ rhs
}
}
impl BitXorAssign<$innertype> for Volatile<$innertype> {
fn bitxor_assign(&mut self, rhs: $innertype) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write(self.read() ^ rhs);
});
}
}
impl PartialEq<$innertype> for Volatile<$innertype> {
fn eq(&self, other: &$innertype) -> bool {
self.read() == *other
}
}
impl PartialEq<Volatile<$innertype>> for Volatile<$innertype> {
fn eq(&self, other: &Volatile<$innertype>) -> bool {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.read() == other.read()
})
}
}
}
}
volatile_multibyte_impl!(u16);
volatile_multibyte_impl!(u32);
volatile_multibyte_impl!(usize);
#[cfg(test)]
mod tests {
use avr_oxide::util::datatypes::{BitField, BitFieldAccess, BitIndex, BoundedIncDec, BoundedMaths, Volatile, VolatileBitField};
#[test]
fn test_bitindex() {
let bit4 = BitIndex(4);
assert_eq!(bit4.bit_index_in_byte(), 4);
assert_eq!(bit4.positive_byte_mask(), 0b00010000);
assert_eq!(bit4.negative_byte_mask(), 0b11101111);
}
#[test]
fn test_bitfield() {
let mut all_zero : BitField = BitField::all_clr();
let mut all_one : BitField = BitField::all_set();
assert!(all_zero == BitField::with_initial(0b00000000));
assert!(all_one == BitField::with_initial(0b11111111));
all_zero.set(BitIndex(3));
all_one.set(BitIndex(3));
assert!(all_zero == BitField::with_initial(0b00001000));
assert!(all_one == BitField::with_initial(0b11111111));
all_zero.clr(BitIndex(7));
all_one.clr(BitIndex(7));
assert!(all_zero == BitField::with_initial(0b00001000));
assert!(all_one == BitField::with_initial(0b01111111));
assert!(all_one.is_set(BitIndex(6)));
assert!(all_one.is_clr(BitIndex(7)));
assert!(!all_zero.is_set(BitIndex(0)));
assert!(!all_one.is_clr(BitIndex(1)));
}
#[test]
fn test_volatile_bitfield() {
let mut all_zero : VolatileBitField = VolatileBitField::all_clr();
let mut all_one : VolatileBitField = VolatileBitField::all_set();
assert!(all_zero.snapshot() == BitField::with_initial(0b00000000));
assert!(all_one.snapshot() == BitField::with_initial(0b11111111));
all_zero.set(BitIndex(3));
all_one.set(BitIndex(3));
assert!(all_zero.snapshot() == BitField::with_initial(0b00001000));
assert!(all_one.snapshot() == BitField::with_initial(0b11111111));
all_zero.clr(BitIndex(7));
all_one.clr(BitIndex(7));
assert!(all_zero.snapshot() == BitField::with_initial(0b00001000));
assert!(all_one.snapshot() == BitField::with_initial(0b01111111));
assert!(all_one.is_set(BitIndex(6)));
assert!(all_one.is_clr(BitIndex(7)));
assert!(!all_zero.is_set(BitIndex(0)));
assert!(!all_one.is_clr(BitIndex(1)));
}
#[test]
pub fn test_bitfield_init() {
let some_set = BitField::with_bits_set(&[BitIndex(1),BitIndex(7),BitIndex(4)]);
assert!(some_set == BitField::with_initial(0b10010010));
}
#[test]
pub fn test_volatile_u8() {
let mut val : Volatile<u8> = 12u8.into();
assert_eq!(val.read(), 12u8);
val.write(46);
assert_eq!(val.read(), 46u8);
val += 32u8;
assert_eq!(val.read(), 78);
}
#[test]
fn test_bounded_maths() {
let mut val:Volatile<u8> = 0.into();
assert_eq!(val.bsub::<10>(1u8), 9);
assert_eq!(val.read(), 0);
val.binc::<10>();
assert_eq!(val.read(), 1);
val.binc::<10>();
assert_eq!(val.read(), 2);
val.bdec::<10>();
assert_eq!(val.read(), 1);
val.bdec::<10>();
assert_eq!(val.read(), 0);
val.bdec::<10>();
assert_eq!(val.read(), 9);
val.binc::<10>();
assert_eq!(val.read(), 0);
let zero:Volatile<u8> = 0.into();
let one:Volatile<u8> = 1.into();
let five:Volatile<u8> = 5.into();
let four:Volatile<u8> = 4.into();
assert_eq!(five.bsub::<10>(4), 1);
assert_eq!(four.bsub::<10>(5), 9);
assert_eq!(zero.bsub::<10>(1), 9);
assert_eq!(one.bsub::<10>(0), 1);
}
}