#![warn(missing_docs)]
pub mod data;
pub mod file;
pub mod instr;
pub mod reg;
pub mod traits;
pub mod debug;
pub mod directives;
#[macro_use]
mod macros;
#[cfg(test)]
mod tests;
use std::io::prelude::*;
use std::ops::{Add, AddAssign};
pub enum SegmentEL<T> {
Label(reg::Label),
Inline(String),
Comment(String),
Data(T),
Directive(directives::Directive),
}
impl<T: traits::Writable> traits::Writable for SegmentEL<T> {
fn write_in(&self, file: &mut std::fs::File) -> std::io::Result<()> {
match self {
Self::Label(lab) => {
lab.write_in(file)?;
file.write_all(b":")
}
Self::Inline(str) => file.write_all(str.as_bytes()),
Self::Comment(str) => {
file.write_all(b"## ")?;
file.write_all(str.as_bytes())
}
Self::Data(el) => {
file.write_all(b"\t")?;
el.write_in(file)
}
Self::Directive(d) => {
file.write_all(b"\t")?;
d.write_in(file)
}
}
}
}
impl<T> SegmentEL<T> {
pub fn wrapped(self) -> SegmentELWrapper<T> {
SegmentELWrapper {
el: self,
comment: None,
}
}
}
pub struct SegmentELWrapper<T> {
el: SegmentEL<T>,
comment: Option<String>,
}
impl<T> SegmentELWrapper<T> {
pub fn comment(&mut self, str: String) {
match &mut self.comment {
None => self.comment = Some(str),
Some(c) => {
c.push_str(" ## ");
c.push_str(&str);
}
}
}
}
impl<T: traits::Writable> traits::Writable for SegmentELWrapper<T> {
fn write_in(&self, file: &mut std::fs::File) -> std::io::Result<()> {
self.el.write_in(file)?;
if let Some(str) = &self.comment {
file.write_all(b" ## ")?;
file.write_all(str.as_bytes())?;
};
file.write_all(b"\n")
}
}
pub struct Segment<T> {
data: Vec<SegmentELWrapper<T>>,
}
impl<T> Segment<T> {
fn new(el: T) -> Self {
Self {
data: vec![SegmentEL::Data(el).wrapped()],
}
}
pub fn label(lab: reg::Label) -> Self {
Self {
data: vec![SegmentEL::Label(lab).wrapped()],
}
}
pub fn inline(str: String) -> Self {
Self {
data: vec![SegmentEL::Inline(str).wrapped()],
}
}
pub fn comment(str: String) -> Self {
Self {
data: vec![SegmentEL::Comment(str).wrapped()],
}
}
pub fn add_comment(mut self, str: String) -> Self {
match self.data.last_mut() {
None => self.data.push(SegmentEL::Comment(str).wrapped()),
Some(el) => el.comment(str),
}
self
}
pub fn directive(directive: directives::Directive) -> Self {
Self {
data: vec![SegmentEL::Directive(directive).wrapped()],
}
}
pub fn name(self, name: String) -> NamedSegment<T> {
NamedSegment::new(name, self)
}
pub fn empty() -> Self {
Self { data: Vec::new() }
}
}
impl<T> Add for Segment<T> {
type Output = Self;
fn add(mut self, mut other: Self) -> Self {
self.data.append(&mut other.data);
self
}
}
impl<T> AddAssign for Segment<T> {
fn add_assign(&mut self, mut rhs: Self) {
self.data.append(&mut rhs.data)
}
}
impl<T: traits::Writable> traits::Writable for Segment<T> {
fn write_in(&self, file: &mut std::fs::File) -> std::io::Result<()> {
for el in &self.data {
el.write_in(file)?;
}
std::io::Result::Ok(())
}
}
pub struct NamedSegment<T> {
name: String,
data: Segment<T>,
}
impl<T> NamedSegment<T> {
pub fn new(name: String, data: Segment<T>) -> Self {
Self { name, data }
}
}
impl<T: traits::Writable> traits::Writable for NamedSegment<T> {
fn write_in(&self, file: &mut std::fs::File) -> std::io::Result<()> {
file.write_all(b"\t.section ")?;
file.write_all(self.name.as_bytes())?;
file.write_all(b"\n")?;
self.data.write_in(file)
}
}
pub type Text = Segment<instr::Instr>;
pub type Data = Segment<data::DataEL>;
pub fn nop() -> Text {
Segment::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Nop,
reg1: None,
reg2: None,
}))
}
pub fn inline(str: String) -> Text {
Text::inline(str)
}
def_regq!(RAX, Rax);
def_regq!(RBX, Rbx);
def_regq!(RCX, Rcx);
def_regq!(RDX, Rdx);
def_regq!(RSI, Rsi);
def_regq!(RDI, Rdi);
def_regq!(RBP, Rbp);
def_regq!(RSP, Rsp);
def_regq!(R8, R8);
def_regq!(R9, R9);
def_regq!(R10, R10);
def_regq!(R11, R11);
def_regq!(R12, R12);
def_regq!(R13, R13);
def_regq!(R14, R14);
def_regq!(R15, R15);
def_regl!(EAX, Eax);
def_regl!(EBX, Ebx);
def_regl!(ECX, Ecx);
def_regl!(EDX, Edx);
def_regl!(ESI, Esi);
def_regl!(EDI, Edi);
def_regl!(EBP, Ebp);
def_regl!(ESP, Esp);
def_regl!(R8D, R8d);
def_regl!(R9D, R9d);
def_regl!(R10D, R10d);
def_regl!(R11D, R11d);
def_regl!(R12D, R12d);
def_regl!(R13D, R13d);
def_regl!(R14D, R14d);
def_regl!(R15D, R15d);
def_regw!(AX, Ax);
def_regw!(BX, Bx);
def_regw!(CX, Cx);
def_regw!(DX, Dx);
def_regw!(SI, Si);
def_regw!(DI, Di);
def_regw!(BP, Bp);
def_regw!(SP, Sp);
def_regw!(R8W, R8w);
def_regw!(R9W, R9w);
def_regw!(R10W, R10w);
def_regw!(R11W, R11w);
def_regw!(R12W, R12w);
def_regw!(R13W, R13w);
def_regw!(R14W, R14w);
def_regw!(R15W, R15w);
def_regb!(AL, Al);
def_regb!(BL, Bl);
def_regb!(CL, Cl);
def_regb!(DL, Dl);
def_regb!(AH, Ah);
def_regb!(BH, Bh);
def_regb!(CH, Ch);
def_regb!(DH, Dh);
def_regb!(SIL, Sil);
def_regb!(DIL, Dil);
def_regb!(BPL, Bpl);
def_regb!(SPL, Spl);
def_regb!(R8B, R8b);
def_regb!(R9B, R9b);
def_regb!(R10B, R10b);
def_regb!(R11B, R11b);
def_regb!(R12B, R12b);
def_regb!(R13B, R13b);
def_regb!(R14B, R14b);
def_regb!(R15B, R15b);
pub fn immq(imm: i64) -> reg::Operand<reg::RegQ> {
reg::Operand::Imm(imm)
}
pub fn imml(imm: i32) -> reg::Operand<reg::RegL> {
reg::Operand::Imm(imm as i64)
}
pub fn immw(imm: i16) -> reg::Operand<reg::RegW> {
reg::Operand::Imm(imm as i64)
}
pub fn immb(imm: i8) -> reg::Operand<reg::RegB> {
reg::Operand::Imm(imm as i64)
}
#[macro_export]
macro_rules! reg {
($reg:expr) => {
$crate::reg::Operand::Reg($reg)
};
}
#[macro_export]
macro_rules! addr {
($reg:expr) => {
$crate::reg::Operand::Addr(0, $reg, None, 0)
};
($offset:expr, $reg:expr) => {
$crate::reg::Operand::Addr($offset, $reg, None, 0)
};
($offset:expr, $reg:expr, $reg2:expr) => {
$crate::reg::Operand::Addr($offset, $reg, Some($reg2), 1)
};
($offset:expr, $reg:expr, $reg2:expr, $scale:expr) => {
$crate::reg::Operand::Addr($offset, $reg, Some($reg2), scale)
};
}
#[cfg(target_os = "macos")]
#[macro_export]
macro_rules! lab {
($label:expr) => {
$crate::reg::Operand::LabRelAddr($label)
};
}
#[cfg(target_os = "linux")]
#[macro_export]
macro_rules! lab {
($label:expr) => {
$crate::reg::Operand::LabAbsAddr($label)
};
}
#[macro_export]
macro_rules! ilab {
($label:expr) => {
$crate::reg::Operand::LabVal($label)
};
}
build_instr_op_op!(Move, movb, movw, movl, movq);
pub fn movsbw(reg1: reg::Operand<reg::RegB>, reg2: reg::RegW) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movsbl(reg1: reg::Operand<reg::RegB>, reg2: reg::RegL) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movsbq(reg1: reg::Operand<reg::RegB>, reg2: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movswl(reg1: reg::Operand<reg::RegW>, reg2: reg::RegL) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movswq(reg1: reg::Operand<reg::RegW>, reg2: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movslq(reg1: reg::Operand<reg::RegL>, reg2: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movzbw(reg1: reg::Operand<reg::RegB>, reg2: reg::RegW) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movzbl(reg1: reg::Operand<reg::RegB>, reg2: reg::RegL) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movzbq(reg1: reg::Operand<reg::RegB>, reg2: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movzwl(reg1: reg::Operand<reg::RegW>, reg2: reg::RegL) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
pub fn movzwq(reg1: reg::Operand<reg::RegW>, reg2: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Movs,
reg1: Some(reg1),
reg2: Some(reg!(reg2)),
}))
}
build_instr_op_reg!(Lea, leab, leaw, leal, leaq);
build_instr_op!(Inc, incb, incw, incl, incq);
build_instr_op!(Dec, decb, decw, decl, decq);
build_instr_op!(Neg, negb, negw, negl, negq);
build_instr_op_op!(Add, addb, addw, addl, addq);
build_instr_op_op!(Sub, subb, subw, subl, subq);
build_instr_op_op!(IMul, imulw, imull, imulq);
pub fn cltd() -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Cltd,
reg1: None,
reg2: None,
}))
}
pub fn cqto() -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Cqto,
reg1: None,
reg2: None,
}))
}
build_instr_op!(SignedDiv, idivl, idivq);
build_instr_op!(UnsignedDiv, divl, divq);
build_instr_op!(Not, notb, notw, notl, notq);
build_instr_op_op!(And, andb, andw, andl, andq);
build_instr_op_op!(Or, orb, orw, orl, orq);
build_instr_op_op!(Xor, xorb, xorw, xorl, xorq);
build_instr_op_op!(Shl, shlb, shlw, shll, shlq);
build_instr_op_op!(Shr, shrb, shrw, shrl, shrq);
build_instr_op_op!(Sar, sarb, sarw, sarl, sarq);
pub fn shlb_reg(reg: reg::Operand<reg::RegB>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shl,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shlw_reg(reg: reg::Operand<reg::RegW>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shl,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shll_reg(reg: reg::Operand<reg::RegL>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shl,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shlq_reg(reg: reg::Operand<reg::RegQ>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shl,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shrb_reg(reg: reg::Operand<reg::RegB>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shr,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shrw_reg(reg: reg::Operand<reg::RegW>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shr,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shrl_reg(reg: reg::Operand<reg::RegL>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shr,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn shrq_reg(reg: reg::Operand<reg::RegQ>) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Shr,
reg1: Some(reg!(CL)),
reg2: Some(reg),
}))
}
pub fn call(label: reg::Label) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Call(label),
reg1: None,
reg2: None,
}))
}
pub fn call_star(op: reg::Operand<reg::RegQ>) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegQ, reg::RegInv> {
instr: instr::InstrName::CallStar,
reg1: Some(op),
reg2: None,
}))
}
pub fn leave() -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Leave,
reg1: None,
reg2: None,
}))
}
pub fn ret() -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Ret,
reg1: None,
reg2: None,
}))
}
pub fn jmp(label: reg::Label) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::Jump(label),
reg1: None,
reg2: None,
}))
}
pub fn jmp_star(op: reg::Operand<reg::RegQ>) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegQ, reg::RegInv> {
instr: instr::InstrName::JumpStar,
reg1: Some(op),
reg2: None,
}))
}
pub fn jcc(cond: instr::Cond, label: reg::Label) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::CondJump(cond, label),
reg1: None,
reg2: None,
}))
}
pub fn jz(label: reg::Label) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::CondJump(instr::Cond::Z, label),
reg1: None,
reg2: None,
}))
}
pub fn jnz(label: reg::Label) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::CondJump(instr::Cond::NZ, label),
reg1: None,
reg2: None,
}))
}
pub fn jae(label: reg::Label) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegInv, reg::RegInv> {
instr: instr::InstrName::CondJump(instr::Cond::AE, label),
reg1: None,
reg2: None,
}))
}
build_instr_op_op!(Cmp, cmpb, cmpw, cmpl, cmpq);
build_instr_op_op!(Test, testb, testw, testl, testq);
pub fn set(cond: instr::Cond, reg: reg::Operand<reg::RegB>) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegB, reg::RegInv> {
instr: instr::InstrName::Set(cond),
reg1: Some(reg),
reg2: None,
}))
}
pub fn pushq(op: reg::Operand<reg::RegQ>) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegQ, reg::RegInv> {
instr: instr::InstrName::Push,
reg1: Some(op),
reg2: None,
}))
}
pub fn popq(reg: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegQ, reg::RegInv> {
instr: instr::InstrName::Pop,
reg1: Some(reg!(reg)),
reg2: None,
}))
}
pub fn comment(s: String) -> Text {
Text::comment(s)
}
#[cfg(target_os = "linux")]
pub fn deplq(l: reg::Label, reg: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegQ, reg::RegQ> {
instr: instr::InstrName::Move,
reg1: Some(reg::Operand::LabAbsAddr(l)),
reg2: Some(reg!(reg)),
}))
}
#[cfg(target_os = "macos")]
pub fn deplq(l: reg::Label, reg: reg::RegQ) -> Text {
Text::new(Box::new(instr::Instruction::<reg::RegQ, reg::RegQ> {
instr: instr::InstrName::Lea,
reg1: Some(reg::Operand::LabAbsAddr(l)),
reg2: Some(reg!(reg)),
}))
}
pub fn cmovw(
cond: instr::Cond,
reg1: reg::Operand<reg::RegW>,
reg2: reg::Operand<reg::RegW>,
) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Cmov(cond),
reg1: Some(reg1),
reg2: Some(reg2),
}))
}
pub fn cmovl(
cond: instr::Cond,
reg1: reg::Operand<reg::RegL>,
reg2: reg::Operand<reg::RegL>,
) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Cmov(cond),
reg1: Some(reg1),
reg2: Some(reg2),
}))
}
pub fn cmovq(
cond: instr::Cond,
reg1: reg::Operand<reg::RegQ>,
reg2: reg::Operand<reg::RegQ>,
) -> Text {
Text::new(Box::new(instr::Instruction {
instr: instr::InstrName::Cmov(cond),
reg1: Some(reg1),
reg2: Some(reg2),
}))
}
pub fn new_label(name: &str) -> reg::Label {
reg::Label::from_str(name.to_string())
}