jevsnes.git / third-party / rust / jgenesis / cpu / m68000-emu / src / disassemble.rs
1use crate::M68000;
2use crate::core::instructions::{
3    BranchCondition, Direction, Instruction, ShiftCount, ShiftDirection, UspDirection,
4};
5use crate::core::{
6    AddressRegister, AddressingMode, DataRegister, IndexRegister, IndexSize, OpSize,
7};
8use jgenesis_common::num::GetBit;
9use std::borrow::Cow;
10use std::mem;
11
12const DATA_REGISTERS: &[&str; 8] = &["d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7"];
13const ADDRESS_REGISTERS: &[&str; 8] = &["a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7"];
14const ADDRESS_INDIRECT: &[&str; 8] =
15    &["(a0)", "(a1)", "(a2)", "(a3)", "(a4)", "(a5)", "(a6)", "(a7)"];
16const ADDRESS_POSTINC: &[&str; 8] =
17    &["(a0)+", "(a1)+", "(a2)+", "(a3)+", "(a4)+", "(a5)+", "(a6)+", "(a7)+"];
18const ADDRESS_PREDEC: &[&str; 8] =
19    &["-(a0)", "-(a1)", "-(a2)", "-(a3)", "-(a4)", "-(a5)", "-(a6)", "-(a7)"];
20
21fn size_str(size: OpSize) -> &'static str {
22    match size {
23        OpSize::Byte => "b",
24        OpSize::Word => "w",
25        OpSize::LongWord => "l",
26    }
27}
28
29fn condition_suffix(condition: BranchCondition) -> &'static str {
30    match condition {
31        BranchCondition::True => "t",
32        BranchCondition::False => "f",
33        BranchCondition::Higher => "hi",
34        BranchCondition::LowerOrSame => "ls",
35        BranchCondition::CarryClear => "cc",
36        BranchCondition::CarrySet => "cs",
37        BranchCondition::NotEqual => "ne",
38        BranchCondition::Equal => "eq",
39        BranchCondition::OverflowClear => "vc",
40        BranchCondition::OverflowSet => "vs",
41        BranchCondition::Plus => "pl",
42        BranchCondition::Minus => "mi",
43        BranchCondition::GreaterOrEqual => "ge",
44        BranchCondition::LessThan => "lt",
45        BranchCondition::GreaterThan => "gt",
46        BranchCondition::LessOrEqual => "le",
47    }
48}
49
50fn direction_str(direction: ShiftDirection) -> &'static str {
51    match direction {
52        ShiftDirection::Left => "l",
53        ShiftDirection::Right => "r",
54    }
55}
56
57fn shift_count_str(count: ShiftCount) -> Cow<'static, str> {
58    match count {
59        ShiftCount::Constant(value) => format!("#{value}").into(),
60        ShiftCount::Register(r) => DATA_REGISTERS[r.0 as usize].into(),
61    }
62}
63
64#[derive(Debug, Clone, Copy)]
65pub enum DisassembledIndex {
66    Data(u8),
67    Address(u8),
68}
69
70impl From<IndexRegister> for DisassembledIndex {
71    fn from(value: IndexRegister) -> Self {
72        match value {
73            IndexRegister::Data(DataRegister(r)) => Self::Data(r),
74            IndexRegister::Address(AddressRegister(r)) => Self::Address(r),
75        }
76    }
77}
78
79impl DisassembledIndex {
80    #[must_use]
81    pub fn resolve(self, cpu: &M68000, size: IndexSize) -> u32 {
82        let register_value = match self {
83            Self::Data(r) => cpu.data_registers()[r as usize],
84            Self::Address(7) => cpu.stack_pointer(),
85            Self::Address(r) => cpu.address_registers()[r as usize],
86        };
87
88        match size {
89            IndexSize::SignExtendedWord => register_value as i16 as u32,
90            IndexSize::LongWord => register_value,
91        }
92    }
93}
94
95#[derive(Debug, Clone, Copy)]
96pub enum MemoryAccess {
97    Absolute {
98        address: u32,
99        size: OpSize,
100    },
101    AddressIndirect {
102        register: u8,
103        displacement: u32,
104        index: Option<(DisassembledIndex, IndexSize)>,
105        size: OpSize,
106    },
107    AddressIndirectPredecrement {
108        register: u8,
109        size: OpSize,
110    },
111    PcRelative {
112        pc: u32,
113        displacement: u32,
114        index: Option<(DisassembledIndex, IndexSize)>,
115        size: OpSize,
116    },
117}
118
119impl MemoryAccess {
120    #[must_use]
121    pub fn resolve_address(self, cpu: &M68000) -> (u32, OpSize) {
122        match self {
123            Self::Absolute { address, size } => (address, size),
124            Self::AddressIndirect { register, displacement, index, size } => {
125                let mut address = address_register(cpu, register).wrapping_add(displacement);
126                if let Some((index, size)) = index {
127                    address = address.wrapping_add(index.resolve(cpu, size));
128                }
129                (address, size)
130            }
131            Self::AddressIndirectPredecrement { register, size } => {
132                let decrement = match (size, register) {
133                    (OpSize::Byte, 7) | (OpSize::Word, _) => 2,
134                    (OpSize::Byte, _) => 1,
135                    (OpSize::LongWord, _) => 4,
136                };
137                let address = address_register(cpu, register).wrapping_sub(decrement);
138                (address, size)
139            }
140            Self::PcRelative { pc, displacement, index, size } => {
141                let mut address = pc.wrapping_add(displacement);
142                if let Some((index, size)) = index {
143                    address = address.wrapping_add(index.resolve(cpu, size));
144                }
145                (address, size)
146            }
147        }
148    }
149}
150
151fn address_register(cpu: &M68000, register: u8) -> u32 {
152    match register {
153        7 => cpu.stack_pointer(),
154        _ => cpu.address_registers()[register as usize],
155    }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum MemoryReadType {
160    EffectiveAddress, // JMP, JSR, LEA, PEA
161    Read,
162}
163
164#[derive(Debug, Clone)]
165pub struct DisassembledInstruction {
166    pub opcodes: Vec<u16>,
167    pub text: String,
168    pub next_opcode_addr: u32,
169    pub memory_read: Option<MemoryAccess>,
170    pub memory_read_type: MemoryReadType,
171    pub memory_write: Option<MemoryAccess>,
172}
173
174impl DisassembledInstruction {
175    #[must_use]
176    pub fn new() -> Self {
177        Self {
178            opcodes: Vec::new(),
179            text: String::new(),
180            next_opcode_addr: 0,
181            memory_read: None,
182            memory_read_type: MemoryReadType::Read,
183            memory_write: None,
184        }
185    }
186
187    fn clear(&mut self) {
188        self.opcodes.clear();
189        self.text.clear();
190        self.memory_read = None;
191        self.memory_read_type = MemoryReadType::Read;
192        self.memory_write = None;
193    }
194}
195
196impl Default for DisassembledInstruction {
197    fn default() -> Self {
198        Self::new()
199    }
200}
201
202struct WordReader<F> {
203    pc: u32,
204    read: F,
205    opcodes: Vec<u16>,
206}
207
208impl<F: FnMut() -> u16> WordReader<F> {
209    fn read_word(&mut self) -> u16 {
210        self.pc = (self.pc + 2) & 0xFFFFFF;
211
212        let word = (self.read)();
213        self.opcodes.push(word);
214        word
215    }
216}
217
218struct Disassembler<'a, F> {
219    reader: WordReader<F>,
220    out: &'a mut DisassembledInstruction,
221}
222
223pub fn disassemble_into(
224    out: &mut DisassembledInstruction,
225    pc: u32,
226    read_word: impl FnMut() -> u16,
227) {
228    out.clear();
229    Disassembler::new(pc, read_word, out).disassemble();
230}
231
232impl<'a, F> Disassembler<'a, F>
233where
234    F: FnMut() -> u16,
235{
236    fn new(pc: u32, read_word: F, out: &'a mut DisassembledInstruction) -> Self {
237        let reader = WordReader { pc, read: read_word, opcodes: mem::take(&mut out.opcodes) };
238        Self { reader, out }
239    }
240
241    fn read_word(&mut self) -> u16 {
242        self.reader.read_word()
243    }
244
245    fn disassemble(mut self) {
246        let instruction = Instruction::decode_from(self.read_word());
247
248        self.out.text = match instruction {
249            Instruction::Add { size, source, dest, with_extend } => {
250                self.add(size, source, dest, with_extend)
251            }
252            Instruction::AddDecimal { source, dest } => self.abcd(source, dest),
253            Instruction::And { size, source, dest } => self.and(size, source, dest),
254            Instruction::AndToCcr => self.andi_to_ccr(),
255            Instruction::AndToSr => self.andi_to_sr(),
256            Instruction::ArithmeticShiftMemory(direction, dest) => {
257                self.asl_asr_memory(direction, dest)
258            }
259            Instruction::ArithmeticShiftRegister(size, direction, register, count) => {
260                Self::asl_asr_register(size, direction, register, count)
261            }
262            Instruction::BitTest { source, dest } => self.btst(source, dest),
263            Instruction::BitTestAndChange { source, dest } => self.bchg(source, dest),
264            Instruction::BitTestAndClear { source, dest } => self.bclr(source, dest),
265            Instruction::BitTestAndSet { source, dest } => self.bset(source, dest),
266            Instruction::Branch(condition, displacement) => self.branch(condition, displacement),
267            Instruction::BranchDecrement(condition, register) => self.dbcc(condition, register),
268            Instruction::BranchToSubroutine(displacement) => self.bsr(displacement),
269            Instruction::CheckRegister(register, source) => self.chk(register, source),
270            Instruction::Clear(size, dest) => self.clr(size, dest),
271            Instruction::Compare { size, source, dest } => self.cmp(size, source, dest),
272            Instruction::DivideSigned(dest, source) => self.divs(dest, source),
273            Instruction::DivideUnsigned(dest, source) => self.divu(dest, source),
274            Instruction::ExchangeAddress(a0, a1) => Self::exg_address(a0, a1),
275            Instruction::ExchangeData(d0, d1) => Self::exg_data(d0, d1),
276            Instruction::ExchangeDataAddress(d, a) => Self::exg_data_address(d, a),
277            Instruction::ExclusiveOr { size, source, dest } => self.eor(size, source, dest),
278            Instruction::ExclusiveOrToCcr => self.eori_to_ccr(),
279            Instruction::ExclusiveOrToSr => self.eori_to_sr(),
280            Instruction::Extend(size, dest) => Self::ext(size, dest),
281            Instruction::Illegal { opcode } => {
282                format!("illegal ;0x{opcode:04X}")
283            }
284            Instruction::Jump(dest) => self.jmp(dest),
285            Instruction::JumpToSubroutine(dest) => self.jsr(dest),
286            Instruction::Link(register) => self.link(register),
287            Instruction::LoadEffectiveAddress(source, dest) => self.lea(source, dest),
288            Instruction::LogicalShiftMemory(direction, dest) => {
289                self.lsl_lsr_memory(direction, dest)
290            }
291            Instruction::LogicalShiftRegister(size, direction, register, count) => {
292                Self::lsl_lsr_register(size, direction, register, count)
293            }
294            Instruction::Move { size, source, dest } => self.move_(size, source, dest),
295            Instruction::MoveFromSr(dest) => self.move_from_sr(dest),
296            Instruction::MoveMultiple(size, am, direction) => self.movem(size, am, direction),
297            Instruction::MovePeripheral(size, data, address, direction) => {
298                self.movep(size, data, address, direction)
299            }
300            Instruction::MoveQuick(value, register) => Self::moveq(value, register),
301            Instruction::MoveToCcr(source) => self.move_to_ccr(source),
302            Instruction::MoveToSr(source) => self.move_to_sr(source),
303            Instruction::MoveUsp(direction, register) => Self::move_usp(direction, register),
304            Instruction::MultiplySigned(dest, source) => self.muls(dest, source),
305            Instruction::MultiplyUnsigned(dest, source) => self.mulu(dest, source),
306            Instruction::Negate { size, dest, with_extend } => self.neg(size, dest, with_extend),
307            Instruction::NegateDecimal(dest) => self.nbcd(dest),
308            Instruction::NoOp => "nop".into(),
309            Instruction::Not(size, dest) => self.not(size, dest),
310            Instruction::Or { size, source, dest } => self.or(size, source, dest),
311            Instruction::OrToCcr => self.ori_to_ccr(),
312            Instruction::OrToSr => self.ori_to_sr(),
313            Instruction::PushEffectiveAddress(source) => self.pea(source),
314            Instruction::Reset => "reset".into(),
315            Instruction::Return { restore_ccr } => {
316                if restore_ccr {
317                    "rtr".into()
318                } else {
319                    "rts".into()
320                }
321            }
322            Instruction::ReturnFromException => "rte".into(),
323            Instruction::RotateMemory(direction, dest) => self.rol_ror_memory(direction, dest),
324            Instruction::RotateRegister(size, direction, register, count) => {
325                Self::rol_ror_register(size, direction, register, count)
326            }
327            Instruction::RotateThruExtendMemory(direction, dest) => {
328                self.roxl_roxr_memory(direction, dest)
329            }
330            Instruction::RotateThruExtendRegister(size, direction, register, count) => {
331                Self::roxl_roxr_register(size, direction, register, count)
332            }
333            Instruction::Set(condition, dest) => self.scc(condition, dest),
334            Instruction::Subtract { size, source, dest, with_extend } => {
335                self.sub(size, source, dest, with_extend)
336            }
337            Instruction::SubtractDecimal { source, dest } => self.sbcd(source, dest),
338            Instruction::Swap(register) => Self::swap(register),
339            Instruction::Stop => "stop".into(),
340            Instruction::Test(size, source) => self.tst(size, source),
341            Instruction::TestAndSet(dest) => self.tas(dest),
342            Instruction::Trap(vector) => Self::trap(vector),
343            Instruction::TrapOnOverflow => "trapv".into(),
344            Instruction::Unlink(register) => Self::unlk(register),
345        };
346
347        self.out.opcodes = mem::take(&mut self.reader.opcodes);
348        self.out.next_opcode_addr = self.reader.pc;
349    }
350
351    fn binary_op(
352        &mut self,
353        name: &str,
354        size: OpSize,
355        source: AddressingMode,
356        dest: AddressingMode,
357    ) -> String {
358        let (source_str, source_access) = self.resolve_addressing_mode(source, size);
359        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, size);
360
361        self.out.memory_read = source_access;
362        self.out.memory_write = dest_access;
363
364        format!("{name}.{} {source_str}, {dest_str}", size_str(size))
365    }
366
367    fn add(
368        &mut self,
369        size: OpSize,
370        source: AddressingMode,
371        dest: AddressingMode,
372        with_extend: bool,
373    ) -> String {
374        let name = if with_extend {
375            "addx"
376        } else {
377            match (source, dest) {
378                (AddressingMode::Quick(_), _) => "addq",
379                (_, AddressingMode::AddressDirect(_)) => "adda",
380                (AddressingMode::Immediate, _) => "addi",
381                _ => "add",
382            }
383        };
384
385        self.binary_op(name, size, source, dest)
386    }
387
388    fn abcd(&mut self, source: AddressingMode, dest: AddressingMode) -> String {
389        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::Byte);
390        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Byte);
391
392        self.out.memory_read = source_access;
393        self.out.memory_write = dest_access;
394
395        format!("abcd {source_str}, {dest_str}")
396    }
397
398    fn and(&mut self, size: OpSize, source: AddressingMode, dest: AddressingMode) -> String {
399        let name = match source {
400            AddressingMode::Immediate => "andi",
401            _ => "and",
402        };
403
404        self.binary_op(name, size, source, dest)
405    }
406
407    fn andi_to_ccr(&mut self) -> String {
408        let source_str = self.operand_string(AddressingMode::Immediate, OpSize::Byte);
409        format!("andi {source_str}, ccr")
410    }
411
412    fn andi_to_sr(&mut self) -> String {
413        let source_str = self.operand_string(AddressingMode::Immediate, OpSize::Word);
414        format!("andi {source_str}, sr")
415    }
416
417    fn asl_asr_memory(&mut self, direction: ShiftDirection, dest: AddressingMode) -> String {
418        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Word);
419        self.out.memory_read = dest_access;
420        self.out.memory_write = dest_access;
421        format!("as{} {dest_str}", direction_str(direction))
422    }
423
424    fn asl_asr_register(
425        size: OpSize,
426        direction: ShiftDirection,
427        register: DataRegister,
428        count: ShiftCount,
429    ) -> String {
430        format!(
431            "as{}.{} {}, {}",
432            direction_str(direction),
433            size_str(size),
434            shift_count_str(count),
435            DATA_REGISTERS[register.0 as usize]
436        )
437    }
438
439    fn bit_op(&mut self, name: &str, source: AddressingMode, dest: AddressingMode) -> String {
440        let source_str = match source {
441            AddressingMode::Immediate => {
442                let bit = self.read_word() & 0xFF;
443                format!("#{bit}").into()
444            }
445            _ => self.operand_string(source, OpSize::Byte),
446        };
447        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Byte);
448
449        self.out.memory_read = dest_access;
450        self.out.memory_write = dest_access;
451
452        format!("{name} {source_str}, {dest_str}")
453    }
454
455    fn btst(&mut self, source: AddressingMode, dest: AddressingMode) -> String {
456        self.bit_op("btst", source, dest)
457    }
458
459    fn bchg(&mut self, source: AddressingMode, dest: AddressingMode) -> String {
460        self.bit_op("bchg", source, dest)
461    }
462
463    fn bclr(&mut self, source: AddressingMode, dest: AddressingMode) -> String {
464        self.bit_op("bclr", source, dest)
465    }
466
467    fn bset(&mut self, source: AddressingMode, dest: AddressingMode) -> String {
468        self.bit_op("bset", source, dest)
469    }
470
471    fn branch(&mut self, condition: BranchCondition, displacement: i8) -> String {
472        let pc = self.reader.pc;
473        let effective_displacement = match displacement {
474            0 => self.read_word() as i16,
475            _ => displacement.into(),
476        };
477
478        let name = match condition {
479            BranchCondition::True => "bra".into(),
480            BranchCondition::False => "braf".into(),
481            _ => format!("b{}", condition_suffix(condition)),
482        };
483
484        format!("{name} ${:06X}", pc.wrapping_add_signed(effective_displacement.into()))
485    }
486
487    fn dbcc(&mut self, condition: BranchCondition, register: DataRegister) -> String {
488        let pc = self.reader.pc;
489        let displacement = self.read_word() as i16;
490        format!(
491            "db{} {}, ${:06X}",
492            condition_suffix(condition),
493            DATA_REGISTERS[register.0 as usize],
494            pc.wrapping_add_signed(displacement.into())
495        )
496    }
497
498    fn bsr(&mut self, displacement: i8) -> String {
499        let pc = self.reader.pc;
500        let effective_displacement = match displacement {
501            0 => self.read_word() as i16,
502            _ => displacement.into(),
503        };
504
505        format!("bsr ${:06X}", pc.wrapping_add_signed(effective_displacement.into()))
506    }
507
508    fn chk(&mut self, register: DataRegister, source: AddressingMode) -> String {
509        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::Word);
510        self.out.memory_read = source_access;
511        format!("chk {source_str}, {}", DATA_REGISTERS[register.0 as usize])
512    }
513
514    fn clr(&mut self, size: OpSize, dest: AddressingMode) -> String {
515        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, size);
516        self.out.memory_write = dest_access;
517        format!("clr.{} {dest_str}", size_str(size))
518    }
519
520    fn cmp(&mut self, size: OpSize, source: AddressingMode, dest: AddressingMode) -> String {
521        let name = match (source, dest) {
522            (_, AddressingMode::AddressDirect(_)) => "cmpa",
523            (AddressingMode::Immediate, _) => "cmpi",
524            (
525                AddressingMode::AddressIndirectPostincrement(_),
526                AddressingMode::AddressIndirectPostincrement(_),
527            ) => "cmpm",
528            _ => "cmp",
529        };
530
531        self.binary_op(name, size, source, dest)
532    }
533
534    fn muldiv_op(&mut self, name: &str, dest: DataRegister, source: AddressingMode) -> String {
535        let (source, source_access) = self.resolve_addressing_mode(source, OpSize::Word);
536        self.out.memory_read = source_access;
537        format!("{name} {source}, {}", DATA_REGISTERS[dest.0 as usize])
538    }
539
540    fn divs(&mut self, dest: DataRegister, source: AddressingMode) -> String {
541        self.muldiv_op("divs", dest, source)
542    }
543
544    fn divu(&mut self, dest: DataRegister, source: AddressingMode) -> String {
545        self.muldiv_op("divu", dest, source)
546    }
547
548    fn exg_address(a0: AddressRegister, a1: AddressRegister) -> String {
549        format!("exg {}, {}", ADDRESS_REGISTERS[a0.0 as usize], ADDRESS_REGISTERS[a1.0 as usize])
550    }
551
552    fn exg_data(d0: DataRegister, d1: DataRegister) -> String {
553        format!("exg {}, {}", DATA_REGISTERS[d0.0 as usize], DATA_REGISTERS[d1.0 as usize])
554    }
555
556    fn exg_data_address(d: DataRegister, a: AddressRegister) -> String {
557        format!("exg {}, {}", DATA_REGISTERS[d.0 as usize], ADDRESS_REGISTERS[a.0 as usize])
558    }
559
560    fn eor(&mut self, size: OpSize, source: AddressingMode, dest: AddressingMode) -> String {
561        let name = match source {
562            AddressingMode::Immediate => "eori",
563            _ => "eor",
564        };
565
566        self.binary_op(name, size, source, dest)
567    }
568
569    fn eori_to_ccr(&mut self) -> String {
570        let source_str = self.operand_string(AddressingMode::Immediate, OpSize::Byte);
571        format!("eori {source_str}, ccr")
572    }
573
574    fn eori_to_sr(&mut self) -> String {
575        let source_str = self.operand_string(AddressingMode::Immediate, OpSize::Word);
576        format!("eori {source_str}, sr")
577    }
578
579    fn ext(size: OpSize, dest: DataRegister) -> String {
580        format!("ext.{} {}", size_str(size), DATA_REGISTERS[dest.0 as usize])
581    }
582
583    fn jmp(&mut self, dest: AddressingMode) -> String {
584        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::LongWord);
585        self.out.memory_read = dest_access;
586        self.out.memory_read_type = MemoryReadType::EffectiveAddress;
587        format!("jmp {dest_str}")
588    }
589
590    fn jsr(&mut self, dest: AddressingMode) -> String {
591        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::LongWord);
592        self.out.memory_read = dest_access;
593        self.out.memory_read_type = MemoryReadType::EffectiveAddress;
594        format!("jsr {dest_str}")
595    }
596
597    fn link(&mut self, register: AddressRegister) -> String {
598        let displacement = self.read_word() as i16;
599        format!("link {}, {displacement}", ADDRESS_REGISTERS[register.0 as usize])
600    }
601
602    fn lea(&mut self, source: AddressingMode, dest: AddressRegister) -> String {
603        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::LongWord);
604        self.out.memory_read = source_access;
605        self.out.memory_read_type = MemoryReadType::EffectiveAddress;
606        format!("lea {source_str}, {}", ADDRESS_REGISTERS[dest.0 as usize])
607    }
608
609    fn lsl_lsr_memory(&mut self, direction: ShiftDirection, dest: AddressingMode) -> String {
610        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Word);
611        self.out.memory_read = dest_access;
612        self.out.memory_write = dest_access;
613        format!("ls{} {dest_str}", direction_str(direction))
614    }
615
616    fn lsl_lsr_register(
617        size: OpSize,
618        direction: ShiftDirection,
619        register: DataRegister,
620        count: ShiftCount,
621    ) -> String {
622        format!(
623            "ls{}.{} {}, {}",
624            direction_str(direction),
625            size_str(size),
626            shift_count_str(count),
627            DATA_REGISTERS[register.0 as usize]
628        )
629    }
630
631    fn move_(&mut self, size: OpSize, source: AddressingMode, dest: AddressingMode) -> String {
632        let name = match (source, dest) {
633            (AddressingMode::Quick(_), _) => "moveq",
634            (_, AddressingMode::AddressDirect(_)) => "movea",
635            _ => "move",
636        };
637
638        self.binary_op(name, size, source, dest)
639    }
640
641    fn move_from_sr(&mut self, dest: AddressingMode) -> String {
642        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Word);
643        self.out.memory_write = dest_access;
644        format!("move sr, {dest_str}")
645    }
646
647    fn movem(&mut self, size: OpSize, am: AddressingMode, direction: Direction) -> String {
648        let registers = self.read_word();
649        let registers_str = movem_registers_str(registers, am);
650        let (memory_str, memory_access) = self.resolve_addressing_mode(am, size);
651
652        match direction {
653            Direction::RegisterToMemory => {
654                self.out.memory_write = memory_access;
655                format!("movem.{} {registers_str}, {memory_str}", size_str(size))
656            }
657            Direction::MemoryToRegister => {
658                self.out.memory_read = memory_access;
659                format!("movem.{} {memory_str}, {registers_str}", size_str(size))
660            }
661        }
662    }
663
664    fn movep(
665        &mut self,
666        size: OpSize,
667        data: DataRegister,
668        address: AddressRegister,
669        direction: Direction,
670    ) -> String {
671        let displacement = self.read_word() as i16;
672
673        let memory_access = MemoryAccess::AddressIndirect {
674            register: address.0,
675            displacement: displacement as u32,
676            index: None,
677            size,
678        };
679
680        match direction {
681            Direction::RegisterToMemory => {
682                self.out.memory_write = Some(memory_access);
683                format!(
684                    "movep.{} {}, ({displacement},{})",
685                    size_str(size),
686                    DATA_REGISTERS[data.0 as usize],
687                    ADDRESS_REGISTERS[address.0 as usize]
688                )
689            }
690            Direction::MemoryToRegister => {
691                self.out.memory_read = Some(memory_access);
692                format!(
693                    "movep.{} ({displacement},{}), {}",
694                    size_str(size),
695                    ADDRESS_REGISTERS[address.0 as usize],
696                    DATA_REGISTERS[data.0 as usize]
697                )
698            }
699        }
700    }
701
702    fn moveq(value: i8, register: DataRegister) -> String {
703        format!("moveq #{value}, {}", DATA_REGISTERS[register.0 as usize])
704    }
705
706    fn move_to_ccr(&mut self, source: AddressingMode) -> String {
707        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::Byte);
708        self.out.memory_read = source_access;
709        format!("move {source_str}, ccr")
710    }
711
712    fn move_to_sr(&mut self, source: AddressingMode) -> String {
713        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::Word);
714        self.out.memory_read = source_access;
715        format!("move {source_str}, sr")
716    }
717
718    fn move_usp(direction: UspDirection, register: AddressRegister) -> String {
719        match direction {
720            UspDirection::RegisterToUsp => {
721                format!("move {}, usp", ADDRESS_REGISTERS[register.0 as usize])
722            }
723            UspDirection::UspToRegister => {
724                format!("move usp, {}", ADDRESS_REGISTERS[register.0 as usize])
725            }
726        }
727    }
728
729    fn muls(&mut self, dest: DataRegister, source: AddressingMode) -> String {
730        self.muldiv_op("muls", dest, source)
731    }
732
733    fn mulu(&mut self, dest: DataRegister, source: AddressingMode) -> String {
734        self.muldiv_op("mulu", dest, source)
735    }
736
737    fn neg(&mut self, size: OpSize, dest: AddressingMode, with_extend: bool) -> String {
738        let name = if with_extend { "negx" } else { "neg" };
739        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, size);
740        self.out.memory_read = dest_access;
741        self.out.memory_write = dest_access;
742        format!("{name}.{} {dest_str}", size_str(size))
743    }
744
745    fn nbcd(&mut self, dest: AddressingMode) -> String {
746        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Byte);
747        self.out.memory_read = dest_access;
748        self.out.memory_write = dest_access;
749        format!("nbcd {dest_str}")
750    }
751
752    fn not(&mut self, size: OpSize, dest: AddressingMode) -> String {
753        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, size);
754        self.out.memory_read = dest_access;
755        self.out.memory_write = dest_access;
756        format!("not.{} {dest_str}", size_str(size))
757    }
758
759    fn or(&mut self, size: OpSize, source: AddressingMode, dest: AddressingMode) -> String {
760        let name = match source {
761            AddressingMode::Immediate => "ori",
762            _ => "or",
763        };
764
765        self.binary_op(name, size, source, dest)
766    }
767
768    fn ori_to_ccr(&mut self) -> String {
769        let source_str = self.operand_string(AddressingMode::Immediate, OpSize::Byte);
770        format!("ori {source_str}, ccr")
771    }
772
773    fn ori_to_sr(&mut self) -> String {
774        let source_str = self.operand_string(AddressingMode::Immediate, OpSize::Word);
775        format!("ori {source_str}, sr")
776    }
777
778    fn pea(&mut self, source: AddressingMode) -> String {
779        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::LongWord);
780        self.out.memory_read = source_access;
781        self.out.memory_read_type = MemoryReadType::EffectiveAddress;
782        format!("pea {source_str}")
783    }
784
785    fn rol_ror_memory(&mut self, direction: ShiftDirection, dest: AddressingMode) -> String {
786        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Word);
787        self.out.memory_read = dest_access;
788        self.out.memory_write = dest_access;
789        format!("ro{} {dest_str}", direction_str(direction))
790    }
791
792    fn rol_ror_register(
793        size: OpSize,
794        direction: ShiftDirection,
795        register: DataRegister,
796        count: ShiftCount,
797    ) -> String {
798        format!(
799            "ro{}.{} {}, {}",
800            direction_str(direction),
801            size_str(size),
802            shift_count_str(count),
803            DATA_REGISTERS[register.0 as usize]
804        )
805    }
806
807    fn roxl_roxr_memory(&mut self, direction: ShiftDirection, dest: AddressingMode) -> String {
808        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Word);
809        self.out.memory_read = dest_access;
810        self.out.memory_write = dest_access;
811        format!("rox{} {dest_str}", direction_str(direction))
812    }
813
814    fn roxl_roxr_register(
815        size: OpSize,
816        direction: ShiftDirection,
817        register: DataRegister,
818        count: ShiftCount,
819    ) -> String {
820        format!(
821            "rox{}.{} {}, {}",
822            direction_str(direction),
823            size_str(size),
824            shift_count_str(count),
825            DATA_REGISTERS[register.0 as usize]
826        )
827    }
828
829    fn scc(&mut self, condition: BranchCondition, dest: AddressingMode) -> String {
830        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Byte);
831        self.out.memory_write = dest_access;
832        format!("s{} {dest_str}", condition_suffix(condition))
833    }
834
835    fn sub(
836        &mut self,
837        size: OpSize,
838        source: AddressingMode,
839        dest: AddressingMode,
840        with_extend: bool,
841    ) -> String {
842        let name = if with_extend {
843            "subx"
844        } else {
845            match (source, dest) {
846                (AddressingMode::Quick(_), _) => "subq",
847                (_, AddressingMode::AddressDirect(_)) => "suba",
848                (AddressingMode::Immediate, _) => "subi",
849                _ => "sub",
850            }
851        };
852
853        self.binary_op(name, size, source, dest)
854    }
855
856    fn sbcd(&mut self, source: AddressingMode, dest: AddressingMode) -> String {
857        let (source_str, source_access) = self.resolve_addressing_mode(source, OpSize::Byte);
858        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Byte);
859
860        self.out.memory_read = source_access;
861        self.out.memory_write = dest_access;
862
863        format!("sbcd {source_str}, {dest_str}")
864    }
865
866    fn swap(register: DataRegister) -> String {
867        format!("swap {}", DATA_REGISTERS[register.0 as usize])
868    }
869
870    fn tst(&mut self, size: OpSize, source: AddressingMode) -> String {
871        let (source_str, source_access) = self.resolve_addressing_mode(source, size);
872        self.out.memory_read = source_access;
873        format!("tst.{} {source_str}", size_str(size))
874    }
875
876    fn tas(&mut self, dest: AddressingMode) -> String {
877        let (dest_str, dest_access) = self.resolve_addressing_mode(dest, OpSize::Byte);
878        self.out.memory_read = dest_access;
879        self.out.memory_write = dest_access;
880        format!("tas {dest_str}")
881    }
882
883    fn trap(vector: u32) -> String {
884        format!("trap #{vector}")
885    }
886
887    fn unlk(register: AddressRegister) -> String {
888        format!("unlk {}", ADDRESS_REGISTERS[register.0 as usize])
889    }
890
891    fn resolve_addressing_mode(
892        &mut self,
893        am: AddressingMode,
894        size: OpSize,
895    ) -> (Cow<'static, str>, Option<MemoryAccess>) {
896        fn index_register_str(index: IndexRegister) -> &'static str {
897            match index {
898                IndexRegister::Data(r) => DATA_REGISTERS[r.0 as usize],
899                IndexRegister::Address(r) => ADDRESS_REGISTERS[r.0 as usize],
900            }
901        }
902
903        fn index_size_str(size: IndexSize) -> &'static str {
904            match size {
905                IndexSize::SignExtendedWord => "w",
906                IndexSize::LongWord => "l",
907            }
908        }
909
910        match am {
911            AddressingMode::DataDirect(DataRegister(r)) => {
912                (DATA_REGISTERS[r as usize].into(), None)
913            }
914            AddressingMode::AddressDirect(AddressRegister(r)) => {
915                (ADDRESS_REGISTERS[r as usize].into(), None)
916            }
917            AddressingMode::AddressIndirect(AddressRegister(r)) => {
918                let s = ADDRESS_INDIRECT[r as usize];
919                let access = MemoryAccess::AddressIndirect {
920                    register: r,
921                    displacement: 0,
922                    index: None,
923                    size,
924                };
925                (s.into(), Some(access))
926            }
927            AddressingMode::AddressIndirectPostincrement(AddressRegister(r)) => {
928                let s = ADDRESS_POSTINC[r as usize];
929                let access = MemoryAccess::AddressIndirect {
930                    register: r,
931                    displacement: 0,
932                    index: None,
933                    size,
934                };
935                (s.into(), Some(access))
936            }
937            AddressingMode::AddressIndirectPredecrement(AddressRegister(r)) => {
938                let s = ADDRESS_PREDEC[r as usize];
939                let access = MemoryAccess::AddressIndirectPredecrement { register: r, size };
940                (s.into(), Some(access))
941            }
942            AddressingMode::AddressIndirectDisplacement(AddressRegister(r)) => {
943                let displacement = self.read_word() as i16;
944                let s = format!("({displacement},a{r})");
945                let access = MemoryAccess::AddressIndirect {
946                    register: r,
947                    displacement: displacement as u32,
948                    index: None,
949                    size,
950                };
951                (s.into(), Some(access))
952            }
953            AddressingMode::AddressIndirectIndexed(AddressRegister(r)) => {
954                let extension = self.read_word();
955                let (index_register, index_size) = crate::core::parse_index(extension);
956                let displacement = extension as i8;
957
958                let s = format!(
959                    "({displacement},a{r},{}.{})",
960                    index_register_str(index_register),
961                    index_size_str(index_size)
962                );
963                let access = MemoryAccess::AddressIndirect {
964                    register: r,
965                    displacement: displacement as u32,
966                    index: Some((index_register.into(), index_size)),
967                    size,
968                };
969                (s.into(), Some(access))
970            }
971            AddressingMode::PcRelativeDisplacement => {
972                let pc = self.reader.pc;
973                let displacement = self.read_word() as i16;
974
975                let s = format!("({displacement},pc)");
976                let access = MemoryAccess::PcRelative {
977                    pc,
978                    displacement: displacement as u32,
979                    index: None,
980                    size,
981                };
982                (s.into(), Some(access))
983            }
984            AddressingMode::PcRelativeIndexed => {
985                let pc = self.reader.pc;
986                let extension = self.read_word();
987                let (index_register, index_size) = crate::core::parse_index(extension);
988                let displacement = extension as i8;
989
990                let s = format!(
991                    "({displacement},pc,{}.{})",
992                    index_register_str(index_register),
993                    index_size_str(index_size)
994                );
995                let access = MemoryAccess::PcRelative {
996                    pc,
997                    displacement: displacement as u32,
998                    index: Some((index_register.into(), index_size)),
999                    size,
1000                };
1001                (s.into(), Some(access))
1002            }
1003            AddressingMode::AbsoluteShort => {
1004                let address = (self.read_word() as i16 as u32) & 0xFFFFFF;
1005
1006                let s = format!("(0x{address:06X})");
1007                let access = MemoryAccess::Absolute { address, size };
1008                (s.into(), Some(access))
1009            }
1010            AddressingMode::AbsoluteLong => {
1011                let high: u32 = self.read_word().into();
1012                let low: u32 = self.read_word().into();
1013                let address = (low | (high << 16)) & 0xFFFFFF;
1014
1015                let s = format!("(0x{address:06X})");
1016                let access = MemoryAccess::Absolute { address, size };
1017                (s.into(), Some(access))
1018            }
1019            AddressingMode::Immediate => {
1020                let extension = self.read_word();
1021                let s = match size {
1022                    OpSize::Byte => format!("#0x{:02X}", extension & 0xFF),
1023                    OpSize::Word => format!("#0x{extension:04X}"),
1024                    OpSize::LongWord => {
1025                        let low = self.read_word();
1026                        let immediate = u32::from(low) | (u32::from(extension) << 16);
1027                        format!("#0x{immediate:08X}")
1028                    }
1029                };
1030                (s.into(), None)
1031            }
1032            AddressingMode::Quick(value) => (format!("#{value}").into(), None),
1033        }
1034    }
1035
1036    fn operand_string(&mut self, am: AddressingMode, size: OpSize) -> Cow<'static, str> {
1037        self.resolve_addressing_mode(am, size).0
1038    }
1039}
1040
1041fn movem_registers_str(extension: u16, am: AddressingMode) -> String {
1042    let reverse = matches!(am, AddressingMode::AddressIndirectPredecrement(_));
1043
1044    let data_fn = |i: u8| if reverse { extension.bit(15 - i) } else { extension.bit(i) };
1045    let address_fn = |i: u8| if reverse { extension.bit(7 - i) } else { extension.bit(8 + i) };
1046
1047    let mut out = String::new();
1048    movem_str_half(data_fn, DATA_REGISTERS, &mut out);
1049    movem_str_half(address_fn, ADDRESS_REGISTERS, &mut out);
1050
1051    if out.is_empty() {
1052        // Is this even a valid MOVEM instruction?
1053        return "empty".into();
1054    }
1055
1056    out
1057}
1058
1059fn movem_str_half(register_fn: impl Fn(u8) -> bool, register_strs: &[&str; 8], out: &mut String) {
1060    let mut last_set: Option<u8> = None;
1061    let mut i = 0;
1062    loop {
1063        if i < 8 && register_fn(i) {
1064            if last_set.is_none() {
1065                last_set = Some(i);
1066            }
1067        } else if let Some(last_set) = last_set.take() {
1068            if !out.is_empty() {
1069                out.push('/');
1070            }
1071
1072            out.push_str(register_strs[last_set as usize]);
1073            if last_set != i - 1 {
1074                out.push('-');
1075                out.push_str(register_strs[(i - 1) as usize]);
1076            }
1077        }
1078
1079        if i == 8 {
1080            break;
1081        }
1082
1083        i += 1;
1084    }
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089    use super::*;
1090    use crate::core::{AddressRegister, DataRegister};
1091
1092    #[test]
1093    fn movem() {
1094        assert_eq!(
1095            movem_registers_str(0x0707, AddressingMode::DataDirect(DataRegister(0))),
1096            "d0-d2/a0-a2"
1097        );
1098        assert_eq!(
1099            movem_registers_str(
1100                0x0707,
1101                AddressingMode::AddressIndirectPredecrement(AddressRegister(0))
1102            ),
1103            "d5-d7/a5-a7"
1104        );
1105        assert_eq!(
1106            movem_registers_str(0xAA55, AddressingMode::DataDirect(DataRegister(0))),
1107            "d0/d2/d4/d6/a1/a3/a5/a7"
1108        );
1109        assert_eq!(
1110            movem_registers_str(0xFFFF, AddressingMode::DataDirect(DataRegister(0))),
1111            "d0-d7/a0-a7"
1112        );
1113    }
1114}