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
interpreter: gen_all || table_arg === "interpreter",
19
interpreter0f: gen_all || table_arg === "interpreter0f",
23
Object.keys(to_generate).some(k => to_generate[k]),
24
"Pass --table [interpreter|interpreter0f] or --all to pick which tables to generate"
29
function wrap_imm_call(imm)
31
return `match ${imm} { Ok(o) => o, Err(()) => return }`;
34
function gen_read_imm_call(op, size_variant)
36
let size = (op.os || op.opcode % 2 === 1) ? size_variant : 8;
38
if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
42
return wrap_imm_call("read_imm8()");
46
return wrap_imm_call("read_imm8s()");
53
return wrap_imm_call("read_moffs()");
57
assert(op.imm1632 || op.imm16 || op.imm32);
59
if(op.imm1632 && size === 16 || op.imm16)
61
return wrap_imm_call("read_imm16()");
65
assert(op.imm1632 && size === 32 || op.imm32);
66
return wrap_imm_call("read_imm32s()");
77
function gen_call(name, args)
80
return `${name}(${args.join(", ")});`;
87
function make_instruction_name(encoding, size)
89
const suffix = encoding.os ? String(size) : "";
90
const opcode_hex = hex(encoding.opcode & 0xFF, 2);
91
const first_prefix = (encoding.opcode & 0xFF00) === 0 ? "" : hex(encoding.opcode >> 8 & 0xFF, 2);
92
const second_prefix = (encoding.opcode & 0xFF0000) === 0 ? "" : hex(encoding.opcode >> 16 & 0xFF, 2);
93
const fixed_g_suffix = encoding.fixed_g === undefined ? "" : `_${encoding.fixed_g}`;
94
const module = first_prefix === "0F" || second_prefix === "0F" ? "instructions_0f" : "instructions";
96
assert(first_prefix === "" || first_prefix === "0F" || first_prefix === "F2" || first_prefix === "F3");
97
assert(second_prefix === "" || second_prefix === "66" || second_prefix === "F2" || second_prefix === "F3");
99
return `${module}::instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`;
102
function gen_instruction_body(encodings, size)
104
const encoding = encodings[0];
111
for(let e of encodings)
113
if((e.opcode >>> 16) === 0x66) has_66.push(e);
114
else if((e.opcode >>> 8 & 0xFF) === 0xF2 || (e.opcode >>> 16) === 0xF2) has_F2.push(e);
115
else if((e.opcode >>> 8 & 0xFF) === 0xF3 || (e.opcode >>> 16) === 0xF3) has_F3.push(e);
116
else no_prefix.push(e);
119
if(has_F2.length || has_F3.length)
121
assert((encoding.opcode & 0xFF0000) === 0 || (encoding.opcode & 0xFF00) === 0x0F00);
126
assert((encoding.opcode & 0xFF00) === 0x0F00);
133
code.push(`let modrm_byte = ${wrap_imm_call("read_imm8()")};`);
136
if(has_66.length || has_F2.length || has_F3.length)
138
const if_blocks = [];
141
const body = gen_instruction_body_after_prefix(has_66, size);
142
if_blocks.push({ condition: "prefixes_ & PREFIX_66 != 0", body, });
145
const body = gen_instruction_body_after_prefix(has_F2, size);
146
if_blocks.push({ condition: "prefixes_ & PREFIX_F2 != 0", body, });
149
const body = gen_instruction_body_after_prefix(has_F3, size);
150
if_blocks.push({ condition: "prefixes_ & PREFIX_F3 != 0", body, });
153
const check_prefixes = encoding.sse ? "(PREFIX_66 | PREFIX_F2 | PREFIX_F3)" : "(PREFIX_F2 | PREFIX_F3)";
157
"dbg_assert!((prefixes_ & " + check_prefixes + ") == 0);",
158
gen_instruction_body_after_prefix(no_prefix, size)
163
"let prefixes_ = *prefixes as i32;",
175
gen_instruction_body_after_prefix(encodings, size)
180
function gen_instruction_body_after_prefix(encodings, size)
182
const encoding = encodings[0];
184
if(encoding.fixed_g !== undefined)
191
let cases = encodings.reduce((cases_by_opcode, case_) => {
192
assert(typeof case_.fixed_g === "number");
193
cases_by_opcode[case_.opcode & 0xFFFF | case_.fixed_g << 16] = case_;
194
return cases_by_opcode;
195
}, Object.create(null));
196
cases = Object.values(cases).sort((e1, e2) => e1.fixed_g - e2.fixed_g);
201
condition: "modrm_byte >> 3 & 7",
202
cases: cases.map(case_ => {
203
const fixed_g = case_.fixed_g;
204
const body = gen_instruction_body_after_fixed_g(case_, size);
207
conditions: [fixed_g],
214
`if DEBUG { panic!("Bad instruction at {:x}", *instruction_pointer); }`,
222
assert(encodings.length === 1);
223
return gen_instruction_body_after_fixed_g(encodings[0], size);
227
function gen_instruction_body_after_fixed_g(encoding, size)
229
const instruction_prefix = [];
230
const instruction_postfix =
231
(encoding.block_boundary && !encoding.no_block_boundary_in_interpreted) ||
232
(!encoding.custom && encoding.e) ?
233
["after_block_boundary();"] : [];
235
if(encoding.task_switch_test || encoding.sse)
237
instruction_prefix.push(
242
condition: encoding.sse ? "!task_switch_test_mmx()" : "!task_switch_test()",
249
const imm_read = gen_read_imm_call(encoding, size);
250
const instruction_name = make_instruction_name(encoding, size);
256
const imm_read = gen_read_imm_call(encoding, size);
258
if(encoding.ignore_mod)
260
assert(!imm_read, "Unexpected instruction (ignore mod with immediate value)");
267
gen_call(instruction_name, ["modrm_byte & 7", "modrm_byte >> 3 & 7"]),
275
if(encoding.custom_modrm_resolve)
278
mem_args = ["modrm_byte"];
282
mem_args = ["match modrm_resolve(modrm_byte) { Ok(a) => a, Err(()) => return }"];
285
const reg_args = ["modrm_byte & 7"];
287
if(encoding.fixed_g === undefined)
289
mem_args.push("modrm_byte >> 3 & 7");
290
reg_args.push("modrm_byte >> 3 & 7");
295
mem_args.push(imm_read);
296
reg_args.push(imm_read);
305
condition: "modrm_byte < 0xC0",
307
gen_call(`${instruction_name}_mem`, mem_args)
312
body: [gen_call(`${instruction_name}_reg`, reg_args)],
328
if(encoding.extra_imm16)
331
args.push(wrap_imm_call("read_imm16()"));
333
else if(encoding.extra_imm8)
336
args.push(wrap_imm_call("read_imm8()"));
341
gen_call(instruction_name, args),
349
let by_opcode = Object.create(null);
350
let by_opcode0f = Object.create(null);
352
for(let o of x86_table)
354
let opcode = o.opcode;
356
if((opcode & 0xFF00) === 0x0F00)
359
by_opcode0f[opcode] = by_opcode0f[opcode] || [];
360
by_opcode0f[opcode].push(o);
365
by_opcode[opcode] = by_opcode[opcode] || [];
366
by_opcode[opcode].push(o);
371
for(let opcode = 0; opcode < 0x100; opcode++)
373
let encoding = by_opcode[opcode];
374
assert(encoding && encoding.length);
376
let opcode_hex = hex(opcode, 2);
377
let opcode_high_hex = hex(opcode | 0x100, 2);
382
conditions: [`0x${opcode_hex}`],
383
body: gen_instruction_body(encoding, 16),
386
conditions: [`0x${opcode_high_hex}`],
387
body: gen_instruction_body(encoding, 32),
393
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
394
body: gen_instruction_body(encoding, undefined),
403
body: ["assert!(false);"]
406
if(to_generate.interpreter)
409
"#![cfg_attr(rustfmt, rustfmt_skip)]",
411
"use cpu::cpu::{after_block_boundary, modrm_resolve};",
412
"use cpu::cpu::{read_imm8, read_imm8s, read_imm16, read_imm32s, read_moffs};",
413
"use cpu::cpu::{task_switch_test, trigger_ud, DEBUG, PREFIX_F2, PREFIX_F3};",
414
"use cpu::instructions;",
415
"use cpu::global_pointers::{instruction_pointer, prefixes};",
417
"pub unsafe fn run(opcode: u32) {",
425
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"
430
for(let opcode = 0; opcode < 0x100; opcode++)
432
let encoding = by_opcode0f[opcode];
434
assert(encoding && encoding.length);
436
let opcode_hex = hex(opcode, 2);
437
let opcode_high_hex = hex(opcode | 0x100, 2);
442
conditions: [`0x${opcode_hex}`],
443
body: gen_instruction_body(encoding, 16),
446
conditions: [`0x${opcode_high_hex}`],
447
body: gen_instruction_body(encoding, 32),
453
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
454
body: gen_instruction_body(encoding, undefined),
465
body: ["assert!(false);"]
469
if(to_generate.interpreter0f)
472
"#![cfg_attr(rustfmt, rustfmt_skip)]",
474
"use cpu::cpu::{after_block_boundary, modrm_resolve};",
475
"use cpu::cpu::{read_imm8, read_imm16, read_imm32s};",
476
"use cpu::cpu::{task_switch_test, task_switch_test_mmx, trigger_ud};",
477
"use cpu::cpu::{DEBUG, PREFIX_66, PREFIX_F2, PREFIX_F3};",
478
"use cpu::instructions_0f;",
479
"use cpu::global_pointers::{instruction_pointer, prefixes};",
481
"pub unsafe fn run(opcode: u32) {",
489
rust_ast.print_syntax_tree([].concat(code)).join("\n") + "\n"