@aptre/v86 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- package/src/virtio_net.ts +326 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import assert from 'node:assert/strict'
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import url from 'node:url'
|
|
7
|
+
|
|
8
|
+
import x86_table from './x86_table.js'
|
|
9
|
+
import type { X86Encoding } from './x86_table.js'
|
|
10
|
+
import * as rust_ast from './rust_ast.js'
|
|
11
|
+
import type { Statement, SwitchCase } from './rust_ast.js'
|
|
12
|
+
import {
|
|
13
|
+
hex,
|
|
14
|
+
get_switch_value,
|
|
15
|
+
get_switch_exist,
|
|
16
|
+
finalize_table_rust,
|
|
17
|
+
} from './util.js'
|
|
18
|
+
|
|
19
|
+
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
|
20
|
+
const OUT_DIR = path.join(__dirname, '..', 'src/rust/gen/')
|
|
21
|
+
|
|
22
|
+
fs.mkdirSync(OUT_DIR, { recursive: true })
|
|
23
|
+
|
|
24
|
+
const table_arg = get_switch_value('--table')
|
|
25
|
+
const gen_all = get_switch_exist('--all')
|
|
26
|
+
const to_generate: Record<string, boolean> = {
|
|
27
|
+
interpreter: gen_all || table_arg === 'interpreter',
|
|
28
|
+
interpreter0f: gen_all || table_arg === 'interpreter0f',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
assert(
|
|
32
|
+
Object.keys(to_generate).some((k) => to_generate[k]),
|
|
33
|
+
'Pass --table [interpreter|interpreter0f] or --all to pick which tables to generate',
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
gen_table()
|
|
37
|
+
|
|
38
|
+
function wrap_imm_call(imm: string): string {
|
|
39
|
+
return `match ${imm} { Ok(o) => o, Err(()) => return }`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function gen_read_imm_call(
|
|
43
|
+
op: Readonly<X86Encoding>,
|
|
44
|
+
size_variant: number | undefined,
|
|
45
|
+
): string | undefined {
|
|
46
|
+
const size = op.os || op.opcode % 2 === 1 ? size_variant : 8
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
op.imm8 ||
|
|
50
|
+
op.imm8s ||
|
|
51
|
+
op.imm16 ||
|
|
52
|
+
op.imm1632 ||
|
|
53
|
+
op.imm32 ||
|
|
54
|
+
op.immaddr
|
|
55
|
+
) {
|
|
56
|
+
if (op.imm8) {
|
|
57
|
+
return wrap_imm_call('read_imm8()')
|
|
58
|
+
} else if (op.imm8s) {
|
|
59
|
+
return wrap_imm_call('read_imm8s()')
|
|
60
|
+
} else {
|
|
61
|
+
if (op.immaddr) {
|
|
62
|
+
// immaddr: depends on address size
|
|
63
|
+
return wrap_imm_call('read_moffs()')
|
|
64
|
+
} else {
|
|
65
|
+
assert(op.imm1632 || op.imm16 || op.imm32)
|
|
66
|
+
|
|
67
|
+
if ((op.imm1632 && size === 16) || op.imm16) {
|
|
68
|
+
return wrap_imm_call('read_imm16()')
|
|
69
|
+
} else {
|
|
70
|
+
assert((op.imm1632 && size === 32) || op.imm32)
|
|
71
|
+
return wrap_imm_call('read_imm32s()')
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function gen_call(name: string, args: string[] = []): string {
|
|
81
|
+
return `${name}(${args.join(', ')});`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* Current naming scheme:
|
|
86
|
+
* instr(16|32|)_(66|F2|F3)?0F?[0-9a-f]{2}(_[0-7])?(_mem|_reg|)
|
|
87
|
+
*/
|
|
88
|
+
function make_instruction_name(
|
|
89
|
+
encoding: Readonly<X86Encoding>,
|
|
90
|
+
size: number | undefined,
|
|
91
|
+
): string {
|
|
92
|
+
const suffix = encoding.os ? String(size) : ''
|
|
93
|
+
const opcode_hex = hex(encoding.opcode & 0xff, 2)
|
|
94
|
+
const first_prefix =
|
|
95
|
+
(encoding.opcode & 0xff00) === 0
|
|
96
|
+
? ''
|
|
97
|
+
: hex((encoding.opcode >> 8) & 0xff, 2)
|
|
98
|
+
const second_prefix =
|
|
99
|
+
(encoding.opcode & 0xff0000) === 0
|
|
100
|
+
? ''
|
|
101
|
+
: hex((encoding.opcode >> 16) & 0xff, 2)
|
|
102
|
+
const fixed_g_suffix =
|
|
103
|
+
encoding.fixed_g === undefined ? '' : `_${encoding.fixed_g}`
|
|
104
|
+
const module =
|
|
105
|
+
first_prefix === '0F' || second_prefix === '0F'
|
|
106
|
+
? 'instructions_0f'
|
|
107
|
+
: 'instructions'
|
|
108
|
+
|
|
109
|
+
assert(
|
|
110
|
+
first_prefix === '' ||
|
|
111
|
+
first_prefix === '0F' ||
|
|
112
|
+
first_prefix === 'F2' ||
|
|
113
|
+
first_prefix === 'F3',
|
|
114
|
+
)
|
|
115
|
+
assert(
|
|
116
|
+
second_prefix === '' ||
|
|
117
|
+
second_prefix === '66' ||
|
|
118
|
+
second_prefix === 'F2' ||
|
|
119
|
+
second_prefix === 'F3',
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return `${module}::instr${suffix}_${second_prefix}${first_prefix}${opcode_hex}${fixed_g_suffix}`
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function gen_instruction_body(
|
|
126
|
+
encodings: Readonly<X86Encoding>[],
|
|
127
|
+
size: number | undefined,
|
|
128
|
+
): Statement[] {
|
|
129
|
+
const encoding = encodings[0]
|
|
130
|
+
|
|
131
|
+
const has_66: Readonly<X86Encoding>[] = []
|
|
132
|
+
const has_F2: Readonly<X86Encoding>[] = []
|
|
133
|
+
const has_F3: Readonly<X86Encoding>[] = []
|
|
134
|
+
const no_prefix: Readonly<X86Encoding>[] = []
|
|
135
|
+
|
|
136
|
+
for (const e of encodings) {
|
|
137
|
+
if (e.opcode >>> 16 === 0x66) has_66.push(e)
|
|
138
|
+
else if (((e.opcode >>> 8) & 0xff) === 0xf2 || e.opcode >>> 16 === 0xf2)
|
|
139
|
+
has_F2.push(e)
|
|
140
|
+
else if (((e.opcode >>> 8) & 0xff) === 0xf3 || e.opcode >>> 16 === 0xf3)
|
|
141
|
+
has_F3.push(e)
|
|
142
|
+
else no_prefix.push(e)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (has_F2.length || has_F3.length) {
|
|
146
|
+
assert(
|
|
147
|
+
(encoding.opcode & 0xff0000) === 0 ||
|
|
148
|
+
(encoding.opcode & 0xff00) === 0x0f00,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (has_66.length) {
|
|
153
|
+
assert((encoding.opcode & 0xff00) === 0x0f00)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const code: Statement[] = []
|
|
157
|
+
|
|
158
|
+
if (encoding.e) {
|
|
159
|
+
code.push(`let modrm_byte = ${wrap_imm_call('read_imm8()')};`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (has_66.length || has_F2.length || has_F3.length) {
|
|
163
|
+
const if_blocks: { condition: string; body: Statement[] }[] = []
|
|
164
|
+
|
|
165
|
+
if (has_66.length) {
|
|
166
|
+
const body = gen_instruction_body_after_prefix(has_66, size)
|
|
167
|
+
if_blocks.push({
|
|
168
|
+
condition: 'prefixes_ & prefix::PREFIX_66 != 0',
|
|
169
|
+
body,
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
if (has_F2.length) {
|
|
173
|
+
const body = gen_instruction_body_after_prefix(has_F2, size)
|
|
174
|
+
if_blocks.push({
|
|
175
|
+
condition: 'prefixes_ & prefix::PREFIX_F2 != 0',
|
|
176
|
+
body,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
if (has_F3.length) {
|
|
180
|
+
const body = gen_instruction_body_after_prefix(has_F3, size)
|
|
181
|
+
if_blocks.push({
|
|
182
|
+
condition: 'prefixes_ & prefix::PREFIX_F3 != 0',
|
|
183
|
+
body,
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const check_prefixes = encoding.sse
|
|
188
|
+
? '(prefix::PREFIX_66 | prefix::PREFIX_F2 | prefix::PREFIX_F3)'
|
|
189
|
+
: '(prefix::PREFIX_F2 | prefix::PREFIX_F3)'
|
|
190
|
+
|
|
191
|
+
const else_block = {
|
|
192
|
+
body: ([] as Statement[]).concat(
|
|
193
|
+
'dbg_assert!((prefixes_ & ' + check_prefixes + ') == 0);',
|
|
194
|
+
gen_instruction_body_after_prefix(no_prefix, size),
|
|
195
|
+
),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return ([] as Statement[]).concat('let prefixes_ = *prefixes;', code, {
|
|
199
|
+
type: 'if-else',
|
|
200
|
+
if_blocks,
|
|
201
|
+
else_block,
|
|
202
|
+
})
|
|
203
|
+
} else {
|
|
204
|
+
return ([] as Statement[]).concat(
|
|
205
|
+
code,
|
|
206
|
+
gen_instruction_body_after_prefix(encodings, size),
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function gen_instruction_body_after_prefix(
|
|
212
|
+
encodings: Readonly<X86Encoding>[],
|
|
213
|
+
size: number | undefined,
|
|
214
|
+
): Statement[] {
|
|
215
|
+
const encoding = encodings[0]
|
|
216
|
+
|
|
217
|
+
if (encoding.fixed_g !== undefined) {
|
|
218
|
+
assert(encoding.e)
|
|
219
|
+
|
|
220
|
+
// instruction with modrm byte where the middle 3 bits encode the instruction
|
|
221
|
+
|
|
222
|
+
// group by opcode without prefix plus middle bits of modrm byte
|
|
223
|
+
const cases: Record<number, Readonly<X86Encoding>> = encodings.reduce(
|
|
224
|
+
(cases_by_opcode: Record<number, Readonly<X86Encoding>>, case_) => {
|
|
225
|
+
assert(typeof case_.fixed_g === 'number')
|
|
226
|
+
cases_by_opcode[
|
|
227
|
+
(case_.opcode & 0xffff) | (case_.fixed_g << 16)
|
|
228
|
+
] = case_
|
|
229
|
+
return cases_by_opcode
|
|
230
|
+
},
|
|
231
|
+
Object.create(null) as Record<number, Readonly<X86Encoding>>,
|
|
232
|
+
)
|
|
233
|
+
const sorted = Object.values(cases).sort(
|
|
234
|
+
(e1, e2) => (e1.fixed_g ?? 0) - (e2.fixed_g ?? 0),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return [
|
|
238
|
+
{
|
|
239
|
+
type: 'switch',
|
|
240
|
+
condition: 'modrm_byte >> 3 & 7',
|
|
241
|
+
cases: sorted.map((case_): SwitchCase => {
|
|
242
|
+
const fixed_g = case_.fixed_g!
|
|
243
|
+
const body = gen_instruction_body_after_fixed_g(case_, size)
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
conditions: [fixed_g],
|
|
247
|
+
body,
|
|
248
|
+
}
|
|
249
|
+
}),
|
|
250
|
+
|
|
251
|
+
default_case: {
|
|
252
|
+
varname: 'x',
|
|
253
|
+
body: [
|
|
254
|
+
`dbg_log!("#ud ${encoding.opcode.toString(16).toUpperCase()}/{} at {:x}", x, *instruction_pointer);`,
|
|
255
|
+
'trigger_ud();',
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
]
|
|
260
|
+
} else {
|
|
261
|
+
assert(encodings.length === 1)
|
|
262
|
+
return gen_instruction_body_after_fixed_g(encodings[0], size)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function gen_instruction_body_after_fixed_g(
|
|
267
|
+
encoding: Readonly<X86Encoding>,
|
|
268
|
+
size: number | undefined,
|
|
269
|
+
): Statement[] {
|
|
270
|
+
const instruction_prefix: Statement[] = []
|
|
271
|
+
const instruction_postfix: Statement[] =
|
|
272
|
+
(encoding.block_boundary &&
|
|
273
|
+
!encoding.no_block_boundary_in_interpreted) ||
|
|
274
|
+
(!encoding.custom && encoding.e)
|
|
275
|
+
? ['after_block_boundary();']
|
|
276
|
+
: []
|
|
277
|
+
|
|
278
|
+
if (encoding.task_switch_test || encoding.sse) {
|
|
279
|
+
instruction_prefix.push({
|
|
280
|
+
type: 'if-else',
|
|
281
|
+
if_blocks: [
|
|
282
|
+
{
|
|
283
|
+
condition: encoding.sse
|
|
284
|
+
? '!task_switch_test_mmx()'
|
|
285
|
+
: '!task_switch_test()',
|
|
286
|
+
body: ['return;'],
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const imm_read = gen_read_imm_call(encoding, size)
|
|
293
|
+
const instruction_name = make_instruction_name(encoding, size)
|
|
294
|
+
|
|
295
|
+
if (encoding.e) {
|
|
296
|
+
// instruction with modrm byte
|
|
297
|
+
|
|
298
|
+
const imm_read_inner = gen_read_imm_call(encoding, size)
|
|
299
|
+
|
|
300
|
+
if (encoding.ignore_mod) {
|
|
301
|
+
assert(
|
|
302
|
+
!imm_read_inner,
|
|
303
|
+
'Unexpected instruction (ignore mod with immediate value)',
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
// Has modrm byte, but the 2 mod bits are ignored and both
|
|
307
|
+
// operands are always registers (0f20-0f24)
|
|
308
|
+
|
|
309
|
+
return ([] as Statement[]).concat(
|
|
310
|
+
instruction_prefix,
|
|
311
|
+
gen_call(instruction_name, [
|
|
312
|
+
'modrm_byte & 7',
|
|
313
|
+
'modrm_byte >> 3 & 7',
|
|
314
|
+
]),
|
|
315
|
+
instruction_postfix,
|
|
316
|
+
)
|
|
317
|
+
} else {
|
|
318
|
+
let mem_args: string[]
|
|
319
|
+
|
|
320
|
+
if (encoding.custom_modrm_resolve) {
|
|
321
|
+
// requires special handling around modrm_resolve
|
|
322
|
+
mem_args = ['modrm_byte']
|
|
323
|
+
} else {
|
|
324
|
+
mem_args = [
|
|
325
|
+
'match modrm_resolve(modrm_byte) { Ok(a) => a, Err(()) => return }',
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const reg_args: string[] = ['modrm_byte & 7']
|
|
330
|
+
|
|
331
|
+
if (encoding.fixed_g === undefined) {
|
|
332
|
+
mem_args.push('modrm_byte >> 3 & 7')
|
|
333
|
+
reg_args.push('modrm_byte >> 3 & 7')
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (imm_read_inner) {
|
|
337
|
+
mem_args.push(imm_read_inner)
|
|
338
|
+
reg_args.push(imm_read_inner)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return ([] as Statement[]).concat(
|
|
342
|
+
instruction_prefix,
|
|
343
|
+
{
|
|
344
|
+
type: 'if-else',
|
|
345
|
+
if_blocks: [
|
|
346
|
+
{
|
|
347
|
+
condition: 'modrm_byte < 0xC0',
|
|
348
|
+
body: ([] as Statement[]).concat(
|
|
349
|
+
gen_call(`${instruction_name}_mem`, mem_args),
|
|
350
|
+
),
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
else_block: {
|
|
354
|
+
body: [gen_call(`${instruction_name}_reg`, reg_args)],
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
instruction_postfix,
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
const args: string[] = []
|
|
362
|
+
|
|
363
|
+
if (imm_read) {
|
|
364
|
+
args.push(imm_read)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (encoding.extra_imm16) {
|
|
368
|
+
assert(imm_read)
|
|
369
|
+
args.push(wrap_imm_call('read_imm16()'))
|
|
370
|
+
} else if (encoding.extra_imm8) {
|
|
371
|
+
assert(imm_read)
|
|
372
|
+
args.push(wrap_imm_call('read_imm8()'))
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return ([] as Statement[]).concat(
|
|
376
|
+
instruction_prefix,
|
|
377
|
+
gen_call(instruction_name, args),
|
|
378
|
+
instruction_postfix,
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function gen_table(): void {
|
|
384
|
+
const by_opcode: Record<number, Readonly<X86Encoding>[]> = Object.create(
|
|
385
|
+
null,
|
|
386
|
+
) as Record<number, Readonly<X86Encoding>[]>
|
|
387
|
+
const by_opcode0f: Record<number, Readonly<X86Encoding>[]> = Object.create(
|
|
388
|
+
null,
|
|
389
|
+
) as Record<number, Readonly<X86Encoding>[]>
|
|
390
|
+
|
|
391
|
+
for (const o of x86_table) {
|
|
392
|
+
let opcode = o.opcode
|
|
393
|
+
|
|
394
|
+
if ((opcode & 0xff00) === 0x0f00) {
|
|
395
|
+
opcode &= 0xff
|
|
396
|
+
by_opcode0f[opcode] = by_opcode0f[opcode] || []
|
|
397
|
+
by_opcode0f[opcode].push(o)
|
|
398
|
+
} else {
|
|
399
|
+
opcode &= 0xff
|
|
400
|
+
by_opcode[opcode] = by_opcode[opcode] || []
|
|
401
|
+
by_opcode[opcode].push(o)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const cases: SwitchCase[] = []
|
|
406
|
+
for (let opcode = 0; opcode < 0x100; opcode++) {
|
|
407
|
+
const encoding = by_opcode[opcode]
|
|
408
|
+
assert(encoding && encoding.length)
|
|
409
|
+
|
|
410
|
+
const opcode_hex = hex(opcode, 2)
|
|
411
|
+
const opcode_high_hex = hex(opcode | 0x100, 2)
|
|
412
|
+
|
|
413
|
+
if (encoding[0].os) {
|
|
414
|
+
cases.push({
|
|
415
|
+
conditions: [`0x${opcode_hex}`],
|
|
416
|
+
body: gen_instruction_body(encoding, 16),
|
|
417
|
+
})
|
|
418
|
+
cases.push({
|
|
419
|
+
conditions: [`0x${opcode_high_hex}`],
|
|
420
|
+
body: gen_instruction_body(encoding, 32),
|
|
421
|
+
})
|
|
422
|
+
} else {
|
|
423
|
+
cases.push({
|
|
424
|
+
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
|
425
|
+
body: gen_instruction_body(encoding, undefined),
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const table: Statement = {
|
|
430
|
+
type: 'switch',
|
|
431
|
+
condition: 'opcode',
|
|
432
|
+
cases,
|
|
433
|
+
default_case: {
|
|
434
|
+
body: ['assert!(false);'],
|
|
435
|
+
},
|
|
436
|
+
}
|
|
437
|
+
if (to_generate.interpreter) {
|
|
438
|
+
const code: Statement[] = [
|
|
439
|
+
'#![cfg_attr(rustfmt, rustfmt_skip)]',
|
|
440
|
+
|
|
441
|
+
'use crate::cpu::cpu::{after_block_boundary, modrm_resolve};',
|
|
442
|
+
'use crate::cpu::cpu::{read_imm8, read_imm8s, read_imm16, read_imm32s, read_moffs};',
|
|
443
|
+
'use crate::cpu::cpu::{task_switch_test, trigger_ud};',
|
|
444
|
+
'use crate::cpu::instructions;',
|
|
445
|
+
'use crate::cpu::global_pointers::{instruction_pointer, prefixes};',
|
|
446
|
+
'use crate::prefix;',
|
|
447
|
+
|
|
448
|
+
'pub unsafe fn run(opcode: u32) {',
|
|
449
|
+
table,
|
|
450
|
+
'}',
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
finalize_table_rust(
|
|
454
|
+
OUT_DIR,
|
|
455
|
+
'interpreter.rs',
|
|
456
|
+
rust_ast
|
|
457
|
+
.print_syntax_tree(([] as Statement[]).concat(code))
|
|
458
|
+
.join('\n') + '\n',
|
|
459
|
+
)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const cases0f: SwitchCase[] = []
|
|
463
|
+
for (let opcode = 0; opcode < 0x100; opcode++) {
|
|
464
|
+
const encoding = by_opcode0f[opcode]
|
|
465
|
+
|
|
466
|
+
assert(encoding && encoding.length)
|
|
467
|
+
|
|
468
|
+
const opcode_hex = hex(opcode, 2)
|
|
469
|
+
const opcode_high_hex = hex(opcode | 0x100, 2)
|
|
470
|
+
|
|
471
|
+
if (encoding[0].os) {
|
|
472
|
+
cases0f.push({
|
|
473
|
+
conditions: [`0x${opcode_hex}`],
|
|
474
|
+
body: gen_instruction_body(encoding, 16),
|
|
475
|
+
})
|
|
476
|
+
cases0f.push({
|
|
477
|
+
conditions: [`0x${opcode_high_hex}`],
|
|
478
|
+
body: gen_instruction_body(encoding, 32),
|
|
479
|
+
})
|
|
480
|
+
} else {
|
|
481
|
+
const block: SwitchCase = {
|
|
482
|
+
conditions: [`0x${opcode_hex}`, `0x${opcode_high_hex}`],
|
|
483
|
+
body: gen_instruction_body(encoding, undefined),
|
|
484
|
+
}
|
|
485
|
+
cases0f.push(block)
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const table0f: Statement = {
|
|
490
|
+
type: 'switch',
|
|
491
|
+
condition: 'opcode',
|
|
492
|
+
cases: cases0f,
|
|
493
|
+
default_case: {
|
|
494
|
+
body: ['assert!(false);'],
|
|
495
|
+
},
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (to_generate.interpreter0f) {
|
|
499
|
+
const code: Statement[] = [
|
|
500
|
+
'#![cfg_attr(rustfmt, rustfmt_skip)]',
|
|
501
|
+
|
|
502
|
+
'use crate::cpu::cpu::{after_block_boundary, modrm_resolve};',
|
|
503
|
+
'use crate::cpu::cpu::{read_imm8, read_imm16, read_imm32s};',
|
|
504
|
+
'use crate::cpu::cpu::{task_switch_test, task_switch_test_mmx, trigger_ud};',
|
|
505
|
+
'use crate::cpu::instructions_0f;',
|
|
506
|
+
'use crate::cpu::global_pointers::{instruction_pointer, prefixes};',
|
|
507
|
+
'use crate::prefix;',
|
|
508
|
+
|
|
509
|
+
'pub unsafe fn run(opcode: u32) {',
|
|
510
|
+
table0f,
|
|
511
|
+
'}',
|
|
512
|
+
]
|
|
513
|
+
|
|
514
|
+
finalize_table_rust(
|
|
515
|
+
OUT_DIR,
|
|
516
|
+
'interpreter0f.rs',
|
|
517
|
+
rust_ast
|
|
518
|
+
.print_syntax_tree(([] as Statement[]).concat(code))
|
|
519
|
+
.join('\n') + '\n',
|
|
520
|
+
)
|
|
521
|
+
}
|
|
522
|
+
}
|