use std::num::FpCategory;
use super::registers::{Status, Control, Tag, TagValue};
use crate::common::f80::{self, F80};
pub struct FPU {
pub vals: [F80; 8],
pub control: Control,
pub status: Status,
pub tag: Tag,
}
impl FPU {
pub fn new() -> Self {
FPU {
vals: [f80::POSITIVE_ZERO; 8],
control: Control(0x3bf),
status: Status(0),
tag: Tag(0xffff),
}
}
pub fn reset(&mut self) {
self.control.0 = 0x3bf;
self.status.0 = 0;
self.tag.0 = 0xffff;
}
pub fn clear_exceptions(&mut self) {
self.status.0 &= 0x7f00;
}
pub fn st_to_physical(&self, st: u8) -> u8 {
assert!(st < 8);
(st + self.status.get_top()) & 7
}
fn set_physical(&mut self, physical: u8, val: F80) {
self.vals[physical as usize] = val;
self.tag.set_physical(physical, match val.classify() {
FpCategory::Infinite | FpCategory::Nan => TagValue::Special,
FpCategory::Zero => TagValue::Zero,
_ => TagValue::NonZero,
});
}
pub fn set_st(&mut self, st: u8, val: F80) { self.set_physical(self.st_to_physical(st), val) }
pub fn get_st(&self, st: u8) -> Option<F80> {
let physical = self.st_to_physical(st);
if self.tag.get_physical(physical) != TagValue::Empty as u8 { Some(self.vals[physical as usize]) } else { None }
}
pub fn free_st(&mut self, st: u8) { self.tag.set_physical(self.st_to_physical(st), TagValue::Empty) }
pub fn push(&mut self, val: F80) -> Result<(), ()> {
let physical = self.status.get_top().wrapping_sub(1) & 7;
if self.tag.get_physical(physical) != TagValue::Empty as u8 {
return Err(());
}
self.set_physical(physical, val); self.status.assign_top(physical);
Ok(())
}
pub fn pop(&mut self) -> Result<(), ()> {
let physical = self.status.get_top();
if self.tag.get_physical(physical) == TagValue::Empty as u8 {
return Err(());
}
self.tag.set_physical(physical, TagValue::Empty);
self.status.assign_top((physical + 1) & 7);
Ok(())
}
}
impl Default for FPU {
fn default() -> FPU {
FPU::new()
}
}