use crate::errors::lpanic;
use std::str::FromStr;
use crate::bbu::ArchMcrInst;
use crate::bbu::DatSize;
use crate::bbu::PtrSize;
use crate::bbu::RcSym;
use crate::bbu::SymConv;
use crate::bbu::ArgSymbol;
pub type Chip8DatSize = crate::bbu::GenScal<u8>;
pub type Chip8DisSize = crate::bbu::GenScal<u8>;
pub type Chip8PtrSize = crate::bbu::Gen12;
pub type Chip8SymAlias = ArgSymbol<Chip8PtrSize, Chip8DatSize>;
pub struct Chip8Symbol {
pub i: Chip8SymAlias,
}
impl SymConv for Chip8Symbol {
fn from_ptr<T: PtrSize>(a: T) -> Self {
Self {
i: ArgSymbol::Pointer(Box::new(
<Chip8PtrSize as PtrSize>::from_int::<u16>(a.extract_int::<u16>()),
)),
}
}
fn from_dat<T: DatSize>(a: T) -> Self {
Self {
i: ArgSymbol::Data(Box::new(<Chip8DatSize as DatSize>::from_int::<u8>(
a.extract_int::<u8>(),
))),
}
}
fn into_ptr<T: PtrSize, E: crate::bbu::Integral>(&self) -> T {
T::from_int(PtrSize::extract_int::<E>(
&**self.i.unwrap_ptr().unwrap(),
))
}
}
impl<T: SymConv> crate::bbu::ArchSym<T> for Chip8Symbol {
fn get_uk_sym(&self) -> Option<RcSym> {
match &self.i {
ArgSymbol::UnknownPointer(i) | ArgSymbol::UnknownData(i) => {
Some(i.clone())
}
_ => None,
}
}
fn set_sym(&mut self, _a: T) -> () {
unimplemented!()
}
}
macro_rules! gim {
($n:ident,$i:ident) => {{
Box::new(<$n as ArchMcrInst<Chip8Symbol>>::get_lex($i.args))
}};
}
pub fn get_instruction<T: SymConv>(
i: crate::parser::ParsedInstruction,
) -> Box<dyn ArchMcrInst<T>> {
match i.instr.to_lowercase().as_str() {
"0nnn" => gim!(Chip8_0NNN, i),
"00e0" => gim!(Chip8_00E0, i),
"00ee" => gim!(Chip8_00EE, i),
"1nnn" => gim!(Chip8_1NNN, i),
"2nnn" => gim!(Chip8_2NNN, i),
"3xnn" => gim!(Chip8_3XNN, i),
"4xnn" => gim!(Chip8_4XNN, i),
"5xy0" => gim!(Chip8_5XY0, i),
"6xnn" => gim!(Chip8_6XNN, i),
"7xnn" => gim!(Chip8_7XNN, i),
"8xy0" => gim!(Chip8_8XY0, i),
"8xy1" => gim!(Chip8_8XY1, i),
"8xy2" => gim!(Chip8_8XY2, i),
"8xy3" => gim!(Chip8_8XY3, i),
"8xy4" => gim!(Chip8_8XY4, i),
"8xy5" => gim!(Chip8_8XY5, i),
"8xy6" => gim!(Chip8_8XY6, i),
"8xy7" => gim!(Chip8_8XY7, i),
"8xye" => gim!(Chip8_8XYE, i),
"9xy0" => gim!(Chip8_9XY0, i),
"annn" => gim!(Chip8_ANNN, i),
"bnnn" => gim!(Chip8_BNNN, i),
"cxnn" => gim!(Chip8_CXNN, i),
"dxyn" => gim!(Chip8_DXYN, i),
"ex9e" => gim!(Chip8_EX9E, i),
"exa1" => gim!(Chip8_EXA1, i),
"fx07" => gim!(Chip8_FX07, i),
"fx0a" => gim!(Chip8_FX0A, i),
"fx15" => gim!(Chip8_FX15, i),
"fx18" => gim!(Chip8_FX18, i),
"fx1e" => gim!(Chip8_FX1E, i),
"fx29" => gim!(Chip8_FX29, i),
"fx33" => gim!(Chip8_FX33, i),
"fx55" => gim!(Chip8_FX55, i),
"fx65" => gim!(Chip8_FX65, i),
_ => lpanic("unknown instruction error"),
}
}
#[derive(Copy, Clone)]
pub struct Chip8ArchReg {
n: u8,
}
impl FromStr for Chip8ArchReg {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() > 3 || s.len() < 2 {
lpanic("chip8_raw: unknown register")
} else {
let a: char = s.chars().nth(2).unwrap();
Ok(Chip8ArchReg {
n: char::to_digit(
if a == 'v' {
s.chars().nth(3).unwrap()
} else {
a
},
16,
)
.unwrap() as u8,
})
}
}
}
impl crate::bbu::ArchReg for Chip8ArchReg {}
pub type Chip8Arg = crate::bbu::ArchArg<Chip8PtrSize, Chip8DatSize, Chip8DisSize, Chip8ArchReg>;
fn chip8_placeholder() -> Vec<u8> {
vec![0u8, 0u8]
}
const CHIP8_INSTR_LEN: crate::bbu::SymbolPosition = 0x2;
macro_rules! make_std_const {
($nm:ident,$offs:expr) => {
#[allow(non_camel_case_types)]
pub struct $nm {}
impl<T: SymConv> ArchMcrInst<T> for $nm {
fn get_output_bytes(&self) -> Vec<u8> {
Vec::from($offs.to_be_bytes())
}
fn get_lex(_a: Option<Vec<String>>) -> Self {
Self {}
}
fn check_symbols(&self) -> bool {
false
}
fn get_symbols(&self) -> crate::bbu::USIWrap {
None
}
fn get_length(&self) -> crate::bbu::SymbolPosition {
CHIP8_INSTR_LEN
}
fn get_placeholder(&self) -> Vec<u8> {
chip8_placeholder()
}
fn fulfill_symbol(&mut self, _s: &T, _p: crate::bbu::SymbolPosition) -> () {}
}
};
}
macro_rules! make_std_nnn {
($nm:ident,$offs:expr) => {
#[allow(non_camel_case_types)]
pub struct $nm {
addr: Chip8SymAlias,
}
impl<T: SymConv> ArchMcrInst<T> for $nm {
fn get_output_bytes(&self) -> Vec<u8> {
Vec::from(($offs | self.addr.unwrap_ptr().unwrap().i).to_be_bytes())
}
fn get_lex(a: Option<Vec<String>>) -> Self {
Self { addr: get_nnn(a) }
}
fn check_symbols(&self) -> bool {
match self.addr {
ArgSymbol::UnknownPointer(_) => true,
_ => false,
}
}
fn get_symbols(&self) -> crate::bbu::USIWrap {
let r = match self.addr {
ArgSymbol::UnknownPointer(ref a) => Some(vec![(a.clone(), 0)]),
_ => None,
};
r
}
fn get_length(&self) -> crate::bbu::SymbolPosition {
CHIP8_INSTR_LEN
}
fn get_placeholder(&self) -> Vec<u8> {
chip8_placeholder()
}
fn fulfill_symbol(&mut self, s: &T, p: crate::bbu::SymbolPosition) -> () {
match p {
0 => {
self.addr = ArgSymbol::Pointer(Box::new(
s.into_ptr::<Chip8PtrSize, u16>(),
))
}
_ => lpanic("c8r: unknown positional"),
}
}
}
};
}
macro_rules! make_std_xnn {
($nm:ident,$offs:expr) => {
#[allow(non_camel_case_types)]
pub struct $nm {
x: Chip8ArchReg,
d: Chip8SymAlias,
}
impl<T: SymConv> ArchMcrInst<T> for $nm {
fn get_output_bytes(&self) -> Vec<u8> {
Vec::from(
($offs | ((self.x.n as u16) << 8) | (self.d.unwrap_data().unwrap().i as u16))
.to_be_bytes(),
)
}
fn get_lex(a: Option<Vec<String>>) -> Self {
let b: (Chip8ArchReg, Chip8SymAlias) = get_xnn(a);
Self { x: b.0, d: b.1 }
}
// TODO: better optimize this
fn check_symbols(&self) -> bool {
match self.d {
ArgSymbol::UnknownData(_) => true,
_ => false,
}
}
fn get_symbols(&self) -> crate::bbu::USIWrap {
match self.d {
ArgSymbol::UnknownData(ref a) => Some(vec![(a.clone(), 0)]),
_ => None,
}
}
fn get_length(&self) -> crate::bbu::SymbolPosition {
CHIP8_INSTR_LEN
}
fn get_placeholder(&self) -> Vec<u8> {
chip8_placeholder()
}
fn fulfill_symbol(&mut self, s: &T, p: crate::bbu::SymbolPosition) -> () {
match p {
0 => {
self.d =
ArgSymbol::Data(Box::new(s.into_ptr::<Chip8DatSize, u8>()))
}
_ => lpanic("c8r: unknown positional"),
}
}
}
};
}
// TODO: general for archinstruction, is vec best? could boxed slice work better?
macro_rules! make_std_xy {
($nm:ident,$offs:expr) => {
#[allow(non_camel_case_types)]
pub struct $nm {
s: Chip8ArchReg,
d: Chip8ArchReg,
}
impl<T: SymConv> ArchMcrInst<T> for $nm {
fn get_output_bytes(&self) -> Vec<u8> {
Vec::from(
($offs | ((self.d.n as u16) << 8) | ((self.s.n as u16) << 4)).to_be_bytes(),
)
}
fn get_lex(a: Option<Vec<String>>) -> Self {
let b: (Chip8ArchReg, Chip8ArchReg) = get_xy(a);
Self { s: b.0, d: b.1 }
}
// implementation is the same as for make_std_const
fn check_symbols(&self) -> bool {
false
}
fn get_symbols(&self) -> crate::bbu::USIWrap {
None
}
fn get_length(&self) -> crate::bbu::SymbolPosition {
CHIP8_INSTR_LEN
}
fn get_placeholder(&self) -> Vec<u8> {
chip8_placeholder()
}
fn fulfill_symbol(&mut self, _s: &T, _p: crate::bbu::SymbolPosition) -> () {}
}
};
}
// TODO: create type names for each arg combo type to make tuples easier
// XYN = $N,%vX,%vY
macro_rules! make_std_xyn {
($nm:ident,$offs:expr) => {
// TODO: in the future, let's stray away from needing Copy and Clone
#[allow(non_camel_case_types)]
pub struct $nm {
n: Chip8SymAlias,
x: Chip8ArchReg,
y: Chip8ArchReg,
}
impl<T: SymConv> ArchMcrInst<T> for $nm {
fn get_output_bytes(&self) -> Vec<u8> {
// TODO: warn on overflow
Vec::from(
($offs
| ((self.x.n as u16) << 8)
| ((self.y.n as u16) << 4)
| ((self.n.unwrap_data().unwrap().i as u16) & 0xf))
.to_be_bytes(),
)
}
fn get_lex(a: Option<Vec<String>>) -> Self {
let b: (Chip8SymAlias, Chip8ArchReg, Chip8ArchReg) = get_xyn(a);
Self {
n: b.0,
x: b.1,
y: b.2,
}
}
fn check_symbols(&self) -> bool {
match self.n {
ArgSymbol::UnknownData(_) => true,
_ => false,
}
}
fn get_symbols(&self) -> crate::bbu::USIWrap {
match self.n {
ArgSymbol::UnknownData(ref a) => Some(vec![(a.clone(), 0)]),
_ => None,
}
}
fn get_length(&self) -> crate::bbu::SymbolPosition {
CHIP8_INSTR_LEN
}
fn get_placeholder(&self) -> Vec<u8> {
chip8_placeholder()
}
fn fulfill_symbol(&mut self, s: &T, p: crate::bbu::SymbolPosition) -> () {
match p {
0 => {
self.n =
ArgSymbol::Data(Box::new(s.into_ptr::<Chip8DatSize, u8>()))
}
_ => lpanic("c8r: unknown positional"),
}
}
}
};
}
// named efx because all efx instructions start with 0xE or 0xF
// NOTE wish I had a macro for making these macros
macro_rules! make_std_efx {
($nm:ident,$offs:expr) => {
#[allow(non_camel_case_types)]
pub struct $nm {
x: Chip8ArchReg,
}
impl<T: SymConv> ArchMcrInst<T> for $nm {
fn get_output_bytes(&self) -> Vec<u8> {
Vec::from(($offs | ((self.x.n as u16) << 8)).to_be_bytes())
}
fn get_lex(a: Option<Vec<String>>) -> Self {
Self { x: get_efx(a) }
}
// implementation is the same as for make_std_const as well
fn check_symbols(&self) -> bool {
false
}
fn get_symbols(&self) -> crate::bbu::USIWrap {
None
}
fn get_length(&self) -> crate::bbu::SymbolPosition {
CHIP8_INSTR_LEN
}
fn get_placeholder(&self) -> Vec<u8> {
chip8_placeholder()
}
fn fulfill_symbol(&mut self, _s: &T, _p: crate::bbu::SymbolPosition) -> () {}
}
};
}
make_std_nnn!(Chip8_0NNN, 0u16);
make_std_const!(Chip8_00E0, 0xe0u16);
make_std_const!(Chip8_00EE, 0xeeu16);
make_std_nnn!(Chip8_1NNN, 0x1000u16);
make_std_nnn!(Chip8_2NNN, 0x2000u16);
make_std_xnn!(Chip8_3XNN, 0x3000u16);
make_std_xnn!(Chip8_4XNN, 0x4000u16);
make_std_xy!(Chip8_5XY0, 0x5000u16);
make_std_xnn!(Chip8_6XNN, 0x6000u16);
make_std_xnn!(Chip8_7XNN, 0x7000u16);
make_std_xy!(Chip8_8XY0, 0x8000u16);
make_std_xy!(Chip8_8XY1, 0x8001u16);
make_std_xy!(Chip8_8XY2, 0x8002u16);
make_std_xy!(Chip8_8XY3, 0x8003u16);
make_std_xy!(Chip8_8XY4, 0x8004u16);
make_std_xy!(Chip8_8XY5, 0x8005u16);
make_std_xy!(Chip8_8XY6, 0x8006u16);
make_std_xy!(Chip8_8XY7, 0x8007u16);
make_std_xy!(Chip8_8XYE, 0x800eu16);
make_std_xy!(Chip8_9XY0, 0x9000u16);
make_std_nnn!(Chip8_ANNN, 0xa000u16);
make_std_nnn!(Chip8_BNNN, 0xb000u16);
make_std_xnn!(Chip8_CXNN, 0xc000u16);
make_std_xyn!(Chip8_DXYN, 0xd000u16);
make_std_efx!(Chip8_EX9E, 0xe09eu16);
make_std_efx!(Chip8_EXA1, 0xe0a1u16);
make_std_efx!(Chip8_FX07, 0xf007u16);
make_std_efx!(Chip8_FX0A, 0xf00au16);
make_std_efx!(Chip8_FX15, 0xf015u16);
make_std_efx!(Chip8_FX18, 0xf018u16);
make_std_efx!(Chip8_FX1E, 0xf01eu16);
make_std_efx!(Chip8_FX29, 0xf029u16);
make_std_efx!(Chip8_FX33, 0xf033u16);
make_std_efx!(Chip8_FX55, 0xf055u16);
make_std_efx!(Chip8_FX65, 0xf065u16);
// FIXME: switch to argcheck
// TODO condense similarly to get_xnn
fn get_nnn(a: Option<Vec<String>>) -> Chip8SymAlias {
if let Some(ref i) = a {
if i.len() != 1 {
lpanic("c8r: wrong arg count")
}
let b: Chip8Arg = crate::bbu::parse_arg(&a.unwrap()[0]).unwrap();
// TODO: avoid this clone in the future
b.unwrap_memory().unwrap().v.clone()
} else {
lpanic("c8r: not enough args")
}
}
// this logic structure is repeated a lot
// TODO consider condensing it somehow
fn get_xnn(a: Option<Vec<String>>) -> (Chip8ArchReg, Chip8SymAlias) {
let b: Vec<Chip8Arg> = argcheck(&a, 2);
(
*b[1].unwrap_register().unwrap().reg,
// TODO: also avoid this clone
b[0].unwrap_direct().unwrap().clone(),
)
/*
if let Some(ref i) = a {
if i.len() != 2 {
lpanic("c8r: not enough args")
}
// TODO: move these directly in
// data
let b: Chip8Arg = crate::bbu::parse_arg(&a.as_ref().unwrap()[0]).unwrap();
// register X
let c: Chip8Arg = crate::bbu::parse_arg(&a.as_ref().unwrap()[1]).unwrap();
(
*c.unwrap_register().unwrap().reg,
// TODO: also avoid this clone
b.unwrap_direct().unwrap().clone(),
)
} else {
lpanic("c8r: not enough args")
}*/
}
// NOTE: is tuple the best option here? Would an array be better?
fn get_xy(a: Option<Vec<String>>) -> (Chip8ArchReg, Chip8ArchReg) {
//if let Some(ref i) = a {
// if i.len() != 2 {
// panic!("c8r: not enough args")
// }
let b: Vec<Chip8Arg> = argcheck(&a, 2);
(
*b[0].unwrap_register().unwrap().reg,
*b[1].unwrap_register().unwrap().reg,
)
//} else {
// panic!("c8r: not enough args")
//}
}
// res.0 limited to 4 bits
fn get_xyn(a: Option<Vec<String>>) -> (Chip8SymAlias, Chip8ArchReg, Chip8ArchReg) {
let b: Vec<Chip8Arg> = argcheck(&a, 3);
(
// TODO FIXME: get rid of the clones!!
b[0].unwrap_direct().unwrap().clone(),
*b[1].unwrap_register().unwrap().reg,
*b[2].unwrap_register().unwrap().reg,
)
}
fn get_efx(a: Option<Vec<String>>) -> Chip8ArchReg {
let b: Vec<Chip8Arg> = argcheck(&a, 1);
*b[0].unwrap_register().unwrap().reg
}
// TODO: make this a utility public function
// TODO: better error handling (log)
// TODO: dedup panic message
fn argcheck(a: &Option<Vec<String>>, i: usize) -> Vec<Chip8Arg> {
if let Some(ref b) = a {
if b.len() != i {
lpanic("argument check failed")
} else {
Vec::from_iter(b.into_iter().map(|x| crate::bbu::parse_arg(&x).unwrap()))
}
} else {
lpanic("argument check failed")
}
}
// TODO genericize for LE, BE
// TODO FIXME NOTE: throw warnings when number is truncated!
macro_rules! gmm {
($n:ident,$i:ident) => {{
Box::new(<crate::bbu::$n as ArchMcrInst<Chip8Symbol>>::get_lex(
$i.args,
))
}};
}
// TODO: consider putting these in lexer maybe? idk
// TODO FIXME: add symbols to macros
pub fn get_macro<T: SymConv>(i: crate::parser::ParsedMacro) -> Box<dyn ArchMcrInst<T>> {
match i.mcr.to_lowercase().as_str() {
"byte" => gmm!(Bu8, i),
"word" => gmm!(Bu16, i),
_ => lpanic("macro not supported"),
}
}