use crate::core::types::CpuType;
const DN: [&str; 8] = ["D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7"];
const AN: [&str; 8] = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7"];
const CC: [&str; 16] = [
"T", "F", "HI", "LS", "CC", "CS", "NE", "EQ", "VC", "VS", "PL", "MI", "GE", "LT", "GT", "LE",
];
fn size_suffix(size: u8) -> &'static str {
match size {
0 => ".B",
1 => ".W",
2 => ".L",
_ => "",
}
}
pub fn disassemble(_pc: u32, opcode: u16, cpu_type: CpuType) -> (String, u32) {
let op_hi = (opcode >> 12) & 0xF;
match op_hi {
0x0 => disasm_0xxx(opcode),
0x1 => disasm_move(opcode, 0),
0x2 => disasm_move(opcode, 2),
0x3 => disasm_move(opcode, 1),
0x4 => disasm_4xxx(opcode),
0x5 => disasm_5xxx(opcode),
0x6 => disasm_branch(opcode),
0x7 => {
let reg = ((opcode >> 9) & 7) as usize;
let data = (opcode & 0xFF) as i8;
(format!("MOVEQ #{},{}", data, DN[reg]), 2)
}
0x8 => disasm_8xxx(opcode),
0x9 => disasm_9xxx(opcode),
0xA => (format!("DC.W ${:04X}", opcode), 2),
0xB => disasm_bxxx(opcode),
0xC => disasm_cxxx(opcode),
0xD => disasm_dxxx(opcode),
0xE => disasm_shift(opcode),
0xF => disasm_fline(opcode, cpu_type),
_ => (format!("DC.W ${:04X}", opcode), 2),
}
}
fn disasm_0xxx(opcode: u16) -> (String, u32) {
let size = ((opcode >> 6) & 3) as u8;
match (opcode >> 8) & 0xF {
0x0 => {
if opcode == 0x003C {
return ("ORI.B #xx,CCR".to_string(), 4);
}
return (format!("ORI{} #xx,<ea>", size_suffix(size)), 4);
}
0x2 => return (format!("ANDI{} #xx,<ea>", size_suffix(size)), 4),
0x4 => return (format!("SUBI{} #xx,<ea>", size_suffix(size)), 4),
0x6 => return (format!("ADDI{} #xx,<ea>", size_suffix(size)), 4),
0x8 => {
let bit_op = (opcode >> 6) & 3;
let op_name = match bit_op {
0 => "BTST",
1 => "BCHG",
2 => "BCLR",
3 => "BSET",
_ => "???",
};
return (format!("{} #xx,<ea>", op_name), 4);
}
0xA => return (format!("EORI{} #xx,<ea>", size_suffix(size)), 4),
0xC => return (format!("CMPI{} #xx,<ea>", size_suffix(size)), 4),
_ => {}
}
if (opcode & 0x0138) == 0x0108 {
return ("MOVEP <ea>,Dn".to_string(), 4);
}
let bit_op = (opcode >> 6) & 3;
let reg = ((opcode >> 9) & 7) as usize;
let op_name = match bit_op {
0 => "BTST",
1 => "BCHG",
2 => "BCLR",
3 => "BSET",
_ => "???",
};
(format!("{} {},<ea>", op_name, DN[reg]), 2)
}
fn disasm_move(opcode: u16, size: u8) -> (String, u32) {
let src_mode = (opcode >> 3) & 7;
let src_reg = opcode & 7;
let dst_reg = (opcode >> 9) & 7;
let dst_mode = (opcode >> 6) & 7;
let src = format_ea(src_mode as u8, src_reg as u8, size);
let dst = format_ea(dst_mode as u8, dst_reg as u8, size);
(format!("MOVE{} {},{}", size_suffix(size), src, dst), 2)
}
fn disasm_4xxx(opcode: u16) -> (String, u32) {
if opcode == 0x4E71 {
return ("NOP".to_string(), 2);
}
if opcode == 0x4E75 {
return ("RTS".to_string(), 2);
}
if opcode == 0x4E73 {
return ("RTE".to_string(), 2);
}
if opcode == 0x4E77 {
return ("RTR".to_string(), 2);
}
if opcode == 0x4E72 {
return ("STOP #xxxx".to_string(), 4);
}
if opcode == 0x4E70 {
return ("RESET".to_string(), 2);
}
if (opcode & 0xFFF0) == 0x4E40 {
let vector = opcode & 0xF;
return (format!("TRAP #{}", vector), 2);
}
if (opcode & 0xFFF8) == 0x4E50 {
let reg = (opcode & 7) as usize;
return (format!("LINK {},#xxxx", AN[reg]), 4);
}
if (opcode & 0xFFF8) == 0x4E58 {
let reg = (opcode & 7) as usize;
return (format!("UNLK {}", AN[reg]), 2);
}
if (opcode & 0xFFF0) == 0x4E60 {
let reg = (opcode & 7) as usize;
let dir = (opcode >> 3) & 1;
if dir == 0 {
return (format!("MOVE {},USP", AN[reg]), 2);
} else {
return (format!("MOVE USP,{}", AN[reg]), 2);
}
}
if (opcode & 0xFFC0) == 0x4EC0 {
return ("JMP <ea>".to_string(), 2);
}
if (opcode & 0xFFC0) == 0x4E80 {
return ("JSR <ea>".to_string(), 2);
}
if (opcode & 0xF1C0) == 0x41C0 {
let reg = ((opcode >> 9) & 7) as usize;
return (format!("LEA <ea>,{}", AN[reg]), 2);
}
if (opcode & 0xFFC0) == 0x4840 {
return ("PEA <ea>".to_string(), 2);
}
if (opcode & 0xFB80) == 0x4880 {
let dir = (opcode >> 10) & 1;
let size = if (opcode >> 6) & 1 == 0 { ".W" } else { ".L" };
if dir == 0 {
return (format!("MOVEM{} <regs>,<ea>", size), 4);
} else {
return (format!("MOVEM{} <ea>,<regs>", size), 4);
}
}
let size = ((opcode >> 6) & 3) as u8;
match (opcode >> 8) & 0xF {
0x2 => return (format!("CLR{} <ea>", size_suffix(size)), 2),
0x4 => return (format!("NEG{} <ea>", size_suffix(size)), 2),
0x0 => return (format!("NEGX{} <ea>", size_suffix(size)), 2),
0x6 => return (format!("NOT{} <ea>", size_suffix(size)), 2),
0xA => return (format!("TST{} <ea>", size_suffix(size)), 2),
_ => {}
}
if (opcode & 0xFEB8) == 0x4880 {
let reg = (opcode & 7) as usize;
let size = if (opcode >> 6) & 1 == 0 { ".W" } else { ".L" };
return (format!("EXT{} {}", size, DN[reg]), 2);
}
if (opcode & 0xFFF8) == 0x4840 {
let reg = (opcode & 7) as usize;
return (format!("SWAP {}", DN[reg]), 2);
}
if (opcode & 0xF1C0) == 0x4180 {
let reg = ((opcode >> 9) & 7) as usize;
return (format!("CHK <ea>,{}", DN[reg]), 2);
}
(format!("DC.W ${:04X}", opcode), 2)
}
fn disasm_5xxx(opcode: u16) -> (String, u32) {
let size = ((opcode >> 6) & 3) as u8;
if size == 3 {
let mode = (opcode >> 3) & 7;
let cond = ((opcode >> 8) & 0xF) as usize;
if mode == 1 {
let reg = (opcode & 7) as usize;
return (format!("DB{} {},<label>", CC[cond], DN[reg]), 4);
} else {
return (format!("S{} <ea>", CC[cond]), 2);
}
}
let data = ((opcode >> 9) & 7) as u8;
let data = if data == 0 { 8 } else { data };
let op = if (opcode >> 8) & 1 == 0 {
"ADDQ"
} else {
"SUBQ"
};
(format!("{}{} #{},<ea>", op, size_suffix(size), data), 2)
}
fn disasm_branch(opcode: u16) -> (String, u32) {
let cond = ((opcode >> 8) & 0xF) as usize;
let disp = (opcode & 0xFF) as i8;
let mnemonic = match cond {
0 => "BRA",
1 => "BSR",
_ => {
return (
format!("B{} <label>", CC[cond]),
if disp == 0 { 4 } else { 2 },
);
}
};
if disp == 0 {
(format!("{}.W <label>", mnemonic), 4)
} else {
(format!("{}.S <label>", mnemonic), 2)
}
}
fn disasm_8xxx(opcode: u16) -> (String, u32) {
let reg = ((opcode >> 9) & 7) as usize;
let size = ((opcode >> 6) & 3) as u8;
let opmode = (opcode >> 6) & 7;
if opmode == 4 {
return ("SBCD <ea>,<ea>".to_string(), 2);
}
if opmode == 7 {
return (format!("DIVS.W <ea>,{}", DN[reg]), 2);
}
if opmode == 3 {
return (format!("DIVU.W <ea>,{}", DN[reg]), 2);
}
if (opcode >> 8) & 1 == 0 {
(format!("OR{} <ea>,{}", size_suffix(size), DN[reg]), 2)
} else {
(format!("OR{} {},<ea>", size_suffix(size), DN[reg]), 2)
}
}
fn disasm_9xxx(opcode: u16) -> (String, u32) {
let reg = ((opcode >> 9) & 7) as usize;
let size = ((opcode >> 6) & 3) as u8;
let opmode = (opcode >> 6) & 7;
if opmode == 3 || opmode == 7 {
let sz = if opmode == 3 { ".W" } else { ".L" };
return (format!("SUBA{} <ea>,{}", sz, AN[reg]), 2);
}
if (opcode & 0x0130) == 0x0100 && size != 3 {
return (format!("SUBX{} <ea>,<ea>", size_suffix(size)), 2);
}
if (opcode >> 8) & 1 == 0 {
(format!("SUB{} <ea>,{}", size_suffix(size), DN[reg]), 2)
} else {
(format!("SUB{} {},<ea>", size_suffix(size), DN[reg]), 2)
}
}
fn disasm_bxxx(opcode: u16) -> (String, u32) {
let reg = ((opcode >> 9) & 7) as usize;
let size = ((opcode >> 6) & 3) as u8;
let opmode = (opcode >> 6) & 7;
if opmode == 3 || opmode == 7 {
let sz = if opmode == 3 { ".W" } else { ".L" };
return (format!("CMPA{} <ea>,{}", sz, AN[reg]), 2);
}
if (opcode & 0x0138) == 0x0108 {
return (format!("CMPM{} (Ay)+,(Ax)+", size_suffix(size)), 2);
}
if (opcode >> 8) & 1 == 1 {
return (format!("EOR{} {},<ea>", size_suffix(size), DN[reg]), 2);
}
(format!("CMP{} <ea>,{}", size_suffix(size), DN[reg]), 2)
}
fn disasm_cxxx(opcode: u16) -> (String, u32) {
let reg = ((opcode >> 9) & 7) as usize;
let size = ((opcode >> 6) & 3) as u8;
let opmode = (opcode >> 6) & 7;
if opmode == 4 {
return ("ABCD <ea>,<ea>".to_string(), 2);
}
if (opcode & 0xF130) == 0xC100 {
return ("EXG Rx,Ry".to_string(), 2);
}
if opmode == 7 {
return (format!("MULS.W <ea>,{}", DN[reg]), 2);
}
if opmode == 3 {
return (format!("MULU.W <ea>,{}", DN[reg]), 2);
}
if (opcode >> 8) & 1 == 0 {
(format!("AND{} <ea>,{}", size_suffix(size), DN[reg]), 2)
} else {
(format!("AND{} {},<ea>", size_suffix(size), DN[reg]), 2)
}
}
fn disasm_dxxx(opcode: u16) -> (String, u32) {
let reg = ((opcode >> 9) & 7) as usize;
let size = ((opcode >> 6) & 3) as u8;
let opmode = (opcode >> 6) & 7;
if opmode == 3 || opmode == 7 {
let sz = if opmode == 3 { ".W" } else { ".L" };
return (format!("ADDA{} <ea>,{}", sz, AN[reg]), 2);
}
if (opcode & 0x0130) == 0x0100 && size != 3 {
return (format!("ADDX{} <ea>,<ea>", size_suffix(size)), 2);
}
if (opcode >> 8) & 1 == 0 {
(format!("ADD{} <ea>,{}", size_suffix(size), DN[reg]), 2)
} else {
(format!("ADD{} {},<ea>", size_suffix(size), DN[reg]), 2)
}
}
fn disasm_shift(opcode: u16) -> (String, u32) {
let size = ((opcode >> 6) & 3) as u8;
if size == 3 {
let op = match (opcode >> 9) & 7 {
0 => "ASR",
1 => "ASL",
2 => "LSR",
3 => "LSL",
4 => "ROXR",
5 => "ROXL",
6 => "ROR",
7 => "ROL",
_ => "???",
};
return (format!("{} <ea>", op), 2);
}
let ir = (opcode >> 5) & 1; let dr = (opcode >> 8) & 1; let op_type = (opcode >> 3) & 3;
let op_base = match op_type {
0 => {
if dr == 0 {
"ASR"
} else {
"ASL"
}
}
1 => {
if dr == 0 {
"LSR"
} else {
"LSL"
}
}
2 => {
if dr == 0 {
"ROXR"
} else {
"ROXL"
}
}
3 => {
if dr == 0 {
"ROR"
} else {
"ROL"
}
}
_ => "???",
};
let reg = (opcode & 7) as usize;
let cnt = ((opcode >> 9) & 7) as u8;
if ir == 0 {
let cnt = if cnt == 0 { 8 } else { cnt };
(
format!("{}{} #{},{}", op_base, size_suffix(size), cnt, DN[reg]),
2,
)
} else {
let cnt_reg = cnt as usize;
(
format!(
"{}{} {},{}",
op_base,
size_suffix(size),
DN[cnt_reg],
DN[reg]
),
2,
)
}
}
fn disasm_fline(opcode: u16, cpu_type: CpuType) -> (String, u32) {
let cp_id = (opcode >> 9) & 7;
if cp_id == 1 {
match cpu_type {
CpuType::M68EC020
| CpuType::M68020
| CpuType::M68EC030
| CpuType::M68030
| CpuType::M68EC040
| CpuType::M68LC040
| CpuType::M68040 => {
let cmd = (opcode >> 6) & 7;
match cmd {
0 => return ("FMOVE/FINT/... <ea>,FPn".to_string(), 4),
1 => return ("FBcc <label>".to_string(), 4),
2 => return ("FMOVEM <ea>,<list>".to_string(), 4),
3 => return ("FMOVE FPn,<ea>".to_string(), 4),
4 => return ("FMOVEM <list>,<ea>".to_string(), 4),
5 => return ("FMOVE.L <ea>,FPCR/FPSR/FPIAR".to_string(), 4),
6 => return ("FMOVE.L FPCR/FPSR/FPIAR,<ea>".to_string(), 4),
7 => return ("FDBcc/FScc/FTRAPcc".to_string(), 4),
_ => {}
}
}
_ => {}
}
}
if cp_id == 0 {
return ("PMOVE/PTEST/...".to_string(), 4);
}
(format!("DC.W ${:04X}", opcode), 2)
}
fn format_ea(mode: u8, reg: u8, _size: u8) -> String {
let reg = reg as usize;
match mode {
0 => DN[reg].to_string(), 1 => AN[reg].to_string(), 2 => format!("({})", AN[reg]), 3 => format!("({})+", AN[reg]), 4 => format!("-({})", AN[reg]), 5 => format!("d16({})", AN[reg]), 6 => format!("d8({},Xi)", AN[reg]), 7 => match reg {
0 => "(xxx).W".to_string(), 1 => "(xxx).L".to_string(), 2 => "d16(PC)".to_string(), 3 => "d8(PC,Xi)".to_string(), 4 => "#<data>".to_string(), _ => "???".to_string(),
},
_ => "???".to_string(),
}
}