use super::msp430_instr_info::Msp430InstrInfo;
use crate::codegen::*;
use crate::module::Module;
pub struct Msp430AsmPrinter {
pub output: String,
pub instr_info: Msp430InstrInfo,
}
impl Msp430AsmPrinter {
pub fn new() -> Self {
Msp430AsmPrinter {
output: String::new(),
instr_info: Msp430InstrInfo::new(),
}
}
pub fn print_function(&mut self, mf: &MachineFunction) {
self.print_prologue(mf);
for bb in &mf.blocks {
self.print_basic_block(bb);
}
self.print_epilogue(mf);
}
pub fn print_module(&mut self, module: &Module) {
self.output.push_str("\t.text\n");
self.output.push_str("\t.code\t16\n");
if !module.source_filename.is_empty() {
self.output
.push_str(&format!("\t.file\t\"{}\"\n", module.source_filename));
}
}
pub fn print_prologue(&mut self, mf: &MachineFunction) {
let name = &mf.name;
self.output.push_str(&format!("\t.globl\t{}\n", name));
self.output
.push_str(&format!("\t.type\t{}, @function\n", name));
self.output.push_str(&format!("{}:\n", name));
}
pub fn print_epilogue(&mut self, mf: &MachineFunction) {
let name = &mf.name;
self.output
.push_str(&format!("\t.size\t{}, .-{}\n", name, name));
}
pub fn print_basic_block(&mut self, bb: &MachineBasicBlock) {
if !bb.name.is_empty() {
self.output.push_str(&format!(".LBB_{}:\n", bb.name));
}
for mi in &bb.instructions {
self.print_instruction(mi);
}
}
pub fn print_instruction(&mut self, mi: &MachineInstr) {
let mnemonic = self.get_mnemonic(mi.opcode);
if mnemonic.is_empty() || mnemonic == "INVALID" {
return;
}
let op_strs: Vec<String> = mi
.operands
.iter()
.map(|op| self.print_operand(op))
.filter(|s| !s.is_empty())
.collect();
if op_strs.is_empty() {
self.output.push_str(&format!("\t{}\n", mnemonic));
} else {
self.output
.push_str(&format!("\t{}\t{}\n", mnemonic, op_strs.join(", ")));
}
}
pub fn print_operand(&self, op: &MachineOperand) -> String {
match op {
MachineOperand::Reg(vr) => format!("%vreg{}", vr),
MachineOperand::PhysReg(reg) => self.format_reg(*reg as u16),
MachineOperand::Imm(imm) => format!("#{}", imm),
MachineOperand::Label(label) => format!(".LBB_{}", label),
MachineOperand::Global(name) => name.clone(),
}
}
fn format_reg(&self, reg_id: u16) -> String {
match reg_id {
0 => "PC".into(),
1 => "SP".into(),
2 => "SR".into(),
3 => "CG2".into(),
_ if reg_id >= 4 && reg_id <= 15 => format!("R{}", reg_id),
_ => format!("R{}", reg_id),
}
}
pub fn get_mnemonic(&self, opcode: u32) -> String {
match opcode {
0 => "rrc".to_string(),
1 => "swpb".to_string(),
2 => "rra".to_string(),
3 => "sxt".to_string(),
4 => "push".to_string(),
5 => "pop".to_string(),
6 => "call".to_string(),
7 => "reti".to_string(),
10 => "mov".to_string(),
11 => "add".to_string(),
12 => "addc".to_string(),
13 => "subc".to_string(),
14 => "sub".to_string(),
15 => "cmp".to_string(),
16 => "dadd".to_string(),
17 => "bit".to_string(),
18 => "bic".to_string(),
19 => "bis".to_string(),
20 => "xor".to_string(),
21 => "and".to_string(),
30 => "jne".to_string(),
31 => "jeq".to_string(),
32 => "jnc".to_string(),
33 => "jc".to_string(),
34 => "jn".to_string(),
35 => "jge".to_string(),
36 => "jl".to_string(),
37 => "jmp".to_string(),
40 => "clr".to_string(),
41 => "clrc".to_string(),
42 => "setc".to_string(),
43 => "clrz".to_string(),
44 => "setz".to_string(),
45 => "clrn".to_string(),
46 => "setn".to_string(),
47 => "dint".to_string(),
48 => "eint".to_string(),
49 => "nop".to_string(),
50 => "br".to_string(),
51 => "ret".to_string(),
52 => "tst".to_string(),
53 => "inv".to_string(),
54 => "rla".to_string(),
55 => "rlc".to_string(),
56 => "adc".to_string(),
57 => "dadc".to_string(),
60 => "mova".to_string(),
61 => "cmpa".to_string(),
62 => "adda".to_string(),
63 => "suba".to_string(),
64 => "bra".to_string(),
65 => "reta".to_string(),
66 => "pushm".to_string(),
67 => "popm".to_string(),
68 => "calla".to_string(),
_ => "INVALID".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mnemonic_lookup() {
let printer = Msp430AsmPrinter::new();
assert_eq!(printer.get_mnemonic(10), "mov");
assert_eq!(printer.get_mnemonic(11), "add");
assert_eq!(printer.get_mnemonic(14), "sub");
assert_eq!(printer.get_mnemonic(19), "bis");
assert_eq!(printer.get_mnemonic(20), "xor");
assert_eq!(printer.get_mnemonic(21), "and");
assert_eq!(printer.get_mnemonic(30), "jne");
assert_eq!(printer.get_mnemonic(37), "jmp");
assert_eq!(printer.get_mnemonic(49), "nop");
assert_eq!(printer.get_mnemonic(51), "ret");
}
#[test]
fn test_format_reg() {
let printer = Msp430AsmPrinter::new();
assert_eq!(printer.format_reg(0), "PC");
assert_eq!(printer.format_reg(1), "SP");
assert_eq!(printer.format_reg(4), "R4");
assert_eq!(printer.format_reg(15), "R15");
}
#[test]
fn test_print_nop() {
let mut printer = Msp430AsmPrinter::new();
let mi = MachineInstr::new(49);
printer.print_instruction(&mi);
assert!(printer.output.contains("nop"));
}
#[test]
fn test_print_ret() {
let mut printer = Msp430AsmPrinter::new();
let mi = MachineInstr::new(51);
printer.print_instruction(&mi);
assert!(printer.output.contains("ret"));
}
}