use std::collections::HashMap;
use crate::ast::Reg::{R0, R6};
use crate::ast::Reg;
use super::mem::{MemArray, RegFile, Word};
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum ParameterList {
CallingConvention {
params: Vec<String>
},
PassByRegister {
params: Vec<(String, Reg)>,
ret: Option<Reg>
}
}
impl ParameterList {
pub fn with_calling_convention(params: &[&str]) -> Self {
let params = params.iter()
.map(|name| name.to_string())
.collect();
Self::CallingConvention { params }
}
pub fn with_pass_by_register(params: &[(&str, Reg)], ret: Option<Reg>) -> Self {
let params = params.iter()
.map(|&(name, reg)| (name.to_string(), reg))
.collect();
Self::PassByRegister { params, ret }
}
fn get_arguments(&self, regs: &RegFile, mem: &MemArray, fp: u16) -> Vec<Word> {
match self {
ParameterList::CallingConvention { params } => {
(0..params.len())
.map(|i| fp.wrapping_add(4).wrapping_add(i as u16))
.map(|addr| mem[addr])
.collect()
},
ParameterList::PassByRegister { params, ret: _ } => {
params.iter()
.map(|&(_, r)| regs[r])
.collect()
},
}
}
}
impl std::fmt::Debug for ParameterList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CallingConvention { params } => {
f.write_str("fn[stdcc](")?;
if let Some((first, rest)) = params.split_first() {
f.write_str(first)?;
for param in rest {
f.write_str(", ")?;
f.write_str(param)?;
}
}
f.write_str(") -> _")?;
Ok(())
},
Self::PassByRegister { params, ret } => {
f.write_str("fn[pass by reg](")?;
if let Some(((first_param, first_reg), rest)) = params.split_first() {
write!(f, "{first_param} = {first_reg}")?;
for (param, reg) in rest {
write!(f, ", {param} = {reg}")?;
}
}
f.write_str(")")?;
if let Some(ret) = ret {
write!(f, " -> {ret}")?;
}
Ok(())
},
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FrameType {
Subroutine,
Trap,
Interrupt
}
#[derive(Debug, Clone)]
pub struct Frame {
pub caller_addr: u16,
pub callee_addr: u16,
pub frame_type: FrameType,
pub frame_ptr: Option<Word>,
pub arguments: Vec<Word>
}
#[derive(Debug)]
pub struct FrameStack {
frame_no: u64,
trap_defns: HashMap<u8, ParameterList>,
sr_defns: HashMap<u16, ParameterList>,
frames: Option<Vec<Frame>>
}
impl FrameStack {
pub(super) fn new(debug_frames: bool) -> Self {
Self {
frame_no: 0,
trap_defns: HashMap::from_iter([
(0x20, ParameterList::with_pass_by_register(&[], Some(R0))),
(0x21, ParameterList::with_pass_by_register(&[("char", R0)], None)),
(0x22, ParameterList::with_pass_by_register(&[("addr", R0)], None)),
(0x23, ParameterList::with_pass_by_register(&[], Some(R0))),
(0x24, ParameterList::with_pass_by_register(&[("addr", R0)], None)),
(0x25, ParameterList::with_pass_by_register(&[], None)),
]),
sr_defns: Default::default(),
frames: debug_frames.then(Vec::new)
}
}
pub fn get_trap_def(&self, vect: u8) -> Option<&ParameterList> {
self.trap_defns.get(&vect)
}
pub fn get_subroutine_def(&self, addr: u16) -> Option<&ParameterList> {
self.sr_defns.get(&addr)
}
pub fn set_subroutine_def(&mut self, addr: u16, params: ParameterList) {
self.sr_defns.insert(addr, params);
}
pub fn len(&self) -> u64 {
self.frame_no
}
pub fn is_empty(&self) -> bool {
self.frame_no == 0
}
pub fn frames(&self) -> Option<&[Frame]> {
self.frames.as_deref()
}
pub(super) fn push_frame(&mut self, caller: u16, callee: u16, frame_type: FrameType, regs: &RegFile, mem: &MemArray) {
self.frame_no += 1;
if let Some(frames) = self.frames.as_mut() {
let m_plist = match frame_type {
FrameType::Subroutine => self.sr_defns.get(&callee),
FrameType::Trap => {
u8::try_from(callee).ok()
.and_then(|addr| self.trap_defns.get(&addr))
},
FrameType::Interrupt => {
self.sr_defns.get(&callee)
},
};
let (fp, args) = match m_plist {
Some(plist @ ParameterList::CallingConvention { .. }) => {
let fp = regs[R6] - Word::new_init(4);
(Some(fp), plist.get_arguments(regs, mem, fp.get()))
},
Some(plist @ ParameterList::PassByRegister { .. }) => {
(None, plist.get_arguments(regs, mem, 0))
},
None => (None, vec![])
};
frames.push(Frame {
caller_addr: caller,
callee_addr: callee,
frame_type,
frame_ptr: fp,
arguments: args,
})
}
}
pub(super) fn pop_frame(&mut self) {
self.frame_no = self.frame_no.saturating_sub(1);
if let Some(frames) = self.frames.as_mut() {
frames.pop();
}
}
}
impl Default for FrameStack {
fn default() -> Self {
Self::new(false)
}
}