Proof-of-concept .text section disassembly

This commit is contained in:
Seth Morabito 2019-01-17 09:50:07 -08:00
parent ce7152ef20
commit 79326745e0
4 changed files with 1255 additions and 144 deletions

View File

@ -7,7 +7,6 @@ use std::fmt;
use std::io::Cursor;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::marker::PhantomData;
use std::str;
use crate::errors::{CoffError, ReadResult, OffsetError};
@ -16,6 +15,7 @@ use chrono::prelude::*;
use chrono::TimeZone;
use byteorder::{BigEndian, ReadBytesExt};
use std::collections::HashMap;
// WE32000 without transfer vector
const MAGIC_WE32K: u16 = 0x170;
@ -36,29 +36,19 @@ const SYM_NAME_LEN: usize = 8;
bitflags! {
pub struct FileHeaderFlags: u16 {
// Relocation info stripped from file
const REL_STRIPPED = 0x0001;
const F_RELFLG = 0x0001;
// File is executable (i.e. no unresolved external references)
const EXECUTABLE = 0x0002;
const F_EXEC = 0x0002;
// Line numbers stripped from file
const LINE_NUM_STRIPPED = 0x0004;
const F_LNNO = 0x0004;
// Local symbols stripped from file
const LSYM_STRIPPED = 0x0010;
// This is a minimal object file (".m") output of fextract
const MINMAL_OBJECT = 0x0020;
// This is a fully bound update file, output of ogen
const UPDATE_FILE = 0x0040;
// This file has had its bytes swabbed (in names)
const SWABBED = 0x0100;
// This file has the byte ordering of an AR16WR (e.g. 11/70) machine
const BYTES_AR16WR = 0x0200;
// This file has the byte ordering of an AR32WR machine (e.g. vax)
const BYTES_AR32WR = 0x0400;
const F_LSYMS = 0x0008;
// This file has the byte ordering of an AR32W machine (e.g. 3b, maxi)
const BYTES_AR32W = 0x1000;
// File contains "patch" list in optional header
const PATCH_LIST = 0x2000;
// (minimal file only) no decision functions for replaced functions
const NO_DECISION_FUNCTIONS = 0x2000;
const F_AR32W = 0x0200;
// WE32100 required
const F_BM32B = 0x2000;
// MAU required
const F_BM32MAU = 0x4000;
}
}
@ -106,6 +96,22 @@ impl FileHeader {
Ok(header)
}
pub fn executable(&self) -> bool {
self.flags.contains(FileHeaderFlags::F_EXEC)
}
pub fn local_symbols_stripped(&self) -> bool {
self.flags.contains(FileHeaderFlags::F_LSYMS)
}
pub fn is_32100_required(&self) -> bool {
self.flags.contains(FileHeaderFlags::F_BM32B)
}
pub fn mau_required(&self) -> bool {
self.flags.contains(FileHeaderFlags::F_BM32MAU)
}
}
impl fmt::Debug for FileHeader {
@ -113,27 +119,26 @@ impl fmt::Debug for FileHeader {
writeln!(f, "COFF File Header:")?;
let magic = match self.magic {
MAGIC_WE32K => "WE32000 COFF",
MAGIC_WE32K_TV => "WE32000 COFF (TV)",
MAGIC_WE32K | MAGIC_WE32K_TV => "WE32000",
_ => "Unknown"
};
write!(f, " {}", magic)?;
write!(f, "{}", magic)?;
if self.flags.contains(FileHeaderFlags::EXECUTABLE) {
if self.executable() {
write!(f, " executable")?;
}
if self.flags.contains(FileHeaderFlags::LSYM_STRIPPED) {
write!(f, ", symbols stripped")?;
} else {
write!(f, ", with symbols")?;
if !self.local_symbols_stripped() {
write!(f, " not stripped")?;
}
if self.flags.contains(FileHeaderFlags::REL_STRIPPED) {
write!(f, ", relocation info stripped")?;
} else {
write!(f, ", with relocation info")?;
if self.is_32100_required() {
write!(f, ", 32100 required")?;
}
if self.mau_required() {
write!(f, ", MAU hardware required")?;
}
writeln!(f, ".")?;
@ -210,7 +215,7 @@ impl SectionHeader {
cursor.read_exact(&mut name)?;
let header = SectionHeader {
name: name,
name,
paddr: cursor.read_u32::<BigEndian>()?,
vaddr: cursor.read_u32::<BigEndian>()?,
size: cursor.read_u32::<BigEndian>()?,
@ -251,8 +256,81 @@ pub struct RelocationEntry {
pub rtype: u16,
}
///// Applicaple only to primary symbols.
//pub enum SymbolType {
// None,
// Pointer,
// Function,
// Array,
//}
#[derive(Copy, Clone)]
pub enum StorageClass {
EndOfFunction,
Null,
Auto,
ExternalSym,
Static,
Register,
ExternalDef,
Label,
UndefinedLabel,
MemberOfStruct,
FunctionArg,
StructureTag,
MemberOfUnion,
UnionTag,
TypeDefinition,
UninitializedStatic,
EnumerationTag,
MemberOfEnumeration,
RegisterParameter,
BitField,
BeginEndBlock,
BeginEndFunc,
EndOfStruct,
Filename,
Line,
Alias,
Hidden,
}
impl fmt::Debug for StorageClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
StorageClass::EndOfFunction => write!(f, "end of function"),
StorageClass::Null => write!(f, "null"),
StorageClass::Auto => write!(f, "automatic variable"),
StorageClass::ExternalSym => write!(f, "external symbol"),
StorageClass::Static => write!(f, "static"),
StorageClass::Register => write!(f, "register variable"),
StorageClass::ExternalDef => write!(f, "external definition"),
StorageClass::Label => write!(f, "label"),
StorageClass::UndefinedLabel => write!(f, "undefined label"),
StorageClass::MemberOfStruct => write!(f, "member of structure"),
StorageClass::FunctionArg => write!(f, "function argument"),
StorageClass::StructureTag => write!(f, "structure tag"),
StorageClass::MemberOfUnion => write!(f, "member of union"),
StorageClass::UnionTag => write!(f, "union tag"),
StorageClass::TypeDefinition => write!(f, "type definition"),
StorageClass::UninitializedStatic => write!(f, "uninitialized static"),
StorageClass::EnumerationTag => write!(f, "enumeration tag"),
StorageClass::MemberOfEnumeration => write!(f, "member of enumeration"),
StorageClass::RegisterParameter => write!(f, "register parameter"),
StorageClass::BitField => write!(f, "bit field"),
StorageClass::BeginEndBlock => write!(f, "beginning and end of block"),
StorageClass::BeginEndFunc => write!(f, "beginning and end of function"),
StorageClass::EndOfStruct => write!(f, "end of structure"),
StorageClass::Filename => write!(f, "filename"),
StorageClass::Line => write!(f, "line"),
StorageClass::Alias => write!(f, "duplicated tag"),
StorageClass::Hidden => write!(f, "hidden"),
}
}
}
/// Representation of a Symbol Table Entry
pub enum SymbolTableEntry {
pub enum Symbol {
Primary {
// Primary Symbol Data
//
@ -278,8 +356,8 @@ pub enum SymbolTableEntry {
n_value: u32,
n_scnum: i16,
n_type: u16,
n_sclass: u8,
n_numaux: u8,
storage_class: StorageClass,
},
Auxiliary {
// Auxiliary Symbol Data
@ -303,6 +381,7 @@ pub enum SymbolTableEntry {
// 2 bytes: x_tvndx
// ------------------
// 18 bytes total
x_fname: Option<String>,
x_tagndx: u32,
x_lnno: u16, // Decl. line number
x_size: u16, // Str, union, array size
@ -314,16 +393,117 @@ pub enum SymbolTableEntry {
}
}
pub struct StringTable<'s> {
pub data: Vec<u8>,
pub data_size: u32,
pub string_count: u32,
pub strings: Vec<(u32, String)>,
phantom: PhantomData<&'s str>,
pub struct SymbolTableEntry {
symbol: Symbol,
}
impl<'s> StringTable<'s> {
impl SymbolTableEntry {
pub fn read_symbol(cursor: &mut Cursor<&[u8]>, is_aux: bool, parent_class: &StorageClass) -> io::Result<Symbol> {
let mut raw_data: [u8; 18] = [0; 18];
// Consume 18 bytes.
cursor.read_exact(&mut raw_data)?;
let symbol = match is_aux {
true => {
let mut x_dimen: [u16; 4] = Default::default();
let x_fname = match parent_class {
StorageClass::Filename => {
Some(buf_to_str(&raw_data[0..14]).unwrap_or("???").to_owned())
},
_ => None
};
let x_tagndx = (&raw_data[0..4]).read_u32::<BigEndian>()?;
let x_lnno = (&raw_data[4..6]).read_u16::<BigEndian>()?;
let x_size = (&raw_data[6..8]).read_u16::<BigEndian>()?;
let x_fsize = (&raw_data[4..8]).read_u32::<BigEndian>()?;
let x_lnnoptr = (&raw_data[8..12]).read_u32::<BigEndian>()?;
let x_endndx = (&raw_data[12..16]).read_u32::<BigEndian>()?;
x_dimen[0] = (&raw_data[8..10]).read_u16::<BigEndian>()?;
x_dimen[1] = (&raw_data[10..12]).read_u16::<BigEndian>()?;
x_dimen[2] = (&raw_data[12..14]).read_u16::<BigEndian>()?;
x_dimen[3] = (&raw_data[14..16]).read_u16::<BigEndian>()?;
let x_tvndx = (&raw_data[16..18]).read_u16::<BigEndian>()?;
Symbol::Auxiliary {
x_fname,
x_tagndx,
x_lnno,
x_size,
x_fsize,
x_lnnoptr,
x_endndx,
x_dimen,
x_tvndx,
}
},
false => {
let mut n_name: [u8; SYM_NAME_LEN] = Default::default();
n_name.copy_from_slice(&raw_data[0..8]);
let n_zeroes = (&raw_data[0..4]).read_u32::<BigEndian>()?;
let n_offset = (&raw_data[4..8]).read_u32::<BigEndian>()?;
let n_value = (&raw_data[8..12]).read_u32::<BigEndian>()?;
let n_scnum = (&raw_data[12..14]).read_i16::<BigEndian>()?;
let n_type = (&raw_data[14..16]).read_u16::<BigEndian>()?;
let n_sclass = raw_data[16] as i8;
let n_numaux = raw_data[17];
let storage_class = match n_sclass {
-1 => StorageClass::EndOfFunction,
1 => StorageClass::Auto,
2 => StorageClass::ExternalSym,
3 => StorageClass::Static,
4 => StorageClass::Register,
5 => StorageClass::ExternalDef,
6 => StorageClass::Label,
7 => StorageClass::UndefinedLabel,
8 => StorageClass::MemberOfStruct,
9 => StorageClass::FunctionArg,
10 => StorageClass::StructureTag,
11 => StorageClass::MemberOfUnion,
12 => StorageClass::UnionTag,
13 => StorageClass::TypeDefinition,
14 => StorageClass::UninitializedStatic,
15 => StorageClass::EnumerationTag,
16 => StorageClass::MemberOfEnumeration,
17 => StorageClass::RegisterParameter,
18 => StorageClass::BitField,
100 => StorageClass::BeginEndBlock,
101 => StorageClass::BeginEndFunc,
102 => StorageClass::EndOfStruct,
103 => StorageClass::Filename,
104 => StorageClass::Line,
105 => StorageClass::Alias,
106 => StorageClass::Hidden,
_ => StorageClass::Null,
};
Symbol::Primary {
n_name,
n_zeroes,
n_offset,
n_value,
n_scnum,
n_type,
n_numaux,
storage_class,
}
},
};
Ok(symbol)
}
}
pub struct StringTable {
pub data: Vec<u8>,
pub data_size: u32,
pub strings: HashMap<u32, String>,
}
impl StringTable {
pub fn read(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
let mut data: Vec<u8> = vec!();
@ -336,8 +516,9 @@ impl<'s> StringTable<'s> {
let mut i: usize = 4;
// Denormalize the strings as we parse them.
let mut strings: Vec<(u32, String)> = vec!();
let mut strings = HashMap::new();
// Get the size of data we're expected to read
let data_size = cursor.read_u32::<BigEndian>()?;
for j in 4..data_size as usize {
@ -346,7 +527,7 @@ impl<'s> StringTable<'s> {
if c == 0 {
// Push from the last start to here.
let s = buf_to_str(&data[i..j]).unwrap_or("???");
strings.push((i as u32, s.to_owned()));
strings.insert(i as u32, s.to_owned());
i = j + 1usize;
}
}
@ -354,15 +535,13 @@ impl<'s> StringTable<'s> {
let table = StringTable {
data,
data_size,
string_count: strings.len() as u32,
strings,
phantom: PhantomData,
};
Ok(table)
}
pub fn string_at(&'s self, index: u32) -> Result<&'s str, Utf8Error> {
pub fn string_at(&self, index: u32) -> Result<&str, Utf8Error> {
let start = index as usize;
// Index into the vector at the appropriate location, and then
@ -385,16 +564,15 @@ pub struct Section {
pub data: Vec<u8>,
}
pub struct FileContainer<'s> {
pub struct FileContainer {
pub header: FileHeader,
pub opt_header: Option<OptionalHeader>,
pub sections: Vec<Section>,
pub symbols: Vec<SymbolTableEntry>,
pub strings: StringTable<'s>,
pub strings: StringTable,
}
impl<'s> FileContainer<'s> {
impl FileContainer {
///
/// Read in and destructure a WE32100 COFF file.
///
@ -463,77 +641,39 @@ impl<'s> FileContainer<'s> {
// Keep track of which symbols are aux symbols.
let mut is_aux = false;
let mut aux_index: u8 = 0;
let mut raw_data: [u8; 18] = [0; 18];
let mut sclass: StorageClass = StorageClass::Null;
for _ in 0..header.symbol_count {
let symbol = SymbolTableEntry::read_symbol(cursor, is_aux, &sclass)?;
// Consume 18 bytes.
cursor.read_exact(&mut raw_data)?;
let symbol_table_entry = match is_aux {
true => {
aux_index -= 1;
if aux_index == 0 {
is_aux = false;
}
let mut x_dimen: [u16; 4] = Default::default();
// let x_tagndx = read_u32(&raw_data[0..4]);
let x_tagndx = (&raw_data[0..4]).read_u32::<BigEndian>()?;
let x_lnno = (&raw_data[4..6]).read_u16::<BigEndian>()?;
let x_size = (&raw_data[6..8]).read_u16::<BigEndian>()?;
let x_fsize = (&raw_data[4..8]).read_u32::<BigEndian>()?;
let x_lnnoptr = (&raw_data[8..12]).read_u32::<BigEndian>()?;
let x_endndx = (&raw_data[12..16]).read_u32::<BigEndian>()?;
x_dimen[0] = (&raw_data[8..10]).read_u16::<BigEndian>()?;
x_dimen[1] = (&raw_data[10..12]).read_u16::<BigEndian>()?;
x_dimen[2] = (&raw_data[12..14]).read_u16::<BigEndian>()?;
x_dimen[3] = (&raw_data[14..16]).read_u16::<BigEndian>()?;
let x_tvndx = (&raw_data[16..18]).read_u16::<BigEndian>()?;
SymbolTableEntry::Auxiliary {
x_tagndx,
x_lnno,
x_size,
x_fsize,
x_lnnoptr,
x_endndx,
x_dimen,
x_tvndx,
}
},
false => {
let mut n_name: [u8; SYM_NAME_LEN] = Default::default();
n_name.copy_from_slice(&raw_data[0..8]);
let n_zeroes = (&raw_data[0..4]).read_u32::<BigEndian>()?;
let n_offset = (&raw_data[4..8]).read_u32::<BigEndian>()?;
let n_value = (&raw_data[8..12]).read_u32::<BigEndian>()?;
let n_scnum = (&raw_data[12..14]).read_i16::<BigEndian>()?;
let n_type = (&raw_data[14..16]).read_u16::<BigEndian>()?;
let n_sclass = raw_data[16];
let n_numaux = raw_data[17];
if is_aux {
aux_index -= 1;
if aux_index == 0 {
is_aux = false;
}
}
match symbol {
Symbol::Primary {
n_name: _,
n_zeroes: _,
n_offset: _,
n_value: _,
n_scnum: _,
n_type: _,
n_numaux,
storage_class,
} => {
if n_numaux > 0 {
is_aux = true;
aux_index = n_numaux;
}
SymbolTableEntry::Primary {
n_name,
n_zeroes,
n_offset,
n_value,
n_scnum,
n_type,
n_sclass,
n_numaux,
sclass = storage_class;
}
},
};
_ => {}
}
symbols.push(symbol_table_entry);
symbols.push(SymbolTableEntry { symbol });
}
}
@ -722,30 +862,40 @@ impl<'s> FileContainer<'s> {
return;
}
for (i, e) in self.symbols.iter().enumerate() {
println!("[");
match e {
SymbolTableEntry::Primary {
for (i, e) in self.symbols.iter().enumerate() {
let symbol = &e.symbol;
match symbol {
Symbol::Primary {
n_name,
n_zeroes,
n_offset,
n_value,
n_scnum,
n_type,
n_sclass,
n_numaux,
storage_class,
} => {
let (name, sclass) = if *n_zeroes == 0 {
(self.strings.string_at(*n_offset).unwrap_or("???"), "m2")
let name = if *n_zeroes == 0 {
self.strings.string_at(*n_offset).unwrap_or("???")
} else {
(buf_to_str(n_name).unwrap_or("???"), "m1")
buf_to_str(n_name).unwrap_or("???")
};
println!(" [{:4}] {:2} name='{}' value=0x{:x} scnum={} type={:x} sclass={:x} numaux={}",
i, sclass, name, n_value, n_scnum, n_type, n_sclass, n_numaux);
println!(" {{");
println!(" index: {},", i);
println!(" name: '{}',", name);
println!(" value: '0x{:x}',", n_value);
println!(" section: {},", n_scnum);
println!(" type: '0x{:02x}',", n_type);
println!(" class: '{:?}',", storage_class);
println!(" numaux: {}", n_numaux);
},
SymbolTableEntry::Auxiliary {
Symbol::Auxiliary {
x_fname,
x_tagndx,
x_lnno,
x_size,
@ -755,14 +905,32 @@ impl<'s> FileContainer<'s> {
x_dimen,
x_tvndx,
} => {
print!(" [{:4}] a tagndx={} lnno=0x{:04x} size=0x{:04x} fsize=0x{:04x} ",
i, x_tagndx, x_lnno, x_size, x_fsize);
println!("lnnoptr=0x{:08x} endndx={} dimen0={} dimen2={} tvndx={}",
x_lnnoptr, x_endndx, x_dimen[0], x_dimen[1], x_tvndx);
println!(" {{");
println!(" index: {},", i);
if x_fname.is_some() {
println!(" filename: '{}',", x_fname.as_ref().unwrap());
} else {
println!(" tagindex: {},", x_tagndx);
println!(" lnno: '0x{:x}',", x_lnno);
println!(" size: '0x{:x}',", x_size);
println!(" fsize: '0x{:x}',", x_fsize);
}
println!(" lnnoptr: '0x{:x}',", x_lnnoptr);
println!(" endndx: {},", x_endndx);
println!(" dim0: {},", x_dimen[0]);
println!(" dim1: {},", x_dimen[1]);
println!(" tvndx: {}", x_tvndx);
}
}
if i < self.symbols.len() - 1 {
println!(" }},")
} else {
println!(" }}");
}
}
println!("]");
}
pub fn dump_strings_table(&self) {
@ -770,12 +938,26 @@ impl<'s> FileContainer<'s> {
let strings = &self.strings;
if strings.string_count > 0 {
for s_tuple in &strings.strings {
println!(" [{:4}] {}", s_tuple.0, s_tuple.1);
if strings.strings.len() > 0 {
// Strings are kept in an unsorted hash map, so they should
// be sorted before printing out.
let mut keys: Vec<&u32> = strings.strings.keys().collect();
keys.sort();
for key in keys.iter() {
if let Some(val) = &strings.strings.get(key) {
println!(" [{:4}] {}", key, val);
}
}
} else {
println!(" No Strings");
}
}
pub fn section_data(&self, sec_num: usize) -> Option<&Vec<u8>> {
if let Some(section) = &self.sections.get(sec_num) {
return Some(&section.data);
}
None
}
}

873
we32dis/src/decode.rs Normal file
View File

@ -0,0 +1,873 @@
#![allow(clippy::unreadable_literal)]
use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};
use crate::errors::DecodeError;
use std::fmt;
const R_FP: usize = 9;
const R_AP: usize = 10;
const HALFWORD_MNEMONIC_COUNT: usize = 11;
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum AddrMode {
None,
Absolute,
AbsoluteDeferred,
ByteDisplacement,
ByteDisplacementDeferred,
HalfwordDisplacement,
HalfwordDisplacementDeferred,
WordDisplacement,
WordDisplacementDeferred,
APShortOffset,
FPShortOffset,
ByteImmediate,
HalfwordImmediate,
WordImmediate,
PositiveLiteral,
NegativeLiteral,
Register,
RegisterDeferred,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OpType {
Lit,
Src,
Dest,
None,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Data {
None,
Byte,
Half,
Word,
SByte,
UHalf,
UWord,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub struct Operand {
size: u8,
mode: AddrMode,
data_type: Data,
expanded_type: Option<Data>,
register: Option<usize>,
embedded: u32,
cursor: usize,
bytes: [u8; 32],
}
impl Operand {
fn new(
size: u8,
mode: AddrMode,
data_type: Data,
expanded_type: Option<Data>,
register: Option<usize>,
embedded: u32,
) -> Operand {
Operand {
size,
mode,
data_type,
expanded_type,
register,
embedded,
cursor: 0,
bytes: [0; 32],
}
}
fn reset(&mut self) {
self.cursor = 0;
}
fn byte_size(&self) -> u8 {
self.cursor as u8
}
fn append_u8(&mut self, b: u8) {
if self.cursor < 31 {
self.bytes[self.cursor] = b;
self.cursor += 1;
}
}
fn append_u16(&mut self, h: u16) {
if self.cursor < 29 {
self.bytes[self.cursor] = (h & 0xff) as u8;
self.bytes[self.cursor + 1] = ((h >> 8) & 0xff) as u8;
self.cursor += 2;
}
}
fn append_u32(&mut self, w: u32) {
if self.cursor < 27 {
self.bytes[self.cursor] = (w & 0xff) as u8 ;
self.bytes[self.cursor + 1] = ((w >> 8) & 0xff) as u8;
self.bytes[self.cursor + 2] = ((w >> 16) & 0xff) as u8;
self.bytes[self.cursor + 3] = ((w >> 24) & 0xff) as u8;
self.cursor += 4;
}
}
}
impl fmt::Display for Operand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let reg_string = match self.register {
Some(0) => "%r0",
Some(1) => "%r1",
Some(2) => "%r2",
Some(3) => "%r3",
Some(4) => "%r4",
Some(5) => "%r5",
Some(6) => "%r6",
Some(7) => "%r7",
Some(8) => "%r8",
Some(9) => "%fp",
Some(10) => "%ap",
Some(11) => "%psw",
Some(12) => "%sp",
Some(13) => "%pcbp",
Some(14) => "%isp",
Some(15) => "%pc",
_ => "%??",
};
match self.mode {
AddrMode::Absolute => write!(f, "$0x{:x}", self.embedded)?,
AddrMode::AbsoluteDeferred => write!(f, "*$0x{:x}", self.embedded)?,
AddrMode::ByteDisplacement => write!(f, "{}({})", (self.embedded as u8) as i8, reg_string)?,
AddrMode::ByteDisplacementDeferred => write!(f, "*{}({})", (self.embedded as u8) as i8, reg_string)?,
AddrMode::HalfwordDisplacement => write!(f, "0x{:x}({})", self.embedded as u16, reg_string)?,
AddrMode::HalfwordDisplacementDeferred => write!(f, "*0x{:x}({})", self.embedded as u16, reg_string)?,
AddrMode::WordDisplacement => write!(f, "0x{:x}({})", self.embedded, reg_string)?,
AddrMode::WordDisplacementDeferred => write!(f, "*0x{:x}({})", self.embedded, reg_string)?,
AddrMode::APShortOffset => write!(f, "{}(%ap)", self.embedded)?,
AddrMode::FPShortOffset => write!(f, "{}(%fp)", self.embedded)?,
AddrMode::ByteImmediate => write!(f, "&{}", self.embedded)?,
AddrMode::HalfwordImmediate => write!(f, "&0x{:x}", self.embedded)?,
AddrMode::WordImmediate => write!(f, "&0x{:x}", self.embedded)?,
AddrMode::PositiveLiteral => write!(f, "&{}", self.embedded)?,
AddrMode::NegativeLiteral => write!(f, "&{}", (self.embedded as u8) as i8)?,
AddrMode::Register => write!(f, "{}", reg_string)?,
AddrMode::RegisterDeferred => write!(f, "({})", reg_string)?,
AddrMode::None => write!(f, "{}", self.embedded)?,
}
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct Mnemonic {
opcode: u16,
dtype: Data,
name: &'static str,
ops: [OpType; 4],
}
#[derive(Debug, Eq, PartialEq)]
pub struct Instruction {
pub opcode: u16,
pub name: &'static str,
pub data_type: Data,
pub operand_count: u8,
pub operands: [Operand; 4],
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
// How many characters wide is the byte dump?
// (At least 2)
let mut bytes_width: i32 = 2;
// Print instruction bytes
write!(f, "{:02x}", self.opcode)?;
for i in 0..self.operand_count as usize {
let op: &Operand = &self.operands[i];
for j in 0..op.cursor {
write!(f, " {:02x}", op.bytes[j])?;
bytes_width += 3;
}
}
// Now compute how many spaces we need to write.
let spaces_needed: i32 = 30 - bytes_width;
if spaces_needed > 0 {
for _ in 0..spaces_needed {
write!(f, " ")?;
}
}
// Now write the mnemonic
write!(f, " | {}", self.name)?;
let mut more_spaces: i32 = 10 - self.name.len() as i32;
if more_spaces > 0 {
for _ in 0..more_spaces {
write!(f, " ")?;
}
}
let op_count = self.operand_count as usize;
for i in 0..op_count {
write!(f, "{}", self.operands[i])?;
if i < op_count - 1 {
write!(f, ",")?;
}
}
return Ok(())
}
}
macro_rules! mn {
($opcode:expr, $dtype:expr, $name:expr, $ops:expr) => {
Mnemonic {
opcode: $opcode,
dtype: $dtype,
name: $name,
ops: $ops,
}
};
}
static BYTE_MNEMONICS: [Option<Mnemonic>; 256] = [
Some(mn!(0x00, Data::None, "halt", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x02, Data::Word, "SPOPRD", [OpType::Lit, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x03, Data::Word, "SPOPRD2", [OpType::Lit, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0x04, Data::Word, "MOVAW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x06, Data::Word, "SPOPRT", [OpType::Lit, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x07, Data::Word, "SPOPT2", [OpType::Lit, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0x08, Data::None, "RET", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0x0C, Data::Word, "MOVTRW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0x10, Data::Word, "SAVE", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x13, Data::Word, "SPOPWD", [OpType::Lit, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x14, Data::Byte, "EXTOP", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x17, Data::Word, "SPOPWT", [OpType::Lit, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x18, Data::None, "RESTORE", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0x1C, Data::Word, "SWAPWI", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x1E, Data::Half, "SWAPHI", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x1F, Data::Byte, "SWAPBI", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x20, Data::Word, "POPW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x22, Data::Word, "SPOPRS", [OpType::Lit, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x23, Data::Word, "SPOPS2", [OpType::Lit, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0x24, Data::Word, "JMP", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x27, Data::None, "CFLUSH", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x28, Data::Word, "TSTW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x2A, Data::Half, "TSTH", [OpType::Src, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x2B, Data::Byte, "TSTB", [OpType::Src, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x2C, Data::Word, "CALL", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x2E, Data::None, "BPT", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x2F, Data::None, "WAIT", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x32, Data::Word, "SPOP", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x33, Data::Word, "SPOPWS", [OpType::Lit, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x34, Data::Word, "JSB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x36, Data::Half, "BSBH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x37, Data::Byte, "BSBB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x38, Data::Word, "BITW", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
None,
Some(mn!(0x3A, Data::Half, "BITH", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x3B, Data::Byte, "BITB", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x3C, Data::Word, "CMPW", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
None,
Some(mn!(0x3E, Data::Half, "CMPH", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x3F, Data::Byte, "CMPB", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x40, Data::None, "RGEQ", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x42, Data::Half, "BGEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x43, Data::Byte, "BGEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x44, Data::None, "RGTR", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x46, Data::Half, "BGH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x47, Data::Byte, "BGB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x48, Data::None, "RLSS", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x4A, Data::Half, "BLH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x4B, Data::Byte, "BLB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x4C, Data::None, "RLEQ", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x4E, Data::Half, "BLEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x4F, Data::Byte, "BLEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x50, Data::None, "RGEQU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x52, Data::Half, "BGEUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x53, Data::Byte, "BGEUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x54, Data::None, "RGTRU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x56, Data::Half, "BGUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x57, Data::Byte, "BGUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x58, Data::None, "RLSSU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x5A, Data::Half, "BLUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x5B, Data::Byte, "BLUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x5C, Data::None, "RLEQU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x5E, Data::Half, "BLEUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x5F, Data::Byte, "BLEUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x60, Data::None, "RVC", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x62, Data::Half, "BVCH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x63, Data::Byte, "BVCB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x64, Data::None, "RNEQU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x66, Data::Half, "BNEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x67, Data::Byte, "BNEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x68, Data::None, "RVS", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x6A, Data::Half, "BVSH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x6B, Data::Byte, "BVSB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x6C, Data::None, "REQLU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x6E, Data::Half, "BEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x6F, Data::Byte, "BEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x70, Data::None, "NOP", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x72, Data::None, "NOP3", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x73, Data::None, "NOP2", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x74, Data::None, "RNEQ", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x76, Data::Half, "BNEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x77, Data::Byte, "BNEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x78, Data::None, "RSB", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x7A, Data::Half, "BRH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x7B, Data::Byte, "BRB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x7C, Data::None, "REQL", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x7E, Data::Half, "BEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x7F, Data::Byte, "BEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x80, Data::Word, "CLRW", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x82, Data::Half, "CLRH", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x83, Data::Byte, "CLRB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x84, Data::Word, "MOVW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x86, Data::Half, "MOVH", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x87, Data::Byte, "MOVB", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x88, Data::Word, "MCOMW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x8A, Data::Half, "MCOMH", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x8B, Data::Byte, "MCOMB", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x8C, Data::Word, "MNEGW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x8E, Data::Half, "MNEGH", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x8F, Data::Byte, "MNEGB", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x90, Data::Word, "INCW", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x92, Data::Half, "INCH", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x93, Data::Byte, "INCB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x94, Data::Word, "DECW", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x96, Data::Half, "DECH", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x97, Data::Byte, "DECB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
None,
Some(mn!(0x9C, Data::Word, "ADDW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x9E, Data::Half, "ADDH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x9F, Data::Byte, "ADDB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xA0, Data::Word, "PUSHW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0xA4, Data::Word, "MODW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xA6, Data::Half, "MODH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xA7, Data::Byte, "MODB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xA8, Data::Word, "MULW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xAA, Data::Half, "MULH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xAB, Data::Byte, "MULB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xAC, Data::Word, "DIVW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xAE, Data::Half, "DIVH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xAF, Data::Byte, "DIVB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB0, Data::Word, "ORW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xB2, Data::Half, "ORH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB3, Data::Byte, "ORB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB4, Data::Word, "XORW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xB6, Data::Half, "XORH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB7, Data::Byte, "XORB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB8, Data::Word, "ANDW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xBA, Data::Half, "ANDH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xBB, Data::Byte, "ANDB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xBC, Data::Word, "SUBW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xBE, Data::Half, "SUBH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xBF, Data::Byte, "SUBB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xC0, Data::Word, "ALSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
None,
None,
Some(mn!(0xC4, Data::Word, "ARSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xC6, Data::Half, "ARSH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xC7, Data::Byte, "ARSB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xC8, Data::Word, "INSFW", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
None,
Some(mn!(0xCA, Data::Half, "INSFH", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xCB, Data::Byte, "INSFB", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xCC, Data::Word, "EXTFW", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
None,
Some(mn!(0xCE, Data::Half, "EXTFH", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xCF, Data::Byte, "EXTFB", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xD0, Data::Word, "LLSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xD2, Data::Half, "LLSH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xD3, Data::Byte, "LLSB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xD4, Data::Word, "LRSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
None,
None,
Some(mn!(0xD8, Data::Word, "ROTW", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
None,
None,
Some(mn!(0xDC, Data::Word, "ADDW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xDE, Data::Half, "ADDH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xDF, Data::Byte, "ADDB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xE0, Data::Word, "PUSHAW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0xE4, Data::Word, "MODW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xE6, Data::Half, "MODH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xE7, Data::Byte, "MODB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xE8, Data::Word, "MULW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xEA, Data::Half, "MULH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xEB, Data::Byte, "MULB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xEC, Data::Word, "DIVW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xEE, Data::Half, "DIVH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xEF, Data::Byte, "DIVB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF0, Data::Word, "ORW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xF2, Data::Half, "ORH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF3, Data::Byte, "ORB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF4, Data::Word, "XORW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xF6, Data::Half, "XORH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF7, Data::Byte, "XORB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF8, Data::Word, "ANDW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xFA, Data::Half, "ANDH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xFB, Data::Byte, "ANDB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xFC, Data::Word, "SUBW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xFE, Data::Half, "SUBH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xFF, Data::Byte, "SUBB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None]))
];
static HALFWORD_MNEMONICS: [Option<Mnemonic>; HALFWORD_MNEMONIC_COUNT] = [
Some(mn!(0x3009, Data::None, "MVERNO", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x300d, Data::None, "ENBVJMP", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x3013, Data::None, "DISVJMP", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x3019, Data::None, "MOVBLW", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x301f, Data::None, "STREND", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x302f, Data::None, "INTACK", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x303f, Data::None, "STRCPY", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x3045, Data::None, "RETG", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x3061, Data::None, "GATE", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x30ac, Data::None, "CALLPS", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x30c8, Data::None, "RETPS", [OpType::None, OpType::None, OpType::None, OpType::None]))
];
static NULL_MNEMONIC: Option<Mnemonic> = None;
pub struct Decoder {
pub ir: Instruction,
}
impl Default for Decoder {
fn default() -> Self {
Decoder::new()
}
}
impl Decoder {
pub fn new() -> Self {
Decoder {
ir: Instruction {
opcode: 0,
name: "???",
data_type: Data::None,
operand_count: 0,
operands: [
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
]
}
}
}
/// Decode a literal Operand type.
///
/// These operands belong to only certain instructions, where a word without
/// a descriptor byte immediately follows the opcode.
fn decode_literal_operand(&mut self, cursor: &mut Cursor<&[u8]>, index: usize, mn: &Mnemonic) -> Result<(), DecodeError> {
let op = &mut self.ir.operands[index];
op.mode = AddrMode::None;
op.data_type = Data::Byte;
op.expanded_type = None;
op.register = None;
match mn.dtype {
Data::Byte => {
let b: u8 = cursor.read_u8()?;
op.embedded = u32::from(b);
op.append_u8(b);
}
Data::Half => {
let h: u16 = cursor.read_u16::<LittleEndian>()?;
op.embedded = u32::from(h);
op.append_u16(h);
}
Data::Word => {
let w: u32 = cursor.read_u32::<LittleEndian>()?;
op.embedded = w;
op.append_u32(w);
}
_ => return Err(DecodeError::Parse),
}
Ok(())
}
/// Decode a descriptor Operand type.
fn decode_descriptor_operand(
&mut self,
cursor: &mut Cursor<&[u8]>,
index: usize,
dtype: Data,
etype: Option<Data>,
recur: bool,
) -> Result<(), DecodeError> {
let op = &mut self.ir.operands[index];
op.data_type = dtype;
op.expanded_type = etype;
let descriptor_byte: u8 = cursor.read_u8()?;
op.append_u8(descriptor_byte);
let m = (descriptor_byte & 0xf0) >> 4;
let r = descriptor_byte & 0xf;
match m {
0 | 1 | 2 | 3 => {
// Positive Literal
op.mode = AddrMode::PositiveLiteral;
op.register = None;
op.embedded = u32::from(descriptor_byte);
}
4 => {
match r {
15 => {
// Word Immediate
let w = cursor.read_u32::<LittleEndian>()?;
op.mode = AddrMode::WordImmediate;
op.register = None;
op.embedded = w;
op.append_u32(w);
}
_ => {
// Register
op.mode = AddrMode::Register;
op.register = Some(r as usize);
op.embedded = 0;
}
}
}
5 => {
match r {
15 => {
// Halfword Immediate
let h = cursor.read_u16::<LittleEndian>()?;
op.mode = AddrMode::HalfwordImmediate;
op.register = None;
op.embedded = u32::from(h);
op.append_u16(h);
}
11 => {
// Illegal
return Err(DecodeError::Parse);
}
_ => {
// Register Deferred Mode
op.mode = AddrMode::RegisterDeferred;
op.register = Some(r as usize);
op.embedded = 0;
}
}
}
6 => {
match r {
15 => {
// Byte Immediate
let b = cursor.read_u8()?;
op.mode = AddrMode::ByteImmediate;
op.register = None;
op.embedded = u32::from(b);
op.append_u8(b);
}
_ => {
// FP Short Offset
op.mode = AddrMode::FPShortOffset;
op.register = Some(R_FP);
op.embedded = u32::from(r);
}
}
}
7 => {
match r {
15 => {
// Absolute
let w = cursor.read_u32::<LittleEndian>()?;
op.mode = AddrMode::Absolute;
op.register = None;
op.embedded = w;
op.append_u32(w);
}
_ => {
// AP Short Offset
op.mode = AddrMode::APShortOffset;
op.register = Some(R_AP);
op.embedded = u32::from(r);
}
}
}
8 => {
match r {
11 => return Err(DecodeError::Parse),
_ => {
// Word Displacement
let disp = cursor.read_u32::<LittleEndian>()?;
op.mode = AddrMode::WordDisplacement;
op.register = Some(r as usize);
op.embedded = disp;
op.append_u32(disp);
}
}
}
9 => {
match r {
11 => return Err(DecodeError::Parse),
_ => {
// Word Displacement Deferred
let disp = cursor.read_u32::<LittleEndian>()?;
op.mode = AddrMode::WordDisplacementDeferred;
op.register = Some(r as usize);
op.embedded = disp;
op.append_u32(disp);
}
}
}
10 => {
match r {
11 => return Err(DecodeError::Parse),
_ => {
// Halfword Displacement
let disp = cursor.read_u16::<LittleEndian>()?;
op.mode = AddrMode::HalfwordDisplacement;
op.register = Some(r as usize);
op.embedded = u32::from(disp);
op.append_u16(disp);
}
}
}
11 => {
match r {
11 => return Err(DecodeError::Parse),
_ => {
// Halfword Displacement Deferred
let disp = cursor.read_u16::<LittleEndian>()?;
op.mode = AddrMode::HalfwordDisplacementDeferred;
op.register = Some(r as usize);
op.embedded = u32::from(disp);
op.append_u16(disp);
}
}
}
12 => {
match r {
11 => return Err(DecodeError::Parse),
_ => {
// Byte Displacement
let disp = cursor.read_u8()?;
op.mode = AddrMode::ByteDisplacement;
op.register = Some(r as usize);
op.embedded = u32::from(disp);
op.append_u8(disp);
}
}
}
13 => {
match r {
11 => return Err(DecodeError::Parse),
_ => {
// Byte Displacement Deferred
let disp = cursor.read_u8()?;
op.mode = AddrMode::ByteDisplacementDeferred;
op.register = Some(r as usize);
op.embedded = u32::from(disp);
op.append_u8(disp);
}
}
}
14 => match r {
0 => self.decode_descriptor_operand(cursor, index, dtype, Some(Data::UWord), true)?,
2 => self.decode_descriptor_operand(cursor, index, dtype, Some(Data::UHalf), true)?,
3 => self.decode_descriptor_operand(cursor, index, dtype, Some(Data::Byte), true)?,
4 => self.decode_descriptor_operand(cursor, index, dtype, Some(Data::Word), true)?,
6 => self.decode_descriptor_operand(cursor, index, dtype, Some(Data::Half), true)?,
7 => self.decode_descriptor_operand(cursor, index, dtype, Some(Data::SByte), true)?,
15 => {
let w = cursor.read_u32::<LittleEndian>()?;
op.mode = AddrMode::AbsoluteDeferred;
op.register = None;
op.embedded = w;
op.append_u32(w);
}
_ => { return Err(DecodeError::Parse); }
},
15 => {
// Negative Literal
op.mode = AddrMode::NegativeLiteral;
op.register = None;
op.embedded = u32::from(descriptor_byte);
},
_ => { return Err(DecodeError::Parse); }
};
Ok(())
}
/// Fully decode an Operand
fn decode_operand(
&mut self,
cursor: &mut Cursor<&[u8]>,
index: usize,
mn: &Mnemonic,
ot: OpType,
etype: Option<Data>,
) -> Result<(), DecodeError> {
self.ir.operands[index].reset();
match ot {
OpType::Lit => self.decode_literal_operand(cursor, index, mn),
OpType::Src | OpType::Dest => self.decode_descriptor_operand(cursor, index, mn.dtype, etype, false),
OpType::None => Ok(())
}
}
/// Decode the instruction currently pointed at by the cursor.
pub fn decode_instruction(&mut self, cursor: &mut Cursor<&[u8]>) -> Result<(), DecodeError> {
// Read the first byte of the instruction. Most instructions are only
// one byte, so this is usually enough.
let b1 = cursor.read_u8()?;
// Map the Mnemonic to the opcode we just read. But there's a special
// case if the value we read was '0x30'. This indicates that the instruction
// we're reading is a halfword, requiring two bytes.
let mut mn: &Option<Mnemonic> = &NULL_MNEMONIC;
if b1 == 0x30 {
let b2 = cursor.read_u8()?;
let opcode = (u16::from(b1) << 8) | u16::from(b2);
for m in &HALFWORD_MNEMONICS {
if m.is_some() && m.as_ref().unwrap().opcode == opcode {
mn = m;
break;
}
}
} else {
mn = &BYTE_MNEMONICS[b1 as usize];
};
// If we found a valid mnemonic, read in and decode all of its operands.
match mn {
Some(mn) => {
let mut etype: Option<Data> = None;
let mut index: usize = 0;
for ot in &mn.ops {
if *ot == OpType::None {
break;
}
// Push a decoded operand
self.decode_operand(cursor, index, mn, *ot, etype)?;
etype = self.ir.operands[index].expanded_type;
index += 1;
}
self.ir.opcode = mn.opcode;
self.ir.name = mn.name;
self.ir.operand_count = index as u8;
self.ir.data_type = mn.dtype;
}
None => return Err(DecodeError::Parse),
}
Ok(())
}
}

View File

@ -1,5 +1,6 @@
use std::error;
use std::fmt;
use std::io;
pub type ReadResult<T> = std::result::Result<T, CoffError>;
@ -59,3 +60,43 @@ impl error::Error for CoffError {
None
}
}
///
/// Error while decoding instruction stream
///
#[derive(Debug)]
pub enum DecodeError {
IoError(io::Error),
Parse,
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DecodeError::IoError(error) => write!(f, "io error on decode: {:?}", error),
DecodeError::Parse => write!(f, "parse error on decode"),
}
}
}
impl error::Error for DecodeError {
fn description(&self) -> &str {
match self {
DecodeError::IoError(_) => "io error on decode",
DecodeError::Parse => "parse error on decode",
}
}
fn cause(&self) -> Option<&error::Error> {
match self {
DecodeError::IoError(error) => Some(error),
DecodeError::Parse => None,
}
}
}
impl From<io::Error> for DecodeError {
fn from(error: io::Error) -> Self {
DecodeError::IoError(error)
}
}

View File

@ -10,32 +10,47 @@ use std::vec::Vec;
use clap::{Arg, App};
use crate::coff::FileContainer;
use crate::decode::Decoder;
use std::io::Cursor;
mod errors;
mod coff;
mod decode;
fn disassemble(buf: &[u8]) {
match FileContainer::read(buf) {
Ok(container) => {
println!("{:?}", container.header);
//
// if let Some(opt_header) = &container.opt_header {
// println!("{:?}", opt_header);
// }
//
// for (sec_num, section) in container.sections.iter().enumerate() {
// println!("{:?}", section.header);
//
// if let Err(e) = container.dump_relocation_table(sec_num) {
// println!("Error: Couldn't dump relocation table: {:?}", e);
// }
//
// if let Err(e) = container.dump_section_data(sec_num) {
// println!("Error: Couldn't dump section data: {:?}", e);
// }
// }
// container.dump_symbol_table();
// container.dump_strings_table();
if let Some(opt_header) = &container.opt_header {
println!("{:?}", opt_header);
}
for (sec_num, section) in container.sections.iter().enumerate() {
println!("{:?}", section.header);
// OK, now let's try to decode some shit.
if let Some(data) = container.section_data(0) {
println!("\nSection: .text\n");
let mut decoder = Decoder::new();
let mut cursor: Cursor<&[u8]> = Cursor::new(data);
if let Err(e) = container.dump_relocation_table(sec_num) {
println!("Error: Couldn't dump relocation table: {:?}", e);
}
if let Err(e) = container.dump_section_data(sec_num) {
println!("Error: Couldn't dump section data: {:?}", e);
while let Ok(()) = decoder.decode_instruction(&mut cursor) {
println!("{}", decoder.ir);
}
}
container.dump_symbol_table();
container.dump_strings_table();
},
Err(e) => {
println!("Could not parse file: {}", e);