// rasm-core/src/disasm/decode.rs
//
//! Противоположность `codegen/encode.rs` — декодинг машинного кода в AST.
use crate::disasm::err::{DisasmError, DisasmErrorKind};
use crate::isa::{GPRs, Instruction, MemoryAddress, MnemonicKind, Operand, Registers, Scale};

// =========================================================================
// Вспомогательные структуры
// =========================================================================

#[derive(Debug, Clone, Copy, Default)]
struct Rex {
    w: bool,
    r: bool,
    x: bool,
    b: bool,
}

#[derive(Debug, Clone, Copy)]
struct ModRM {
    mod_: u8,
    reg: u8,
    rm: u8,
}

#[derive(Debug, Clone, Copy)]
struct Sib {
    scale: u8,
    index: u8,
    base: u8,
}

// =========================================================================
// Чтение байтов
// =========================================================================

fn read_u8(bytes: &[u8], offset: &mut usize) -> Result<u8, DisasmError> {
    if *offset >= bytes.len() {
        return Err(DisasmError::new(DisasmErrorKind::TruncatedInstruction));
    }
    let val = bytes[*offset];
    *offset += 1;
    Ok(val)
}

fn read_i32(bytes: &[u8], offset: &mut usize) -> Result<i32, DisasmError> {
    if *offset + 4 > bytes.len() {
        return Err(DisasmError::new(DisasmErrorKind::TruncatedInstruction));
    }
    let val = i32::from_le_bytes([
        bytes[*offset],
        bytes[*offset + 1],
        bytes[*offset + 2],
        bytes[*offset + 3],
    ]);
    *offset += 4;
    Ok(val)
}

fn read_i64(bytes: &[u8], offset: &mut usize) -> Result<i64, DisasmError> {
    if *offset + 8 > bytes.len() {
        return Err(DisasmError::new(DisasmErrorKind::TruncatedInstruction));
    }
    let val = i64::from_le_bytes([
        bytes[*offset],
        bytes[*offset + 1],
        bytes[*offset + 2],
        bytes[*offset + 3],
        bytes[*offset + 4],
        bytes[*offset + 5],
        bytes[*offset + 6],
        bytes[*offset + 7],
    ]);
    *offset += 8;
    Ok(val)
}

fn read_i8(bytes: &[u8], offset: &mut usize) -> Result<i8, DisasmError> {
    Ok(read_u8(bytes, offset)? as i8)
}

// =========================================================================
// Парсинг REX-префикса
// =========================================================================

/// Пытается прочитать REX-префикс. Если первый байт не 0x40-0x4F, offset не двигается.
fn try_parse_rex_prefix(bytes: &[u8], offset: &mut usize) -> Rex {
    if *offset >= bytes.len() {
        return Rex::default();
    }
    let byte = bytes[*offset];
    if byte & 0xF0 == 0x40 {
        *offset += 1;
        Rex {
            w: (byte >> 3) & 1 != 0,
            r: (byte >> 2) & 1 != 0,
            x: (byte >> 1) & 1 != 0,
            b: byte & 1 != 0,
        }
    } else {
        Rex::default()
    }
}

// =========================================================================
// Парсинг ModRM и Sib
// =========================================================================

fn parse_modrm(byte: u8) -> ModRM {
    ModRM {
        mod_: (byte >> 6) & 0x3,
        reg: (byte >> 3) & 0x7,
        rm: byte & 0x7,
    }
}

fn parse_sib(byte: u8) -> Sib {
    Sib {
        scale: (byte >> 6) & 0x3,
        index: (byte >> 3) & 0x7,
        base: byte & 0x7,
    }
}

// =========================================================================
// Преобразование кодов регистров
// =========================================================================

fn gpr_from_code(low3: u8, rex_b: bool) -> GPRs {
    let code = low3 | ((rex_b as u8) << 3);
    match code {
        0 => GPRs::Rax,
        1 => GPRs::Rcx,
        2 => GPRs::Rdx,
        3 => GPRs::Rbx,
        4 => GPRs::Rsp,
        5 => GPRs::Rbp,
        6 => GPRs::Rsi,
        7 => GPRs::Rdi,
        8 => GPRs::R8,
        9 => GPRs::R9,
        10 => GPRs::R10,
        11 => GPRs::R11,
        12 => GPRs::R12,
        13 => GPRs::R13,
        14 => GPRs::R14,
        15 => GPRs::R15,
        _ => unreachable!(),
    }
}

fn reg_from_code(low3: u8, rex_b: bool) -> Registers {
    Registers::GPR(gpr_from_code(low3, rex_b))
}

// =========================================================================
// Декодирование ModRM + Sib + displacement в MemoryAddress
// =========================================================================

fn decode_memory(
    modrm: ModRM,
    rex: Rex,
    bytes: &[u8],
    offset: &mut usize,
) -> Result<MemoryAddress, DisasmError> {
    let need_sib = modrm.rm == 4;

    let (base, index, scale_val, has_disp32) = if need_sib {
        let sib_byte = read_u8(bytes, offset)?;
        let sib = parse_sib(sib_byte);

        let scale = match sib.scale {
            0 => Scale::One,
            1 => Scale::Two,
            2 => Scale::Four,
            3 => Scale::Eight,
            _ => unreachable!(),
        };

        let idx = if sib.index != 4 {
            Some(gpr_from_code(sib.index, rex.x))
        } else {
            None
        };

        let (b, disp32) = if modrm.mod_ == 0 && sib.base == 5 {
            (None, true)
        } else {
            (Some(reg_from_code(sib.base, rex.b)), false)
        };

        (b, idx, idx.map(|_| scale), disp32)
    } else {
        if modrm.mod_ == 0 && modrm.rm == 5 {
            (None, None, None, true)
        } else {
            (Some(reg_from_code(modrm.rm, rex.b)), None, None, false)
        }
    };

    let displacement = match modrm.mod_ {
        0b01 => read_i8(bytes, offset)? as i32,
        0b10 => read_i32(bytes, offset)?,
        0b00 if has_disp32 => read_i32(bytes, offset)?,
        _ => 0,
    };

    Ok(MemoryAddress {
        base,
        index,
        scale: scale_val,
        displacement,
        label: None,
    })
}

// =========================================================================
// Главный декодер инструкции
// =========================================================================

#[derive(Debug)]
pub struct DecodedInstruction {
    pub offset: usize,
    pub bytes_len: usize,
    pub instruction: Instruction,
}

pub fn decode_instruction(
    bytes: &[u8],
    offset: &mut usize,
) -> Result<DecodedInstruction, DisasmError> {
    let start = *offset;

    // Парсим REX-префикс (первый байт 0x40-0x4F)
    let _rex = try_parse_rex_prefix(bytes, offset);

    // Читаем первичный опкод
    let opcode = read_u8(bytes, offset)?;

    match decode_opcode(bytes, offset, opcode, _rex) {
        Ok((mnemonic, operands)) => {
            let end = *offset;
            Ok(DecodedInstruction {
                offset: start,
                bytes_len: end - start,
                instruction: Instruction { mnemonic, operands },
            })
        }
        Err(e) if matches!(e.kind, DisasmErrorKind::UnknownOpcode(..)) => {
            // Неизвестный опкод — не падаем, а выводим как .byte 0xXX
            *offset = start + 1;
            Ok(DecodedInstruction {
                offset: start,
                bytes_len: 1,
                instruction: Instruction {
                    mnemonic: MnemonicKind::Db,
                    operands: vec![Operand::Immediate(bytes[start] as i64)],
                },
            })
        }
        Err(e) => Err(e),
    }
}

// =========================================================================
// Таблица декодирования опкодов
// =========================================================================

fn decode_opcode(
    bytes: &[u8],
    offset: &mut usize,
    opcode: u8,
    rex: Rex,
) -> Result<(MnemonicKind, Vec<Operand>), DisasmError> {
    match opcode {
        // === 1-байтовые инструкции без операндов ===
        0x90 => Ok((MnemonicKind::Nop, vec![])),
        0xC3 => Ok((MnemonicKind::Ret, vec![])),
        0x99 if rex.w => {
            // CQO = REX.W + 0x99
            Ok((MnemonicKind::Cqo, vec![]))
        }

        // === PUSH r64: 0x50-0x57 ===
        0x50..=0x57 => {
            let reg = reg_from_code(opcode - 0x50, rex.b);
            Ok((MnemonicKind::Push, vec![Operand::Register(reg)]))
        }

        // === POP r64: 0x58-0x5F ===
        0x58..=0x5F => {
            let reg = reg_from_code(opcode - 0x58, rex.b);
            Ok((MnemonicKind::Pop, vec![Operand::Register(reg)]))
        }

        // === MOV r64, imm64: 0xB8-0xBF ===
        0xB8..=0xBF => {
            let reg = reg_from_code(opcode - 0xB8, rex.b);
            let imm = read_i64(bytes, offset)?;
            Ok((
                MnemonicKind::Mov,
                vec![Operand::Register(reg), Operand::Immediate(imm)],
            ))
        }

        // === PUSH imm32: 0x68 ===
        0x68 => {
            let imm = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Push, vec![Operand::Immediate(imm as i64)]))
        }

        // === MOV r/m, r: 0x89 ===
        // Варианты из encode.rs:
        //   - 0x89 + REX.W + ModRM(11, src3, dst3):   MOV r64, r64
        //   - 0x89 + REX.W + ModRM(!11, src3, ...):    MOV [mem], r64
        0x89 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            if modrm.mod_ == 0b11 {
                // MOV r64, r64
                let dst = reg_from_code(modrm.rm, rex.b);
                let src = reg_from_code(modrm.reg, rex.r);
                Ok((
                    MnemonicKind::Mov,
                    vec![Operand::Register(dst), Operand::Register(src)],
                ))
            } else {
                // MOV [mem], r64
                let src = reg_from_code(modrm.reg, rex.r);
                let mem = decode_memory(modrm, rex, bytes, offset)?;
                Ok((
                    MnemonicKind::Mov,
                    vec![Operand::Memory(mem), Operand::Register(src)],
                ))
            }
        }

        // === MOV r, r/m: 0x8B ===
        0x8B => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let dst = reg_from_code(modrm.reg, rex.r);
            if modrm.mod_ == 0b11 {
                // MOV r64, r64
                let src = reg_from_code(modrm.rm, rex.b);
                Ok((
                    MnemonicKind::Mov,
                    vec![Operand::Register(dst), Operand::Register(src)],
                ))
            } else {
                // MOV r64, [mem]
                let mem = decode_memory(modrm, rex, bytes, offset)?;
                Ok((
                    MnemonicKind::Mov,
                    vec![Operand::Register(dst), Operand::Memory(mem)],
                ))
            }
        }

        // === MOV [mem], imm32 / MOV r64, imm32: 0xC7; /0 в ModRM.reg ===
        0xC7 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            if modrm.reg != 0 {
                return Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                    0xC7, *offset,
                )));
            }
            if modrm.mod_ == 0b11 {
                // MOV r64, imm32 (sign-extended)
                let dst = reg_from_code(modrm.rm, rex.b);
                let imm = read_i32(bytes, offset)?;
                Ok((
                    MnemonicKind::Mov,
                    vec![Operand::Register(dst), Operand::Immediate(imm as i64)],
                ))
            } else {
                // MOV [mem], imm32
                let mem = decode_memory(modrm, rex, bytes, offset)?;
                let imm = read_i32(bytes, offset)?;
                Ok((
                    MnemonicKind::Mov,
                    vec![Operand::Memory(mem), Operand::Immediate(imm as i64)],
                ))
            }
        }

        // === LEA r, m: 0x8D ===
        0x8D => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let dst = reg_from_code(modrm.reg, rex.r);
            if modrm.mod_ == 0b11 {
                return Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                    0x8D, *offset,
                )));
            }
            let mem = decode_memory(modrm, rex, bytes, offset)?;
            Ok((
                MnemonicKind::Lea,
                vec![Operand::Register(dst), Operand::Memory(mem)],
            ))
        }

        // === IMUL r64, r/m64, imm32: 0x69 ===
        0x69 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let dst = reg_from_code(modrm.reg, rex.r);
            let src = if modrm.mod_ == 0b11 {
                Operand::Register(reg_from_code(modrm.rm, rex.b))
            } else {
                Operand::Memory(decode_memory(modrm, rex, bytes, offset)?)
            };
            let imm = read_i32(bytes, offset)?;
            Ok((
                MnemonicKind::Imul,
                vec![Operand::Register(dst), src, Operand::Immediate(imm as i64)],
            ))
        }

        // === JMP rel32: 0xE9 ===
        0xE9 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jmp, vec![Operand::Immediate(rel as i64)]))
        }

        // === JMP rel8: 0xEB ===
        0xEB => {
            let rel = read_i8(bytes, offset)?;
            Ok((MnemonicKind::Jmp, vec![Operand::Immediate(rel as i64)]))
        }

        // === Jcc rel8: 0x70-0x7F ===
        0x70..=0x7F => {
            let rel = read_i8(bytes, offset)?;
            let mnemonic = match opcode {
                0x70 => MnemonicKind::Jo,
                0x71 => MnemonicKind::Jno,
                0x72 => MnemonicKind::Jb,
                0x73 => MnemonicKind::Jae,
                0x74 => MnemonicKind::Je,
                0x75 => MnemonicKind::Jne,
                0x76 => MnemonicKind::Jbe,
                0x77 => MnemonicKind::Ja,
                0x78 => MnemonicKind::Js,
                0x79 => MnemonicKind::Jns,
                0x7A => MnemonicKind::Jp,
                0x7B => MnemonicKind::Jnp,
                0x7C => MnemonicKind::Jl,
                0x7D => MnemonicKind::Jge,
                0x7E => MnemonicKind::Jle,
                0x7F => MnemonicKind::Jg,
                _ => unreachable!(),
            };
            Ok((mnemonic, vec![Operand::Immediate(rel as i64)]))
        }

        // === CALL rel32: 0xE8 ===
        0xE8 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Call, vec![Operand::Immediate(rel as i64)]))
        }

        // === ADD r/m, r: 0x01 / ADD r, r/m: 0x03 ===
        0x01 | 0x03 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, opcode == 0x01)?;
            Ok((MnemonicKind::Add, operands))
        }

        // === SUB r/m, r: 0x29 / SUB r, r/m: 0x2B ===
        0x29 | 0x2B => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, opcode == 0x29)?;
            Ok((MnemonicKind::Sub, operands))
        }

        // === AND r/m, r: 0x21 / AND r, r/m: 0x23 ===
        0x21 | 0x23 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, opcode == 0x21)?;
            Ok((MnemonicKind::And, operands))
        }

        // === OR r/m, r: 0x09 / OR r, r/m: 0x0B ===
        0x09 | 0x0B => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, opcode == 0x09)?;
            Ok((MnemonicKind::Or, operands))
        }

        // === XOR r/m, r: 0x31 / XOR r, r/m: 0x33 ===
        0x31 | 0x33 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, opcode == 0x31)?;
            Ok((MnemonicKind::Xor, operands))
        }

        // === CMP r/m, r: 0x39 / CMP r, r/m: 0x3B ===
        0x39 | 0x3B => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, opcode == 0x39)?;
            Ok((MnemonicKind::Cmp, operands))
        }

        // === TEST r/m, r: 0x85 ===
        0x85 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let operands = decode_alu_modrm(modrm, rex, bytes, offset, true)?;
            Ok((MnemonicKind::Test, operands))
        }

        // === ModRM-зависимые: 0x81 (imm32), 0x83 (imm8) ===
        // /digit: 0=ADD, 1=OR, 4=AND, 5=SUB, 6=XOR, 7=CMP
        0x81 | 0x83 => {
            let is_imm8 = opcode == 0x83;
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let mnemonic = match modrm.reg {
                0 => MnemonicKind::Add,
                1 => MnemonicKind::Or,
                4 => MnemonicKind::And,
                5 => MnemonicKind::Sub,
                6 => MnemonicKind::Xor,
                7 => MnemonicKind::Cmp,
                _ => {
                    return Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                        opcode, *offset,
                    )));
                }
            };

            let (op1, op2) = if modrm.mod_ == 0b11 {
                let dst = reg_from_code(modrm.rm, rex.b);
                let imm = if is_imm8 {
                    read_i8(bytes, offset)? as i64
                } else {
                    read_i32(bytes, offset)? as i64
                };
                (Operand::Register(dst), Operand::Immediate(imm))
            } else {
                let mem = decode_memory(modrm, rex, bytes, offset)?;
                let imm = if is_imm8 {
                    read_i8(bytes, offset)? as i64
                } else {
                    read_i32(bytes, offset)? as i64
                };
                (Operand::Memory(mem), Operand::Immediate(imm))
            };
            Ok((mnemonic, vec![op1, op2]))
        }

        // === ModRM-зависимые: 0xFF ===
        // /digit: 0=INC, 1=DEC, 2=CALL r64, 4=JMP r64, 6=PUSH r/m
        0xFF => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            match modrm.reg {
                0 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Inc, op))
                }
                1 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Dec, op))
                }
                2 => {
                    if modrm.mod_ == 0b11 {
                        let reg = reg_from_code(modrm.rm, rex.b);
                        Ok((MnemonicKind::Call, vec![Operand::Register(reg)]))
                    } else {
                        // CALL [mem] — не поддерживается в encode.rs
                        Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                            0xFF, *offset,
                        )))
                    }
                }
                4 => {
                    if modrm.mod_ == 0b11 {
                        let reg = reg_from_code(modrm.rm, rex.b);
                        Ok((MnemonicKind::Jmp, vec![Operand::Register(reg)]))
                    } else {
                        Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                            0xFF, *offset,
                        )))
                    }
                }
                6 => {
                    if modrm.mod_ == 0b11 {
                        let reg = reg_from_code(modrm.rm, rex.b);
                        Ok((MnemonicKind::Push, vec![Operand::Register(reg)]))
                    } else {
                        // PUSH [mem] — не в encode.rs
                        Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                            0xFF, *offset,
                        )))
                    }
                }
                _ => Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                    0xFF, *offset,
                ))),
            }
        }

        // === ModRM-зависимые: 0xF7 ===
        // /digit: 0=TEST r, imm32; 2=NOT; 3=NEG; 4=MUL; 5=IMUL; 6=DIV; 7=IDIV
        0xF7 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            match modrm.reg {
                0 => {
                    // TEST r/m64, imm32
                    let op = if modrm.mod_ == 0b11 {
                        Operand::Register(reg_from_code(modrm.rm, rex.b))
                    } else {
                        Operand::Memory(decode_memory(modrm, rex, bytes, offset)?)
                    };
                    let imm = read_i32(bytes, offset)?;
                    Ok((MnemonicKind::Test, vec![op, Operand::Immediate(imm as i64)]))
                }
                2 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Not, op))
                }
                3 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Neg, op))
                }
                4 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Mul, op))
                }
                5 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Imul, op))
                }
                6 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Div, op))
                }
                7 => {
                    let op = decode_modrm_op(modrm, rex, bytes, offset)?;
                    Ok((MnemonicKind::Idiv, op))
                }
                _ => Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                    0xF7, *offset,
                ))),
            }
        }

        // === Сдвиги: 0xD1 (shift by 1), 0xC1 (shift by imm8), 0xD3 (shift by cl) ===
        // /digit: 4=SHL, 5=SHR, 7=SAR
        0xD1 | 0xC1 | 0xD3 => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let mnemonic = match modrm.reg {
                4 => MnemonicKind::Shl,
                5 => MnemonicKind::Shr,
                7 => MnemonicKind::Sar,
                _ => {
                    return Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                        opcode, *offset,
                    )));
                }
            };

            let dst = if modrm.mod_ == 0b11 {
                Operand::Register(reg_from_code(modrm.rm, rex.b))
            } else {
                return Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
                    opcode, *offset,
                )));
            };

            let src = match opcode {
                0xD1 => Operand::Immediate(1),
                0xC1 => Operand::Immediate(read_u8(bytes, offset)? as i64),
                0xD3 => Operand::Register(Registers::GPR(GPRs::Rcx)),
                _ => unreachable!(),
            };
            Ok((mnemonic, vec![dst, src]))
        }

        // === Двухбайтовые опкоды: 0x0F ===
        0x0F => {
            let opcode2 = read_u8(bytes, offset)?;
            decode_two_byte(bytes, offset, opcode2, rex)
        }

        _ => Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
            opcode, *offset,
        ))),
    }
}

// =========================================================================
// Декодирование двухбайтовых опкодов (0F xx)
// =========================================================================

fn decode_two_byte(
    bytes: &[u8],
    offset: &mut usize,
    opcode2: u8,
    rex: Rex,
) -> Result<(MnemonicKind, Vec<Operand>), DisasmError> {
    match opcode2 {
        // SYSCALL: 0F 05
        0x05 => Ok((MnemonicKind::Syscall, vec![])),

        // IMUL r64, r/m64: 0F AF /r
        0xAF => {
            let modrm_byte = read_u8(bytes, offset)?;
            let modrm = parse_modrm(modrm_byte);
            let dst = reg_from_code(modrm.reg, rex.r);
            if modrm.mod_ == 0b11 {
                let src = reg_from_code(modrm.rm, rex.b);
                Ok((
                    MnemonicKind::Imul,
                    vec![Operand::Register(dst), Operand::Register(src)],
                ))
            } else {
                let mem = decode_memory(modrm, rex, bytes, offset)?;
                Ok((
                    MnemonicKind::Imul,
                    vec![Operand::Register(dst), Operand::Memory(mem)],
                ))
            }
        }

        // === Условные прыжки: 0F 8x (x = 0..F) ===
        0x80 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jo, vec![Operand::Immediate(rel as i64)]))
        }
        0x81 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jno, vec![Operand::Immediate(rel as i64)]))
        }
        0x82 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jb, vec![Operand::Immediate(rel as i64)]))
        }
        0x83 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jae, vec![Operand::Immediate(rel as i64)]))
        }
        0x84 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Je, vec![Operand::Immediate(rel as i64)]))
        }
        0x85 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jne, vec![Operand::Immediate(rel as i64)]))
        }
        0x86 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jbe, vec![Operand::Immediate(rel as i64)]))
        }
        0x87 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Ja, vec![Operand::Immediate(rel as i64)]))
        }
        0x88 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Js, vec![Operand::Immediate(rel as i64)]))
        }
        0x89 => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jns, vec![Operand::Immediate(rel as i64)]))
        }
        0x8A => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jp, vec![Operand::Immediate(rel as i64)]))
        }
        0x8B => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jnp, vec![Operand::Immediate(rel as i64)]))
        }
        0x8C => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jl, vec![Operand::Immediate(rel as i64)]))
        }
        0x8D => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jge, vec![Operand::Immediate(rel as i64)]))
        }
        0x8E => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jle, vec![Operand::Immediate(rel as i64)]))
        }
        0x8F => {
            let rel = read_i32(bytes, offset)?;
            Ok((MnemonicKind::Jg, vec![Operand::Immediate(rel as i64)]))
        }

        _ => Err(DisasmError::new(DisasmErrorKind::UnknownOpcode(
            0x0F, *offset,
        ))),
    }
}

// =========================================================================
// Вспомогательные функции
// =========================================================================

/// Декодирует операнд ModRM (регистр или память) для унарных операций.
fn decode_modrm_op(
    modrm: ModRM,
    rex: Rex,
    bytes: &[u8],
    offset: &mut usize,
) -> Result<Vec<Operand>, DisasmError> {
    let op = if modrm.mod_ == 0b11 {
        Operand::Register(reg_from_code(modrm.rm, rex.b))
    } else {
        Operand::Memory(decode_memory(modrm, rex, bytes, offset)?)
    };
    Ok(vec![op])
}

/// Декодирует ModRM для ALU-операций вида r/m, reg.
/// `rm_is_dst` — true для opcode "r/m, reg" (напр. 0x01),
/// false для "reg, r/m" (напр. 0x03).
fn decode_alu_modrm(
    modrm: ModRM,
    rex: Rex,
    bytes: &[u8],
    offset: &mut usize,
    rm_is_dst: bool,
) -> Result<Vec<Operand>, DisasmError> {
    let reg_op = Operand::Register(reg_from_code(modrm.reg, rex.r));
    let other = if modrm.mod_ == 0b11 {
        Operand::Register(reg_from_code(modrm.rm, rex.b))
    } else {
        Operand::Memory(decode_memory(modrm, rex, bytes, offset)?)
    };

    if rm_is_dst {
        Ok(vec![other, reg_op])
    } else {
        Ok(vec![reg_op, other])
    }
}
