use std::fmt::Write as _;
use super::{CondCode, IOffset, ImmOrReg, Label, Offset, PCOffset, Reg, TrapVect8};
type PCOffset9 = PCOffset<i16, 9>;
type PCOffset11 = PCOffset<i16, 11>;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum AsmInstr {
ADD(Reg, Reg, ImmOrReg<5>),
AND(Reg, Reg, ImmOrReg<5>),
BR(CondCode, PCOffset9),
JMP(Reg),
JSR(PCOffset11),
JSRR(Reg),
LD(Reg, PCOffset9),
LDI(Reg, PCOffset9),
LDR(Reg, Reg, IOffset<6>),
LEA(Reg, PCOffset9),
NOT(Reg, Reg),
RET,
RTI,
ST(Reg, PCOffset9),
STI(Reg, PCOffset9),
STR(Reg, Reg, IOffset<6>),
TRAP(TrapVect8),
NOP(PCOffset9),
GETC,
OUT,
PUTC,
PUTS,
IN,
PUTSP,
HALT
}
impl std::fmt::Display for AsmInstr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ADD(dr, sr1, sr2) => write!(f, "ADD {dr}, {sr1}, {sr2}"),
Self::AND(dr, sr1, sr2) => write!(f, "AND {dr}, {sr1}, {sr2}"),
Self::BR(cc, off) => {
if cc != &0 {
write!(f, "BR")?;
if cc & 0b100 != 0 { f.write_char('n')?; };
if cc & 0b010 != 0 { f.write_char('z')?; };
if cc & 0b001 != 0 { f.write_char('p')?; };
} else {
write!(f, "NOP")?;
}
write!(f, " {off}")
},
Self::JMP(br) => write!(f, "JMP {br}"),
Self::JSR(off) => write!(f, "JSR {off}"),
Self::JSRR(br) => write!(f, "JSRR {br}"),
Self::LD(dr, off) => write!(f, "LD {dr}, {off}"),
Self::LDI(dr, off) => write!(f, "LDI {dr}, {off}"),
Self::LDR(dr, br, off) => write!(f, "LDR {dr}, {br}, {off}"),
Self::LEA(dr, off) => write!(f, "LEA {dr}, {off}"),
Self::NOT(dr, sr) => write!(f, "NOT {dr}, {sr}"),
Self::RET => f.write_str("RET"),
Self::RTI => f.write_str("RTI"),
Self::ST(sr, off) => write!(f, "ST {sr}, {off}"),
Self::STI(sr, off) => write!(f, "STI {sr}, {off}"),
Self::STR(sr, br, off) => write!(f, "STR {sr}, {br}, {off}"),
Self::TRAP(vect) => write!(f, "TRAP {vect:02X}"),
Self::NOP(off) => write!(f, "NOP {off}"),
Self::GETC => f.write_str("GETC"),
Self::OUT => f.write_str("OUT"),
Self::PUTC => f.write_str("PUTC"),
Self::PUTS => f.write_str("PUTS"),
Self::IN => f.write_str("IN"),
Self::PUTSP => f.write_str("PUTSP"),
Self::HALT => f.write_str("HALT"),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum Directive {
Orig(Offset<u16, 16>),
Fill(PCOffset<u16, 16>),
Blkw(Offset<u16, 16>),
Stringz(String),
End,
External(Label),
}
impl std::fmt::Display for Directive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Orig(addr) => write!(f, ".orig {addr:04X}"),
Self::Fill(val) => write!(f, ".fill {val}"),
Self::Blkw(n) => write!(f, ".blkw {n}"),
Self::Stringz(val) => write!(f, ".stringz {val:?}"),
Self::End => write!(f, ".end"),
Self::External(lb) => write!(f, ".external {lb}"),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum StmtKind {
#[allow(missing_docs)]
Instr(AsmInstr),
#[allow(missing_docs)]
Directive(Directive)
}
impl std::fmt::Display for StmtKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StmtKind::Instr(i) => i.fmt(f),
StmtKind::Directive(d) => d.fmt(f),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Stmt {
pub labels: Vec<Label>,
pub nucleus: StmtKind,
pub span: std::ops::Range<usize>
}
impl std::fmt::Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for label in &self.labels {
label.fmt(f)?;
f.write_char(' ')?;
}
self.nucleus.fmt(f)
}
}
pub fn try_disassemble_line(word: u16) -> Option<Stmt> {
let si = match word >= 0x0200 {
true => super::sim::SimInstr::decode(word).ok(),
false => None,
}?;
let ai = match si {
super::sim::SimInstr::BR(cc, off) => AsmInstr::BR(cc, PCOffset::Offset(off)),
super::sim::SimInstr::ADD(dr, sr1, sr2) => AsmInstr::ADD(dr, sr1, sr2),
super::sim::SimInstr::LD(dr, off) => AsmInstr::LD(dr, PCOffset::Offset(off)),
super::sim::SimInstr::ST(sr, off) => AsmInstr::ST(sr, PCOffset::Offset(off)),
super::sim::SimInstr::JSR(off) => match off {
ImmOrReg::Imm(imm) => AsmInstr::JSR(PCOffset::Offset(imm)),
ImmOrReg::Reg(reg) => AsmInstr::JSRR(reg),
},
super::sim::SimInstr::AND(dr, sr1, sr2) => AsmInstr::AND(dr, sr1, sr2),
super::sim::SimInstr::LDR(dr, br, off) => AsmInstr::LDR(dr, br, off),
super::sim::SimInstr::STR(sr, br, off) => AsmInstr::STR(sr, br, off),
super::sim::SimInstr::RTI => AsmInstr::RTI,
super::sim::SimInstr::NOT(dr, sr) => AsmInstr::NOT(dr, sr),
super::sim::SimInstr::LDI(dr, off) => AsmInstr::LDI(dr, PCOffset::Offset(off)),
super::sim::SimInstr::STI(sr, off) => AsmInstr::STI(sr, PCOffset::Offset(off)),
super::sim::SimInstr::JMP(Reg::R7) => AsmInstr::RET,
super::sim::SimInstr::JMP(br) => AsmInstr::JMP(br),
super::sim::SimInstr::LEA(dr, off) => AsmInstr::LEA(dr, PCOffset::Offset(off)),
super::sim::SimInstr::TRAP(vect) if vect.get() == 0x20 => AsmInstr::GETC,
super::sim::SimInstr::TRAP(vect) if vect.get() == 0x21 => AsmInstr::PUTC,
super::sim::SimInstr::TRAP(vect) if vect.get() == 0x22 => AsmInstr::PUTS,
super::sim::SimInstr::TRAP(vect) if vect.get() == 0x23 => AsmInstr::IN,
super::sim::SimInstr::TRAP(vect) if vect.get() == 0x24 => AsmInstr::PUTSP,
super::sim::SimInstr::TRAP(vect) if vect.get() == 0x25 => AsmInstr::HALT,
super::sim::SimInstr::TRAP(vect) => AsmInstr::TRAP(vect),
};
Some(Stmt {
labels: vec![],
nucleus: StmtKind::Instr(ai),
span: 0..0
})
}
pub fn disassemble_line(word: u16) -> Stmt {
try_disassemble_line(word)
.unwrap_or_else(|| {
let fill = Directive::Fill(PCOffset::Offset(super::Offset::new_trunc(word)));
Stmt {
labels: vec![],
nucleus: StmtKind::Directive(fill),
span: 0..0
}
})
}
pub fn disassemble(data: &[u16]) -> Vec<Stmt> {
data.iter()
.copied()
.map(disassemble_line)
.collect()
}