use crate::formatter::enums::{FormatterFlowControl, PrefixKind};
use crate::formatter::enums_shared::FormatterTextKind;
use crate::formatter::fmt_utils_all::{show_rep_or_repe_prefix_bool, show_repne_prefix_bool, show_segment_prefix_bool};
use crate::formatter::{FormatterOptions, FormatterOutput};
use crate::{Code, Instruction, PrefixKindUnderlyingType, Register};
use core::{cmp, mem};
#[rustfmt::skip]
static SPACES_TABLE: [&str; 12] = [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
];
#[rustfmt::skip]
static TABS_TABLE: [&str; 4] = [
"\t",
"\t\t",
"\t\t\t",
"\t\t\t\t",
];
pub(super) fn add_tabs(output: &mut dyn FormatterOutput, mut column: u32, mut first_operand_char_index: u32, tab_size: u32) {
const MAX_FIRST_OPERAND_CHAR_INDEX: u32 = 256;
first_operand_char_index = cmp::min(first_operand_char_index, MAX_FIRST_OPERAND_CHAR_INDEX);
if tab_size == 0 {
let chars_left = if first_operand_char_index <= column { 1 } else { first_operand_char_index - column };
add_strings(output, &SPACES_TABLE[..], chars_left);
} else {
let end_col = if first_operand_char_index <= column { column + 1 } else { first_operand_char_index };
let end_col_rounded_down = end_col / tab_size * tab_size;
let added_tabs = end_col_rounded_down > column;
if added_tabs {
let tabs = (end_col_rounded_down - (column / tab_size * tab_size)) / tab_size;
add_strings(output, &TABS_TABLE[..], tabs);
column = end_col_rounded_down;
}
if first_operand_char_index > column {
add_strings(output, &SPACES_TABLE[..], first_operand_char_index - column);
} else if !added_tabs {
add_strings(output, &SPACES_TABLE[..], 1);
}
}
}
fn add_strings(output: &mut dyn FormatterOutput, strings: &[&str], count: u32) {
let mut count = count as usize;
while count > 0 {
let n = cmp::min(count, strings.len());
output.write(strings[n - 1], FormatterTextKind::Text);
count -= n;
}
}
#[must_use]
#[inline]
pub(super) fn is_call(kind: FormatterFlowControl) -> bool {
kind == FormatterFlowControl::NearCall || kind == FormatterFlowControl::FarCall
}
#[must_use]
pub(super) fn get_flow_control(instruction: &Instruction) -> FormatterFlowControl {
#[cfg_attr(feature = "cargo-fmt", rustfmt::skip)]
match instruction.code() {
Code::Jo_rel8_16
| Code::Jo_rel8_32
| Code::Jo_rel8_64
| Code::Jno_rel8_16
| Code::Jno_rel8_32
| Code::Jno_rel8_64
| Code::Jb_rel8_16
| Code::Jb_rel8_32
| Code::Jb_rel8_64
| Code::Jae_rel8_16
| Code::Jae_rel8_32
| Code::Jae_rel8_64
| Code::Je_rel8_16
| Code::Je_rel8_32
| Code::Je_rel8_64
| Code::Jne_rel8_16
| Code::Jne_rel8_32
| Code::Jne_rel8_64
| Code::Jbe_rel8_16
| Code::Jbe_rel8_32
| Code::Jbe_rel8_64
| Code::Ja_rel8_16
| Code::Ja_rel8_32
| Code::Ja_rel8_64
| Code::Js_rel8_16
| Code::Js_rel8_32
| Code::Js_rel8_64
| Code::Jns_rel8_16
| Code::Jns_rel8_32
| Code::Jns_rel8_64
| Code::Jp_rel8_16
| Code::Jp_rel8_32
| Code::Jp_rel8_64
| Code::Jnp_rel8_16
| Code::Jnp_rel8_32
| Code::Jnp_rel8_64
| Code::Jl_rel8_16
| Code::Jl_rel8_32
| Code::Jl_rel8_64
| Code::Jge_rel8_16
| Code::Jge_rel8_32
| Code::Jge_rel8_64
| Code::Jle_rel8_16
| Code::Jle_rel8_32
| Code::Jle_rel8_64
| Code::Jg_rel8_16
| Code::Jg_rel8_32
| Code::Jg_rel8_64
| Code::Jmp_rel8_16
| Code::Jmp_rel8_32
| Code::Jmp_rel8_64
| Code::VEX_KNC_Jkzd_kr_rel8_64
| Code::VEX_KNC_Jknzd_kr_rel8_64
=> FormatterFlowControl::ShortBranch,
Code::Loopne_rel8_16_CX
| Code::Loopne_rel8_32_CX
| Code::Loopne_rel8_16_ECX
| Code::Loopne_rel8_32_ECX
| Code::Loopne_rel8_64_ECX
| Code::Loopne_rel8_16_RCX
| Code::Loopne_rel8_64_RCX
| Code::Loope_rel8_16_CX
| Code::Loope_rel8_32_CX
| Code::Loope_rel8_16_ECX
| Code::Loope_rel8_32_ECX
| Code::Loope_rel8_64_ECX
| Code::Loope_rel8_16_RCX
| Code::Loope_rel8_64_RCX
| Code::Loop_rel8_16_CX
| Code::Loop_rel8_32_CX
| Code::Loop_rel8_16_ECX
| Code::Loop_rel8_32_ECX
| Code::Loop_rel8_64_ECX
| Code::Loop_rel8_16_RCX
| Code::Loop_rel8_64_RCX
| Code::Jcxz_rel8_16
| Code::Jcxz_rel8_32
| Code::Jecxz_rel8_16
| Code::Jecxz_rel8_32
| Code::Jecxz_rel8_64
| Code::Jrcxz_rel8_16
| Code::Jrcxz_rel8_64
=> FormatterFlowControl::AlwaysShortBranch,
Code::Call_rel16
| Code::Call_rel32_32
| Code::Call_rel32_64
=> FormatterFlowControl::NearCall,
Code::Jmp_rel16
| Code::Jmp_rel32_32
| Code::Jmp_rel32_64
| Code::Jo_rel16
| Code::Jo_rel32_32
| Code::Jo_rel32_64
| Code::Jno_rel16
| Code::Jno_rel32_32
| Code::Jno_rel32_64
| Code::Jb_rel16
| Code::Jb_rel32_32
| Code::Jb_rel32_64
| Code::Jae_rel16
| Code::Jae_rel32_32
| Code::Jae_rel32_64
| Code::Je_rel16
| Code::Je_rel32_32
| Code::Je_rel32_64
| Code::Jne_rel16
| Code::Jne_rel32_32
| Code::Jne_rel32_64
| Code::Jbe_rel16
| Code::Jbe_rel32_32
| Code::Jbe_rel32_64
| Code::Ja_rel16
| Code::Ja_rel32_32
| Code::Ja_rel32_64
| Code::Js_rel16
| Code::Js_rel32_32
| Code::Js_rel32_64
| Code::Jns_rel16
| Code::Jns_rel32_32
| Code::Jns_rel32_64
| Code::Jp_rel16
| Code::Jp_rel32_32
| Code::Jp_rel32_64
| Code::Jnp_rel16
| Code::Jnp_rel32_32
| Code::Jnp_rel32_64
| Code::Jl_rel16
| Code::Jl_rel32_32
| Code::Jl_rel32_64
| Code::Jge_rel16
| Code::Jge_rel32_32
| Code::Jge_rel32_64
| Code::Jle_rel16
| Code::Jle_rel32_32
| Code::Jle_rel32_64
| Code::Jg_rel16
| Code::Jg_rel32_32
| Code::Jg_rel32_64
| Code::Jmpe_disp16
| Code::Jmpe_disp32
| Code::VEX_KNC_Jkzd_kr_rel32_64
| Code::VEX_KNC_Jknzd_kr_rel32_64
=> FormatterFlowControl::NearBranch,
Code::Call_ptr1616
| Code::Call_ptr1632
=> FormatterFlowControl::FarCall,
Code::Jmp_ptr1616
| Code::Jmp_ptr1632
=> FormatterFlowControl::FarBranch,
Code::Xbegin_rel16
| Code::Xbegin_rel32
=> FormatterFlowControl::Xbegin,
_ => unreachable!(),
}
}
pub(super) const fn show_rep_or_repe_prefix(code: Code, options: &FormatterOptions) -> bool {
show_rep_or_repe_prefix_bool(code, options.show_useless_prefixes())
}
pub(super) const fn show_repne_prefix(code: Code, options: &FormatterOptions) -> bool {
show_repne_prefix_bool(code, options.show_useless_prefixes())
}
#[must_use]
#[inline]
pub(super) fn get_segment_register_prefix_kind(register: Register) -> PrefixKind {
debug_assert!(
register == Register::ES
|| register == Register::CS
|| register == Register::SS
|| register == Register::DS
|| register == Register::FS
|| register == Register::GS
);
const _: () = assert!(PrefixKind::ES as u32 + 1 == PrefixKind::CS as u32);
const _: () = assert!(PrefixKind::ES as u32 + 2 == PrefixKind::SS as u32);
const _: () = assert!(PrefixKind::ES as u32 + 3 == PrefixKind::DS as u32);
const _: () = assert!(PrefixKind::ES as u32 + 4 == PrefixKind::FS as u32);
const _: () = assert!(PrefixKind::ES as u32 + 5 == PrefixKind::GS as u32);
unsafe { mem::transmute(((register as u32 - Register::ES as u32) + PrefixKind::ES as u32) as PrefixKindUnderlyingType) }
}
pub(super) const fn show_index_scale(instruction: &Instruction, options: &FormatterOptions) -> bool {
options.show_useless_prefixes() || !instruction.code().ignores_index()
}
pub(super) fn show_segment_prefix(default_seg_reg: Register, instruction: &Instruction, options: &FormatterOptions) -> bool {
show_segment_prefix_bool(default_seg_reg, instruction, options.show_useless_prefixes())
}
#[allow(unused_variables)]
pub(super) fn can_show_rounding_control(instruction: &Instruction, options: &FormatterOptions) -> bool {
#[cfg(not(feature = "no_evex"))]
{
let code = instruction.code();
if code == Code::EVEX_Vcvtsi2sd_xmm_xmm_rm32_er
|| code == Code::EVEX_Vcvtusi2sd_xmm_xmm_rm32_er
|| code == Code::EVEX_Vcvtdq2pd_zmm_k1z_ymmm256b32_er
|| code == Code::EVEX_Vcvtudq2pd_zmm_k1z_ymmm256b32_er
{
return options.show_useless_prefixes();
}
}
true
}