use std::{
convert::TryFrom,
fmt::{self, Display, Formatter},
io::{Read, Write},
};
use vax_floating::{FFloating, DFloating, GFloating, HFloating};
use crate::{
error::{Error, Result},
operand::{Operand},
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AccessType {
Address,
Branch,
Modify,
Read,
VariableBitField,
Write,
}
macro_rules! define_data_functions {
($(($desc: ident, $count: expr, $out: ty),)+) => {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DataType {
$(
#[doc = concat!("The VAX ", stringify!($desc), " data type. Reads into a Rust ",
stringify!($out), " value.")]
$desc,
)+
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DataValue {
$(
#[doc = concat!("The VAX ", stringify!($desc), " data type. Reads into a `",
stringify!($out), "` value.")]
$desc($out),
)+
}
impl DataValue {
$(
#[doc = concat!("assert_eq!(DataValue::", stringify!($desc), "(", stringify!($out),
"::MAX).data_type(), DataType::", stringify!($desc), ");")]
)+
pub fn data_type(&self) -> DataType {
match self {
$(
DataValue::$desc(_) => DataType::$desc,
)+
}
}
}
impl Display for DataValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
$(
DataValue::$desc(value) => value.fmt(f),
)+
}
}
}
pub trait ReadDataValue: Read {
fn read_data_value(&mut self, data_type: DataType) -> Result<DataValue> {
match data_type {
$(
DataType::$desc => {
let mut buf = [0; $count];
self.read_exact(&mut buf)?;
Ok(DataValue::$desc(<$out>::from_le_bytes(buf)))
}
)+
}
}
}
impl<R: Read + ?Sized> ReadDataValue for R {}
pub trait WriteDataValue: Write {
fn write_data_value(&mut self, data_value: DataValue) -> Result<()> {
match data_value {
$(
DataValue::$desc(value) => {
let mut buf = [0; $count];
buf.copy_from_slice(&value.to_le_bytes());
self.write_all(&buf)?;
Ok(())
}
)+
}
}
}
impl<W: Write + ?Sized> WriteDataValue for W {}
#[cfg(test)]
pub(crate) mod data_value_tests {
use super::*;
use proptest::prelude::*;
pub fn data_value() -> impl Strategy<Value = DataValue> {
prop_oneof![
$(
any::<$out>().prop_map(DataValue::$desc),
)+
]
}
}
};
}
define_data_functions!{
(Byte, 1, i8),
(Word, 2, i16),
(Longword, 4, i32),
(Quadword, 8, i64),
(Octoword, 16, i128),
(Floating, 4, FFloating),
(Double, 8, DFloating),
(GFloating, 8, GFloating),
(HFloating, 16, HFloating),
}
macro_rules! operand_count {
($_: ident) => {1};
}
macro_rules! access_type {
(ab) => {AccessType::Address};
(aw) => {AccessType::Address};
(al) => {AccessType::Address};
(aq) => {AccessType::Address};
(ao) => {AccessType::Address};
(af) => {AccessType::Address};
(ad) => {AccessType::Address};
(ag) => {AccessType::Address};
(ah) => {AccessType::Address};
(bb) => {AccessType::Branch};
(bw) => {AccessType::Branch};
(bl) => {AccessType::Branch};
(mb) => {AccessType::Modify};
(mw) => {AccessType::Modify};
(ml) => {AccessType::Modify};
(mq) => {AccessType::Modify};
(mo) => {AccessType::Modify};
(mf) => {AccessType::Modify};
(md) => {AccessType::Modify};
(mg) => {AccessType::Modify};
(mh) => {AccessType::Modify};
(rb) => {AccessType::Read};
(rw) => {AccessType::Read};
(rl) => {AccessType::Read};
(rq) => {AccessType::Read};
(ro) => {AccessType::Read};
(rf) => {AccessType::Read};
(rd) => {AccessType::Read};
(rg) => {AccessType::Read};
(rh) => {AccessType::Read};
(vb) => {AccessType::VariableBitField};
(wb) => {AccessType::Write};
(ww) => {AccessType::Write};
(wl) => {AccessType::Write};
(wq) => {AccessType::Write};
(wo) => {AccessType::Write};
(wf) => {AccessType::Write};
(wd) => {AccessType::Write};
(wg) => {AccessType::Write};
(wh) => {AccessType::Write};
}
macro_rules! data_type {
(ab) => {DataType::Byte};
(aw) => {DataType::Word};
(al) => {DataType::Longword};
(aq) => {DataType::Quadword};
(ao) => {DataType::Octoword};
(af) => {DataType::Floating};
(ad) => {DataType::Double};
(ag) => {DataType::GFloating};
(ah) => {DataType::HFloating};
(bb) => {DataType::Byte};
(bw) => {DataType::Word};
(bl) => {DataType::Longword};
(mb) => {DataType::Byte};
(mw) => {DataType::Word};
(ml) => {DataType::Longword};
(mq) => {DataType::Quadword};
(mo) => {DataType::Octoword};
(mf) => {DataType::Floating};
(md) => {DataType::Double};
(mg) => {DataType::GFloating};
(mh) => {DataType::HFloating};
(rb) => {DataType::Byte};
(rw) => {DataType::Word};
(rl) => {DataType::Longword};
(rq) => {DataType::Quadword};
(ro) => {DataType::Octoword};
(rf) => {DataType::Floating};
(rd) => {DataType::Double};
(rg) => {DataType::GFloating};
(rh) => {DataType::HFloating};
(vb) => {DataType::Byte};
(wb) => {DataType::Byte};
(ww) => {DataType::Word};
(wl) => {DataType::Longword};
(wq) => {DataType::Quadword};
(wo) => {DataType::Octoword};
(wf) => {DataType::Floating};
(wd) => {DataType::Double};
(wg) => {DataType::GFloating};
(wh) => {DataType::HFloating};
}
macro_rules! count_operands {
(()) => {0};
(($n1:ident.$s1:ident)) => {1};
(($n1:ident.$s1:ident, $n2:ident.$s2:ident, $n3:ident.$s3:ident, $n4:ident.$s4:ident, $n5:ident.$s5:ident, $n6:ident.$s6:ident)) => {6};
(($($_:ident.$spec: ident),+)) => {
(0 $( + operand_count!($spec) )+)
};
}
macro_rules! commafy {
($s1: expr,) => { $s1 };
($s1: expr, $($s2: expr,)+) => { concat!($s1, $(", ", $s2,)+) };
}
macro_rules! operands_string {
(()) => {""};
(($($name:ident.$spec: ident),+)) => {
commafy!(
$(
concat!(stringify!($name), ".", stringify!($spec)),
)+
)
};
}
macro_rules! operands_slice {
(()) => {&[]};
(($($_:ident.$spec: ident),+)) => {&[
$(
(access_type!($spec), data_type!($spec)),
)+
]};
}
macro_rules! process_opcodes {
(opcodes: {$(($word: expr, $opcode: ident, $operands: tt, $desc: expr),)*},
duplicates: {$(($dup_word: expr, $dup_opcode: ident, $dup_operands: tt, $dup_desc: expr),)*}) => {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)]
pub enum Opcode {
$(
#[doc = $desc]
#[doc = concat!("Opcode ", stringify!($opcode), " = ", stringify!($word))]
$opcode,
)*
$(
#[doc = $dup_desc]
#[doc = concat!("Opcode ", stringify!($dup_opcode), " = ", stringify!($dup_word))]
$dup_opcode,
)*
}
impl Opcode {
pub fn operand_specs(&self) -> &[(AccessType, DataType)] {
use Opcode::*;
match self {
$(
$opcode => operands_slice!($operands),
)*
$(
$dup_opcode => operands_slice!($dup_operands),
)*
}
}
fn tab(&self) -> &'static str {
use Opcode::*;
static SPACES: [&'static str; 8] = [
" ", " ", " ", " ", " ", " ", " ", " ",
];
match self {
$(
$opcode => SPACES[stringify!($opcode).len()],
)+
$(
$dup_opcode => SPACES[stringify!($dup_opcode).len()],
)+
}
}
}
impl TryFrom<u16> for Opcode {
type Error = Error;
fn try_from(value: u16) -> Result<Self> {
use $crate::opcode::Opcode::*;
match value {
$(
$word => Ok($opcode),
)*
other => Err(Error::InvalidOpcode(other))
}
}
}
impl From<&Opcode> for u16 {
fn from(opcode: &Opcode) -> Self {
match opcode {
$(
Opcode::$opcode => $word,
)+
$(
Opcode::$dup_opcode => $dup_word,
)+
}
}
}
impl Display for Opcode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use Opcode::*;
match self {
$(
$opcode => f.write_str(stringify!($opcode)),
)+
$(
$dup_opcode => f.write_str(stringify!($dup_opcode)),
)+
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Instruction {
$(
#[doc = $desc]
#[doc = concat!("Format: ", stringify!($opcode), " ", operands_string!($operands))]
$opcode([Operand; count_operands!($operands)]),
)*
$(
#[doc = concat!("`opcode\t", operands_string!($dup_operands), "`")]
#[doc = concat!("`", stringify!($dup_word), "\t", stringify!($dup_opcode), "\t", $dup_desc, "`")]
$dup_opcode([Operand; count_operands!($dup_operands)]),
)*
}
impl Instruction {
pub fn opcode(&self) -> Opcode {
match self {
$(
Instruction::$opcode(_) => Opcode::$opcode,
)+
$(
Instruction::$dup_opcode(_) => Opcode::$dup_opcode,
)+
}
}
pub fn operands(&self) -> &[Operand] {
match self {
$(
Instruction::$opcode(ref slice) => slice,
)+
$(
Instruction::$dup_opcode(ref slice) => slice,
)+
}
}
pub(crate) fn operands_mut(&mut self) -> &mut [Operand] {
match self {
$(
Instruction::$opcode(ref mut slice) => slice,
)+
$(
Instruction::$dup_opcode(ref mut slice) => slice,
)+
}
}
}
impl From<Opcode> for Instruction {
fn from(opcode: Opcode) -> Self {
match opcode {
$(
Opcode::$opcode => Instruction::$opcode([Operand::Undefined; count_operands!($operands)]),
)+
$(
Opcode::$dup_opcode => Instruction::$dup_opcode([Operand::Undefined; count_operands!($dup_operands)]),
)+
}
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let opcode = self.opcode();
write!(f, "{:?}", opcode)?;
let mut comma = opcode.tab();
for operand in self.operands() {
write!(f, "{}{}", comma, operand)?;
comma = ", ";
}
Ok(())
}
}
#[cfg(test)]
mod opcode_tests {
use super::*;
#[test]
pub fn verify_u16_to_opcode() {
$(
assert_eq!(Opcode::try_from($word).unwrap(), Opcode::$opcode);
)*
}
#[test]
pub fn verify_duplicate_opcodes() {
$(
let opcode_u16 = u16::from(&Opcode::$dup_opcode);
assert_ne!(Opcode::$dup_opcode, Opcode::try_from(opcode_u16).unwrap());
)+
}
}
};
}
include!("process_opcodes.in");
#[cfg(test)]
mod tests {
#[test]
fn verify_u16_to_opcode() {
super::opcode_tests::verify_u16_to_opcode();
}
}