use crate::fpu::F80;
use log::info;
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct FPUStack {
regs: [F80; 8],
top: u8,
depth: u8,
log: bool,
invalid_flag: u8,
}
impl FPUStack {
pub fn new() -> Self {
Self {
regs: [F80::new(); 8],
top: 0,
depth: 0,
log: false,
invalid_flag: 0,
}
}
pub fn len(&self) -> usize {
8
}
pub fn print(&self) {
info!("top: {}", self.top);
info!("depth: {}", self.depth);
for i in (0..8).rev() {
info!(
"ST({}) 0x{:x} {}",
i,
self.regs[i].get(),
self.regs[i].get_f64()
);
}
info!("invalid_flag: {:b}", self.invalid_flag);
}
pub fn get_top(&mut self) -> u8 {
self.top
}
pub fn inc_top(&mut self) {
self.top = (self.top + 1) & 7;
}
pub fn dec_top(&mut self) {
self.top = (self.top.wrapping_sub(1)) & 7;
}
pub fn set_invalid(&mut self, idx: usize) {
assert!(idx < 8);
self.invalid_flag |= 1 << idx;
}
pub fn clear_invalid(&mut self, idx: usize) {
assert!(idx < 8);
self.invalid_flag &= !(1 << idx);
}
pub fn is_invalid(&self, idx: usize) -> bool {
assert!(idx < 8);
(self.invalid_flag & (1 << idx)) != 0
}
pub fn push_f64(&mut self, value: f64) {
if self.depth >= 8 {
panic!("FPU stack overflow");
}
self.top = (self.top.wrapping_sub(1)) & 7;
self.regs[self.top as usize].set_f64(value);
self.depth += 1;
self.clear_invalid(self.top.into());
}
pub fn push_f80(&mut self, value: F80) {
if self.depth >= 8 {
panic!("FPU stack overflow");
}
self.top = (self.top.wrapping_sub(1)) & 7;
self.regs[self.top as usize].set(value.get());
self.depth += 1;
self.clear_invalid(self.top.into());
}
pub fn pop(&mut self) -> Option<F80> {
if self.depth == 0 {
return None;
}
let val = self.regs[self.top as usize].clone();
self.top = (self.top.wrapping_add(1)) & 7;
self.depth -= 1;
Some(val)
}
pub fn peek(&self, n: usize) -> F80 {
self.regs[n].clone()
}
pub fn get(&mut self, n: usize) -> F80 {
let idx = (self.top as usize + n) % 8;
if self.depth == 0 || n >= self.depth as usize {
self.set_invalid(idx);
}
self.regs[idx]
}
pub fn get_ref(&mut self, n: usize) -> &F80 {
let idx = (self.top as usize + n) % 8;
if self.depth == 0 || n >= self.depth as usize {
self.set_invalid(idx);
}
&self.regs[idx]
}
pub fn get_mut(&mut self, n: usize) -> Option<&mut F80> {
let idx = (self.top as usize + n) % 8;
if self.depth == 0 || n >= self.depth as usize {
self.set_invalid(idx);
}
if self.is_invalid(idx) {
self.regs[idx].set_real_indefinite();
None
} else {
Some(&mut self.regs[idx])
}
}
pub fn get_depth(&self) -> u8 {
self.depth
}
pub fn physical_index(&self, n: usize) -> usize {
(self.top as usize + n) % 8
}
pub fn swap(&mut self, a: usize, b: usize) {
let a = self.top as usize + a;
let b = (self.top as usize + b) % 8;
self.regs.swap(a, b);
}
}