4
const assert = require("assert").strict;
5
const fs = require("fs");
6
const path = require("path");
7
const x86_table = require("./x86_table");
8
const rust_ast = require("./rust_ast");
9
const { hex, mkdirpSync, get_switch_value, get_switch_exist, finalize_table_rust } = require("./util");
11
const OUT_DIR = path.join(__dirname, "..", "src/rust/gen/");
15
const table_arg = get_switch_value("--table");
16
const gen_all = get_switch_exist("--all");
18
analyzer: gen_all || table_arg === "analyzer",
19
analyzer0f: gen_all || table_arg === "analyzer0f",
23
Object.keys(to_generate).some(k => to_generate[k]),
24
"Pass --table [analyzer|analyzer0f] or --all to pick which tables to generate"
29
function gen_read_imm_call(op, size_variant)
31
let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
33
if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
37
return "cpu.read_imm8()";
41
return "cpu.read_imm8s()";
48
return "cpu.read_moffs()";
52
assert(op.imm1632 || op.imm16 || op.imm32);
54
if(op.imm1632 && size === 16 || op.imm16)
56
return "cpu.read_imm16()";
60
assert(op.imm1632 && size === 32 || op.imm32);
61
return "cpu.read_imm32()";
72
function gen_call(name, args)
75
return `${name}(${args.join(", ")});`;
82
function make_instruction_name(encoding, size)
84
const suffix = encoding.os ? String(size) : "";
85
const opcode_hex = hex(encoding.opcode & 0xFF, 2);
86
const first_prefix = (encoding.opcode & 0xFF00) === 0 ? "" : hex(encoding.opcode >> 8 & 0xFF, 2);
87
const second_prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
88
const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
90
assert(first_prefix === "" || first_prefix === "0F" || first_prefix === "F2" || first_prefix === "F3");
91
assert(second_prefix === "" || second_prefix === "66" || second_prefix === "F2" || second_prefix === "F3");
93
return `instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
96
function gen_instruction_body(encodings, size)
98
const encoding = encodings[0];
105
for(let e of encodings)
107
if((e.opcode >>> 16) === 0x66) has_66.push(e);
108
else if((e.opcode >>> 8 & 0xFF) === 0xF2 || (e.opcode >>> 16) === 0xF2) has_F2.push(e);
109
else if((e.opcode >>> 8 & 0xFF) === 0xF3 || (e.opcode >>> 16) === 0xF3) has_F3.push(e);
110
else no_prefix.push(e);
113
if(has_F2.length || has_F3.length)
115
assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
120
assert((encoding.opcode & 0xFF00) === 0x0F00);
127
code.push("let modrm_byte = cpu.read_imm8();");
130
if(has_66.length || has_F2.length || has_F3.length)
132
const if_blocks = [];
135
const body = gen_instruction_body_after_prefix(has_66, size);
136
if_blocks.push({ condition: "cpu.prefixes & ::prefix::PREFIX_66 != 0", body, });
139
const body = gen_instruction_body_after_prefix(has_F2, size);
140
if_blocks.push({ condition: "cpu.prefixes & ::prefix::PREFIX_F2 != 0", body, });
143
const body = gen_instruction_body_after_prefix(has_F3, size);
144
if_blocks.push({ condition: "cpu.prefixes & ::prefix::PREFIX_F3 != 0", body, });
148
body: gen_instruction_body_after_prefix(no_prefix, size),
163
gen_instruction_body_after_prefix(encodings, size)
168
function gen_instruction_body_after_prefix(encodings, size)
170
const encoding = encodings[0];
172
if(encoding.fixed_g !== undefined)
179
let cases = encodings.reduce((cases_by_opcode, case_) => {
180
assert(typeof case_.fixed_g === "number");
181
cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
182
return cases_by_opcode;
183
}, Object.create(null));
184
cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
189
condition: "modrm_byte >> 3 & 7",
190
cases: cases.map(case_ => {
191
const fixed_g = case_.fixed_g;
192
const body = gen_instruction_body_after_fixed_g(case_, size);
195
conditions: [fixed_g],
202
"analysis.ty = ::analysis::AnalysisType::BlockBoundary;",
203
"analysis.no_next_instruction = true;",
210
assert(encodings.length === 1);
211
return gen_instruction_body_after_fixed_g(encodings[0], size);
215
function gen_instruction_body_after_fixed_g(encoding, size)
217
const imm_read = gen_read_imm_call(encoding, size);
218
const instruction_postfix = [];
220
if(encoding.custom_sti) {
221
instruction_postfix.push("analysis.ty = ::analysis::AnalysisType::STI;");
224
encoding.block_boundary &&
226
!encoding.jump_offset_imm || (!encoding.custom && encoding.e))
228
instruction_postfix.push("analysis.ty = ::analysis::AnalysisType::BlockBoundary;");
231
if(encoding.no_next_instruction)
233
instruction_postfix.push("analysis.no_next_instruction = true;");
235
if(encoding.absolute_jump)
237
instruction_postfix.push("analysis.absolute_jump = true;");
242
const instruction_name = "::analysis::" + make_instruction_name(encoding, size) + "_analyze";
243
const args = ["cpu", "analysis"];
248
gen_call(instruction_name, args),
256
const reg_postfix = [];
257
const mem_postfix = [];
262
"analysis.ty = ::analysis::AnalysisType::BlockBoundary;"
269
"analysis.ty = ::analysis::AnalysisType::BlockBoundary;"
273
if(encoding.ignore_mod)
275
assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
280
return instruction_postfix;
288
condition: "modrm_byte < 0xC0",
290
gen_call("::analysis::modrm_analyze", ["cpu", "modrm_byte"]),
298
imm_read ? [imm_read + ";"] : [],
311
if(encoding.jump_offset_imm)
313
body.push("let jump_offset = " + imm_read + ";");
315
if(encoding.conditional_jump)
318
(encoding.opcode & ~0xF) === 0x70 ||
319
(encoding.opcode & ~0xF) === 0x0F80 ||
320
(encoding.opcode & ~0x3) === 0xE0
322
const condition_index = encoding.opcode & 0xFF;
323
body.push(`analysis.ty = ::analysis::AnalysisType::Jump { offset: jump_offset as i32, condition: Some(0x${hex(condition_index, 2)}), is_32: cpu.osize_32() };`);
327
body.push(`analysis.ty = ::analysis::AnalysisType::Jump { offset: jump_offset as i32, condition: None, is_32: cpu.osize_32() };`);
332
body.push(imm_read + ";");
336
if(encoding.extra_imm16)
339
body.push(gen_call("cpu.read_imm16"));
341
else if(encoding.extra_imm8)
344
body.push(gen_call("cpu.read_imm8"));
356
let by_opcode = Object.create(null);
357
let by_opcode0f = Object.create(null);
359
for(let o of x86_table)
361
let opcode = o.opcode;
363
if((opcode & 0xFF00) === 0x0F00)
366
by_opcode0f[opcode] = by_opcode0f[opcode] || [];
367
by_opcode0f[opcode].push(o);
372
by_opcode[opcode] = by_opcode[opcode] || [];
373
by_opcode[opcode].push(o);
378
for(let opcode = 0; opcode < 0x100; opcode++)
380
let encoding = by_opcode[opcode];
381
assert(encoding && encoding.length);
383
let opcode_hex = hex(opcode, 2);
384
let opcode_high_hex = hex(opcode | 0x100, 2);
389
conditions: [`0x${opcode_hex}`],
390
body: gen_instruction_body(encoding, 16),
393
conditions: [`0x${opcode_high_hex}`],
394
body: gen_instruction_body(encoding, 32),
400
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
401
body: gen_instruction_body(encoding, undefined),
410
body: ["dbg_assert!(false);"]
414
if(to_generate.analyzer)
417
"#[cfg_attr(rustfmt, rustfmt_skip)]",
418
"pub fn analyzer(opcode: u32, cpu: &mut ::cpu_context::CpuContext, analysis: &mut ::analysis::Analysis) {",
426
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
431
for(let opcode = 0; opcode < 0x100; opcode++)
433
let encoding = by_opcode0f[opcode];
435
assert(encoding && encoding.length);
437
let opcode_hex = hex(opcode, 2);
438
let opcode_high_hex = hex(opcode | 0x100, 2);
443
conditions: [`0x${opcode_hex}`],
444
body: gen_instruction_body(encoding, 16),
447
conditions: [`0x${opcode_high_hex}`],
448
body: gen_instruction_body(encoding, 32),
454
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
455
body: gen_instruction_body(encoding, undefined),
466
body: ["dbg_assert!(false);"]
470
if(to_generate.analyzer0f)
474
"#[cfg_attr(rustfmt, rustfmt_skip)]",
475
"pub fn analyzer(opcode: u32, cpu: &mut ::cpu_context::CpuContext, analysis: &mut ::analysis::Analysis) {",
483
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"