@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,701 @@
|
|
|
1
|
+
// string operations
|
|
2
|
+
//
|
|
3
|
+
// cmp si di
|
|
4
|
+
// movs 0 1 1/w A4
|
|
5
|
+
// cmps 1 1 1/r A6
|
|
6
|
+
// stos 0 0 1/w AA
|
|
7
|
+
// lods 0 1 0 AC
|
|
8
|
+
// scas 1 0 1/r AE
|
|
9
|
+
// ins 0 0 1/w
|
|
10
|
+
// outs 0 1 0
|
|
11
|
+
|
|
12
|
+
use crate::cpu::arith::{cmp16, cmp32, cmp8};
|
|
13
|
+
use crate::cpu::cpu::{
|
|
14
|
+
get_seg, io_port_read16, io_port_read32, io_port_read8, io_port_write16, io_port_write32,
|
|
15
|
+
io_port_write8, read_reg16, read_reg32, safe_read16, safe_read32s, safe_read8, safe_write16,
|
|
16
|
+
safe_write32, safe_write8, set_reg_asize, test_privileges_for_io, translate_address_read,
|
|
17
|
+
translate_address_write_and_can_skip_dirty, writable_or_pagefault, write_reg16, write_reg32,
|
|
18
|
+
write_reg8, AL, AX, DX, EAX, ECX, EDI, ES, ESI, FLAG_DIRECTION,
|
|
19
|
+
};
|
|
20
|
+
use crate::cpu::global_pointers::{flags, instruction_pointer, previous_ip};
|
|
21
|
+
use crate::cpu::memory;
|
|
22
|
+
use crate::jit;
|
|
23
|
+
use crate::page::Page;
|
|
24
|
+
|
|
25
|
+
fn count_until_end_of_page(direction: i32, size: i32, addr: u32) -> u32 {
|
|
26
|
+
(if direction == 1 {
|
|
27
|
+
(0x1000 - (addr & 0xFFF)) / size as u32
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
(addr & 0xFFF) / size as u32 + 1
|
|
31
|
+
}) as u32
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[derive(Copy, Clone, PartialEq)]
|
|
35
|
+
enum Instruction {
|
|
36
|
+
Movs,
|
|
37
|
+
Lods,
|
|
38
|
+
Stos,
|
|
39
|
+
Scas,
|
|
40
|
+
Cmps,
|
|
41
|
+
Ins,
|
|
42
|
+
Outs,
|
|
43
|
+
}
|
|
44
|
+
#[derive(PartialEq)]
|
|
45
|
+
enum Size {
|
|
46
|
+
B,
|
|
47
|
+
W,
|
|
48
|
+
D,
|
|
49
|
+
}
|
|
50
|
+
#[derive(Copy, Clone)]
|
|
51
|
+
enum Rep {
|
|
52
|
+
None,
|
|
53
|
+
Z,
|
|
54
|
+
NZ,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// We implement all string instructions here and rely on the inliner on doing its job of optimising
|
|
58
|
+
// away anything known at compile time (check with `wasm-dis build/v86.wasm`)
|
|
59
|
+
#[inline(always)]
|
|
60
|
+
unsafe fn string_instruction(
|
|
61
|
+
is_asize_32: bool,
|
|
62
|
+
ds_or_prefix: i32,
|
|
63
|
+
instruction: Instruction,
|
|
64
|
+
size: Size,
|
|
65
|
+
rep: Rep,
|
|
66
|
+
) {
|
|
67
|
+
let asize_mask = if is_asize_32 { -1 } else { 0xFFFF };
|
|
68
|
+
|
|
69
|
+
let direction = if 0 != *flags & FLAG_DIRECTION { -1 } else { 1 };
|
|
70
|
+
|
|
71
|
+
let mut count = match rep {
|
|
72
|
+
Rep::Z | Rep::NZ => {
|
|
73
|
+
let c = (read_reg32(ECX) & asize_mask) as u32;
|
|
74
|
+
if c == 0 {
|
|
75
|
+
return;
|
|
76
|
+
};
|
|
77
|
+
c
|
|
78
|
+
},
|
|
79
|
+
Rep::None => 0,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let es = match instruction {
|
|
83
|
+
Instruction::Movs
|
|
84
|
+
| Instruction::Cmps
|
|
85
|
+
| Instruction::Stos
|
|
86
|
+
| Instruction::Scas
|
|
87
|
+
| Instruction::Ins => return_on_pagefault!(get_seg(ES)),
|
|
88
|
+
_ => 0,
|
|
89
|
+
};
|
|
90
|
+
let ds = match instruction {
|
|
91
|
+
Instruction::Movs
|
|
92
|
+
| Instruction::Cmps
|
|
93
|
+
| Instruction::Lods
|
|
94
|
+
| Instruction::Scas
|
|
95
|
+
| Instruction::Outs => return_on_pagefault!(get_seg(ds_or_prefix)),
|
|
96
|
+
_ => 0,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
let size_bytes = match size {
|
|
100
|
+
Size::B => 1,
|
|
101
|
+
Size::W => 2,
|
|
102
|
+
Size::D => 4,
|
|
103
|
+
};
|
|
104
|
+
let size_mask = match size {
|
|
105
|
+
Size::B => 0xFF,
|
|
106
|
+
Size::W => 0xFFFF,
|
|
107
|
+
Size::D => -1,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
let increment = direction * size_bytes;
|
|
111
|
+
|
|
112
|
+
let data = match instruction {
|
|
113
|
+
Instruction::Stos | Instruction::Scas => read_reg32(EAX),
|
|
114
|
+
_ => 0,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let mut src = match instruction {
|
|
118
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
119
|
+
read_reg32(ESI) & asize_mask
|
|
120
|
+
},
|
|
121
|
+
_ => 0,
|
|
122
|
+
};
|
|
123
|
+
let mut dst = match instruction {
|
|
124
|
+
Instruction::Movs
|
|
125
|
+
| Instruction::Cmps
|
|
126
|
+
| Instruction::Stos
|
|
127
|
+
| Instruction::Scas
|
|
128
|
+
| Instruction::Ins => read_reg32(EDI) & asize_mask,
|
|
129
|
+
_ => 0,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
let port = match instruction {
|
|
133
|
+
Instruction::Ins | Instruction::Outs => {
|
|
134
|
+
let port = read_reg16(DX);
|
|
135
|
+
if !test_privileges_for_io(port, size_bytes) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
port
|
|
139
|
+
},
|
|
140
|
+
_ => 0,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
let is_aligned = (ds + src) & (size_bytes - 1) == 0 && (es + dst) & (size_bytes - 1) == 0;
|
|
144
|
+
|
|
145
|
+
// unaligned movs is properly handled in the fast path
|
|
146
|
+
let mut rep_fast = (instruction == Instruction::Movs || is_aligned)
|
|
147
|
+
&& is_asize_32 // 16-bit address wraparound
|
|
148
|
+
&& match rep {
|
|
149
|
+
Rep::NZ | Rep::Z => true,
|
|
150
|
+
Rep::None => false,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
let mut phys_dst = 0;
|
|
154
|
+
let mut phys_src = 0;
|
|
155
|
+
let mut skip_dirty_page = false;
|
|
156
|
+
|
|
157
|
+
let mut movs_into_svga_lfb = false;
|
|
158
|
+
let mut movs_reenter_fast_path = false;
|
|
159
|
+
|
|
160
|
+
let count_until_end_of_page = if rep_fast {
|
|
161
|
+
match instruction {
|
|
162
|
+
Instruction::Movs => {
|
|
163
|
+
let (addr, skip) =
|
|
164
|
+
return_on_pagefault!(translate_address_write_and_can_skip_dirty(es + dst));
|
|
165
|
+
movs_into_svga_lfb = memory::in_svga_lfb(addr);
|
|
166
|
+
rep_fast = rep_fast && (!memory::in_mapped_range(addr) || movs_into_svga_lfb);
|
|
167
|
+
phys_dst = addr;
|
|
168
|
+
skip_dirty_page = skip;
|
|
169
|
+
},
|
|
170
|
+
Instruction::Stos | Instruction::Ins => {
|
|
171
|
+
let (addr, skip) =
|
|
172
|
+
return_on_pagefault!(translate_address_write_and_can_skip_dirty(es + dst));
|
|
173
|
+
rep_fast = rep_fast && !memory::in_mapped_range(addr);
|
|
174
|
+
phys_dst = addr;
|
|
175
|
+
skip_dirty_page = skip;
|
|
176
|
+
},
|
|
177
|
+
Instruction::Cmps | Instruction::Scas => {
|
|
178
|
+
let addr = return_on_pagefault!(translate_address_read(es + dst));
|
|
179
|
+
rep_fast = rep_fast && !memory::in_mapped_range(addr);
|
|
180
|
+
phys_dst = addr;
|
|
181
|
+
skip_dirty_page = true;
|
|
182
|
+
},
|
|
183
|
+
_ => {},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
match instruction {
|
|
187
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
188
|
+
let addr = return_on_pagefault!(translate_address_read(ds + src));
|
|
189
|
+
rep_fast = rep_fast && !memory::in_mapped_range(addr);
|
|
190
|
+
phys_src = addr;
|
|
191
|
+
},
|
|
192
|
+
_ => {},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
let count_until_end_of_page = u32::min(
|
|
196
|
+
count,
|
|
197
|
+
match instruction {
|
|
198
|
+
Instruction::Movs | Instruction::Cmps => u32::min(
|
|
199
|
+
count_until_end_of_page(direction, size_bytes, phys_src),
|
|
200
|
+
count_until_end_of_page(direction, size_bytes, phys_dst),
|
|
201
|
+
),
|
|
202
|
+
Instruction::Stos | Instruction::Ins | Instruction::Scas => {
|
|
203
|
+
count_until_end_of_page(direction, size_bytes, phys_dst)
|
|
204
|
+
},
|
|
205
|
+
Instruction::Lods | Instruction::Outs => {
|
|
206
|
+
count_until_end_of_page(direction, size_bytes, phys_src)
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
match instruction {
|
|
212
|
+
Instruction::Movs => {
|
|
213
|
+
let c = count_until_end_of_page * size_bytes as u32;
|
|
214
|
+
|
|
215
|
+
let overlap_interferes = if phys_src < phys_dst {
|
|
216
|
+
// backward moves may overlap at the front of the destination string
|
|
217
|
+
phys_dst - phys_src < c && direction == 1
|
|
218
|
+
}
|
|
219
|
+
else if phys_src > phys_dst {
|
|
220
|
+
// forward moves may overlap at the front of the source string
|
|
221
|
+
phys_src - phys_dst < c && direction == -1
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
false
|
|
225
|
+
};
|
|
226
|
+
rep_fast = rep_fast && !overlap_interferes;
|
|
227
|
+
|
|
228
|
+
// In case the following page-boundary check fails, re-enter instruction after
|
|
229
|
+
// one iteration of the slow path
|
|
230
|
+
movs_reenter_fast_path = rep_fast;
|
|
231
|
+
rep_fast = rep_fast
|
|
232
|
+
&& (phys_src & 0xFFF <= 0x1000 - size_bytes as u32)
|
|
233
|
+
&& (phys_dst & 0xFFF <= 0x1000 - size_bytes as u32);
|
|
234
|
+
},
|
|
235
|
+
_ => {},
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
count_until_end_of_page
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
0 // not used
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if rep_fast {
|
|
245
|
+
dbg_assert!(count_until_end_of_page > 0);
|
|
246
|
+
|
|
247
|
+
if !skip_dirty_page {
|
|
248
|
+
jit::jit_dirty_page(Page::page_of(phys_dst));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let mut rep_cmp_finished = false;
|
|
252
|
+
|
|
253
|
+
let mut i = 0;
|
|
254
|
+
while i < count_until_end_of_page {
|
|
255
|
+
i += 1;
|
|
256
|
+
|
|
257
|
+
let src_val = match instruction {
|
|
258
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
259
|
+
match size {
|
|
260
|
+
Size::B => memory::read8_no_mmap_check(phys_src),
|
|
261
|
+
Size::W => memory::read16_no_mmap_check(phys_src),
|
|
262
|
+
Size::D => memory::read32_no_mmap_check(phys_src),
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
Instruction::Scas | Instruction::Stos => data & size_mask,
|
|
266
|
+
Instruction::Ins => match size {
|
|
267
|
+
Size::B => io_port_read8(port),
|
|
268
|
+
Size::W => io_port_read16(port),
|
|
269
|
+
Size::D => io_port_read32(port),
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
let mut dst_val = 0;
|
|
274
|
+
|
|
275
|
+
match instruction {
|
|
276
|
+
Instruction::Cmps | Instruction::Scas => match size {
|
|
277
|
+
Size::B => dst_val = memory::read8_no_mmap_check(phys_dst),
|
|
278
|
+
Size::W => dst_val = memory::read16_no_mmap_check(phys_dst),
|
|
279
|
+
Size::D => dst_val = memory::read32_no_mmap_check(phys_dst),
|
|
280
|
+
},
|
|
281
|
+
Instruction::Outs => match size {
|
|
282
|
+
Size::B => io_port_write8(port, src_val),
|
|
283
|
+
Size::W => io_port_write16(port, src_val),
|
|
284
|
+
Size::D => io_port_write32(port, src_val),
|
|
285
|
+
},
|
|
286
|
+
Instruction::Lods => match size {
|
|
287
|
+
Size::B => write_reg8(AL, src_val),
|
|
288
|
+
Size::W => write_reg16(AX, src_val),
|
|
289
|
+
Size::D => write_reg32(EAX, src_val),
|
|
290
|
+
},
|
|
291
|
+
Instruction::Ins => match size {
|
|
292
|
+
Size::B => memory::write8_no_mmap_or_dirty_check(phys_dst, src_val),
|
|
293
|
+
Size::W => memory::write16_no_mmap_or_dirty_check(phys_dst, src_val),
|
|
294
|
+
Size::D => memory::write32_no_mmap_or_dirty_check(phys_dst, src_val),
|
|
295
|
+
},
|
|
296
|
+
Instruction::Movs => {
|
|
297
|
+
if direction == -1 {
|
|
298
|
+
phys_src -= (count_until_end_of_page - 1) * size_bytes as u32;
|
|
299
|
+
phys_dst -= (count_until_end_of_page - 1) * size_bytes as u32;
|
|
300
|
+
}
|
|
301
|
+
if movs_into_svga_lfb {
|
|
302
|
+
memory::memcpy_into_svga_lfb(
|
|
303
|
+
phys_src,
|
|
304
|
+
phys_dst,
|
|
305
|
+
count_until_end_of_page * size_bytes as u32,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
memory::memcpy_no_mmap_or_dirty_check(
|
|
310
|
+
phys_src,
|
|
311
|
+
phys_dst,
|
|
312
|
+
count_until_end_of_page * size_bytes as u32,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
i = count_until_end_of_page;
|
|
316
|
+
break;
|
|
317
|
+
},
|
|
318
|
+
Instruction::Stos => match size {
|
|
319
|
+
Size::B => {
|
|
320
|
+
if direction == -1 {
|
|
321
|
+
phys_dst -= count_until_end_of_page - 1
|
|
322
|
+
}
|
|
323
|
+
memory::memset_no_mmap_or_dirty_check(
|
|
324
|
+
phys_dst,
|
|
325
|
+
src_val as u8,
|
|
326
|
+
count_until_end_of_page,
|
|
327
|
+
);
|
|
328
|
+
i = count_until_end_of_page;
|
|
329
|
+
break;
|
|
330
|
+
},
|
|
331
|
+
Size::W => memory::write16_no_mmap_or_dirty_check(phys_dst, src_val),
|
|
332
|
+
Size::D => memory::write32_no_mmap_or_dirty_check(phys_dst, src_val),
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
match instruction {
|
|
337
|
+
Instruction::Movs
|
|
338
|
+
| Instruction::Cmps
|
|
339
|
+
| Instruction::Stos
|
|
340
|
+
| Instruction::Scas
|
|
341
|
+
| Instruction::Ins => {
|
|
342
|
+
phys_dst += increment as u32;
|
|
343
|
+
},
|
|
344
|
+
_ => {},
|
|
345
|
+
}
|
|
346
|
+
match instruction {
|
|
347
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
348
|
+
phys_src += increment as u32;
|
|
349
|
+
},
|
|
350
|
+
_ => {},
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
match instruction {
|
|
354
|
+
Instruction::Scas | Instruction::Cmps => {
|
|
355
|
+
let rep_cmp = match rep {
|
|
356
|
+
Rep::Z => src_val == dst_val,
|
|
357
|
+
Rep::NZ => src_val != dst_val,
|
|
358
|
+
Rep::None => {
|
|
359
|
+
dbg_assert!(false);
|
|
360
|
+
true
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
if !rep_cmp || count == i {
|
|
364
|
+
match size {
|
|
365
|
+
Size::B => cmp8(src_val, dst_val),
|
|
366
|
+
Size::W => cmp16(src_val, dst_val),
|
|
367
|
+
Size::D => cmp32(src_val, dst_val),
|
|
368
|
+
};
|
|
369
|
+
rep_cmp_finished = true;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
_ => {},
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
dbg_assert!(i <= count);
|
|
378
|
+
count -= i;
|
|
379
|
+
|
|
380
|
+
if !rep_cmp_finished && count != 0 {
|
|
381
|
+
// go back to the current instruction, since this loop just handles a single page
|
|
382
|
+
*instruction_pointer = *previous_ip;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
src += i as i32 * increment;
|
|
386
|
+
dst += i as i32 * increment;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
loop {
|
|
390
|
+
match instruction {
|
|
391
|
+
Instruction::Ins => {
|
|
392
|
+
// check fault *before* reading from port
|
|
393
|
+
// (technically not necessary according to Intel manuals)
|
|
394
|
+
break_on_pagefault!(writable_or_pagefault(es + dst, size_bytes));
|
|
395
|
+
},
|
|
396
|
+
_ => {},
|
|
397
|
+
};
|
|
398
|
+
let src_val = match instruction {
|
|
399
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
400
|
+
break_on_pagefault!(match size {
|
|
401
|
+
Size::B => safe_read8(ds + src),
|
|
402
|
+
Size::W => safe_read16(ds + src),
|
|
403
|
+
Size::D => safe_read32s(ds + src),
|
|
404
|
+
})
|
|
405
|
+
},
|
|
406
|
+
Instruction::Scas | Instruction::Stos => data & size_mask,
|
|
407
|
+
Instruction::Ins => match size {
|
|
408
|
+
Size::B => io_port_read8(port),
|
|
409
|
+
Size::W => io_port_read16(port),
|
|
410
|
+
Size::D => io_port_read32(port),
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
let mut dst_val = 0;
|
|
415
|
+
|
|
416
|
+
match instruction {
|
|
417
|
+
Instruction::Cmps | Instruction::Scas => match size {
|
|
418
|
+
Size::B => dst_val = break_on_pagefault!(safe_read8(es + dst)),
|
|
419
|
+
Size::W => dst_val = break_on_pagefault!(safe_read16(es + dst)),
|
|
420
|
+
Size::D => dst_val = break_on_pagefault!(safe_read32s(es + dst)),
|
|
421
|
+
},
|
|
422
|
+
Instruction::Outs => match size {
|
|
423
|
+
Size::B => io_port_write8(port, src_val),
|
|
424
|
+
Size::W => io_port_write16(port, src_val),
|
|
425
|
+
Size::D => io_port_write32(port, src_val),
|
|
426
|
+
},
|
|
427
|
+
Instruction::Lods => match size {
|
|
428
|
+
Size::B => write_reg8(AL, src_val),
|
|
429
|
+
Size::W => write_reg16(AX, src_val),
|
|
430
|
+
Size::D => write_reg32(EAX, src_val),
|
|
431
|
+
},
|
|
432
|
+
Instruction::Movs | Instruction::Stos | Instruction::Ins => match size {
|
|
433
|
+
Size::B => break_on_pagefault!(safe_write8(es + dst, src_val)),
|
|
434
|
+
Size::W => break_on_pagefault!(safe_write16(es + dst, src_val)),
|
|
435
|
+
Size::D => break_on_pagefault!(safe_write32(es + dst, src_val)),
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
match instruction {
|
|
440
|
+
Instruction::Movs
|
|
441
|
+
| Instruction::Cmps
|
|
442
|
+
| Instruction::Stos
|
|
443
|
+
| Instruction::Scas
|
|
444
|
+
| Instruction::Ins => dst = dst + increment & asize_mask,
|
|
445
|
+
_ => {},
|
|
446
|
+
}
|
|
447
|
+
match instruction {
|
|
448
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
449
|
+
src = src + increment & asize_mask
|
|
450
|
+
},
|
|
451
|
+
_ => {},
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
count -= 1;
|
|
455
|
+
|
|
456
|
+
let finished = match rep {
|
|
457
|
+
Rep::Z | Rep::NZ => match (rep, instruction) {
|
|
458
|
+
(Rep::Z, Instruction::Cmps) => src_val != dst_val || count == 0,
|
|
459
|
+
(Rep::Z, Instruction::Scas) => src_val != dst_val || count == 0,
|
|
460
|
+
(Rep::NZ, Instruction::Cmps) => src_val == dst_val || count == 0,
|
|
461
|
+
(Rep::NZ, Instruction::Scas) => src_val == dst_val || count == 0,
|
|
462
|
+
(Rep::NZ | Rep::Z, Instruction::Movs) => {
|
|
463
|
+
if count == 0 {
|
|
464
|
+
true
|
|
465
|
+
}
|
|
466
|
+
else if movs_reenter_fast_path {
|
|
467
|
+
*instruction_pointer = *previous_ip;
|
|
468
|
+
true
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
false
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
_ => count == 0,
|
|
475
|
+
},
|
|
476
|
+
Rep::None => true,
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
if finished {
|
|
480
|
+
match instruction {
|
|
481
|
+
Instruction::Scas | Instruction::Cmps => match size {
|
|
482
|
+
Size::B => cmp8(src_val, dst_val),
|
|
483
|
+
Size::W => cmp16(src_val, dst_val),
|
|
484
|
+
Size::D => cmp32(src_val, dst_val),
|
|
485
|
+
},
|
|
486
|
+
_ => {},
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
match instruction {
|
|
494
|
+
Instruction::Movs
|
|
495
|
+
| Instruction::Cmps
|
|
496
|
+
| Instruction::Stos
|
|
497
|
+
| Instruction::Scas
|
|
498
|
+
| Instruction::Ins => set_reg_asize(is_asize_32, EDI, dst),
|
|
499
|
+
_ => {},
|
|
500
|
+
}
|
|
501
|
+
match instruction {
|
|
502
|
+
Instruction::Movs | Instruction::Cmps | Instruction::Lods | Instruction::Outs => {
|
|
503
|
+
set_reg_asize(is_asize_32, ESI, src)
|
|
504
|
+
},
|
|
505
|
+
_ => {},
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
match rep {
|
|
509
|
+
Rep::Z | Rep::NZ => {
|
|
510
|
+
set_reg_asize(is_asize_32, ECX, count as i32);
|
|
511
|
+
},
|
|
512
|
+
Rep::None => {},
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
#[no_mangle]
|
|
517
|
+
pub unsafe fn movsb_rep(is_asize_32: bool, seg: i32) {
|
|
518
|
+
string_instruction(is_asize_32, seg, Instruction::Movs, Size::B, Rep::Z)
|
|
519
|
+
}
|
|
520
|
+
#[no_mangle]
|
|
521
|
+
pub unsafe fn movsw_rep(is_asize_32: bool, seg: i32) {
|
|
522
|
+
string_instruction(is_asize_32, seg, Instruction::Movs, Size::W, Rep::Z)
|
|
523
|
+
}
|
|
524
|
+
#[no_mangle]
|
|
525
|
+
pub unsafe fn movsd_rep(is_asize_32: bool, seg: i32) {
|
|
526
|
+
string_instruction(is_asize_32, seg, Instruction::Movs, Size::D, Rep::Z)
|
|
527
|
+
}
|
|
528
|
+
pub unsafe fn movsb_no_rep(is_asize_32: bool, seg: i32) {
|
|
529
|
+
string_instruction(is_asize_32, seg, Instruction::Movs, Size::B, Rep::None)
|
|
530
|
+
}
|
|
531
|
+
pub unsafe fn movsw_no_rep(is_asize_32: bool, seg: i32) {
|
|
532
|
+
string_instruction(is_asize_32, seg, Instruction::Movs, Size::W, Rep::None)
|
|
533
|
+
}
|
|
534
|
+
pub unsafe fn movsd_no_rep(is_asize_32: bool, seg: i32) {
|
|
535
|
+
string_instruction(is_asize_32, seg, Instruction::Movs, Size::D, Rep::None)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
#[no_mangle]
|
|
539
|
+
pub unsafe fn lodsb_rep(is_asize_32: bool, seg: i32) {
|
|
540
|
+
string_instruction(is_asize_32, seg, Instruction::Lods, Size::B, Rep::Z)
|
|
541
|
+
}
|
|
542
|
+
#[no_mangle]
|
|
543
|
+
pub unsafe fn lodsw_rep(is_asize_32: bool, seg: i32) {
|
|
544
|
+
string_instruction(is_asize_32, seg, Instruction::Lods, Size::W, Rep::Z)
|
|
545
|
+
}
|
|
546
|
+
#[no_mangle]
|
|
547
|
+
pub unsafe fn lodsd_rep(is_asize_32: bool, seg: i32) {
|
|
548
|
+
string_instruction(is_asize_32, seg, Instruction::Lods, Size::D, Rep::Z)
|
|
549
|
+
}
|
|
550
|
+
pub unsafe fn lodsb_no_rep(is_asize_32: bool, seg: i32) {
|
|
551
|
+
string_instruction(is_asize_32, seg, Instruction::Lods, Size::B, Rep::None)
|
|
552
|
+
}
|
|
553
|
+
pub unsafe fn lodsw_no_rep(is_asize_32: bool, seg: i32) {
|
|
554
|
+
string_instruction(is_asize_32, seg, Instruction::Lods, Size::W, Rep::None)
|
|
555
|
+
}
|
|
556
|
+
pub unsafe fn lodsd_no_rep(is_asize_32: bool, seg: i32) {
|
|
557
|
+
string_instruction(is_asize_32, seg, Instruction::Lods, Size::D, Rep::None)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
#[no_mangle]
|
|
561
|
+
pub unsafe fn stosb_rep(is_asize_32: bool) {
|
|
562
|
+
string_instruction(is_asize_32, 0, Instruction::Stos, Size::B, Rep::Z)
|
|
563
|
+
}
|
|
564
|
+
#[no_mangle]
|
|
565
|
+
pub unsafe fn stosw_rep(is_asize_32: bool) {
|
|
566
|
+
string_instruction(is_asize_32, 0, Instruction::Stos, Size::W, Rep::Z)
|
|
567
|
+
}
|
|
568
|
+
#[no_mangle]
|
|
569
|
+
pub unsafe fn stosd_rep(is_asize_32: bool) {
|
|
570
|
+
string_instruction(is_asize_32, 0, Instruction::Stos, Size::D, Rep::Z)
|
|
571
|
+
}
|
|
572
|
+
pub unsafe fn stosb_no_rep(is_asize_32: bool) {
|
|
573
|
+
string_instruction(is_asize_32, 0, Instruction::Stos, Size::B, Rep::None)
|
|
574
|
+
}
|
|
575
|
+
pub unsafe fn stosw_no_rep(is_asize_32: bool) {
|
|
576
|
+
string_instruction(is_asize_32, 0, Instruction::Stos, Size::W, Rep::None)
|
|
577
|
+
}
|
|
578
|
+
pub unsafe fn stosd_no_rep(is_asize_32: bool) {
|
|
579
|
+
string_instruction(is_asize_32, 0, Instruction::Stos, Size::D, Rep::None)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
#[no_mangle]
|
|
583
|
+
pub unsafe fn cmpsb_repz(is_asize_32: bool, seg: i32) {
|
|
584
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::B, Rep::Z)
|
|
585
|
+
}
|
|
586
|
+
#[no_mangle]
|
|
587
|
+
pub unsafe fn cmpsw_repz(is_asize_32: bool, seg: i32) {
|
|
588
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::W, Rep::Z)
|
|
589
|
+
}
|
|
590
|
+
#[no_mangle]
|
|
591
|
+
pub unsafe fn cmpsd_repz(is_asize_32: bool, seg: i32) {
|
|
592
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::D, Rep::Z)
|
|
593
|
+
}
|
|
594
|
+
#[no_mangle]
|
|
595
|
+
pub unsafe fn cmpsb_repnz(is_asize_32: bool, seg: i32) {
|
|
596
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::B, Rep::NZ)
|
|
597
|
+
}
|
|
598
|
+
#[no_mangle]
|
|
599
|
+
pub unsafe fn cmpsw_repnz(is_asize_32: bool, seg: i32) {
|
|
600
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::W, Rep::NZ)
|
|
601
|
+
}
|
|
602
|
+
#[no_mangle]
|
|
603
|
+
pub unsafe fn cmpsd_repnz(is_asize_32: bool, seg: i32) {
|
|
604
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::D, Rep::NZ)
|
|
605
|
+
}
|
|
606
|
+
#[no_mangle]
|
|
607
|
+
pub unsafe fn cmpsb_no_rep(is_asize_32: bool, seg: i32) {
|
|
608
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::B, Rep::None)
|
|
609
|
+
}
|
|
610
|
+
#[no_mangle]
|
|
611
|
+
pub unsafe fn cmpsw_no_rep(is_asize_32: bool, seg: i32) {
|
|
612
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::W, Rep::None)
|
|
613
|
+
}
|
|
614
|
+
#[no_mangle]
|
|
615
|
+
pub unsafe fn cmpsd_no_rep(is_asize_32: bool, seg: i32) {
|
|
616
|
+
string_instruction(is_asize_32, seg, Instruction::Cmps, Size::D, Rep::None)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
#[no_mangle]
|
|
620
|
+
pub unsafe fn scasb_repz(is_asize_32: bool) {
|
|
621
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::B, Rep::Z)
|
|
622
|
+
}
|
|
623
|
+
#[no_mangle]
|
|
624
|
+
pub unsafe fn scasw_repz(is_asize_32: bool) {
|
|
625
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::W, Rep::Z)
|
|
626
|
+
}
|
|
627
|
+
#[no_mangle]
|
|
628
|
+
pub unsafe fn scasd_repz(is_asize_32: bool) {
|
|
629
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::D, Rep::Z)
|
|
630
|
+
}
|
|
631
|
+
#[no_mangle]
|
|
632
|
+
pub unsafe fn scasb_repnz(is_asize_32: bool) {
|
|
633
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::B, Rep::NZ)
|
|
634
|
+
}
|
|
635
|
+
#[no_mangle]
|
|
636
|
+
pub unsafe fn scasw_repnz(is_asize_32: bool) {
|
|
637
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::W, Rep::NZ)
|
|
638
|
+
}
|
|
639
|
+
#[no_mangle]
|
|
640
|
+
pub unsafe fn scasd_repnz(is_asize_32: bool) {
|
|
641
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::D, Rep::NZ)
|
|
642
|
+
}
|
|
643
|
+
pub unsafe fn scasb_no_rep(is_asize_32: bool) {
|
|
644
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::B, Rep::None)
|
|
645
|
+
}
|
|
646
|
+
pub unsafe fn scasw_no_rep(is_asize_32: bool) {
|
|
647
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::W, Rep::None)
|
|
648
|
+
}
|
|
649
|
+
pub unsafe fn scasd_no_rep(is_asize_32: bool) {
|
|
650
|
+
string_instruction(is_asize_32, 0, Instruction::Scas, Size::D, Rep::None)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
#[no_mangle]
|
|
654
|
+
pub unsafe fn outsb_rep(is_asize_32: bool, seg: i32) {
|
|
655
|
+
string_instruction(is_asize_32, seg, Instruction::Outs, Size::B, Rep::Z)
|
|
656
|
+
}
|
|
657
|
+
#[no_mangle]
|
|
658
|
+
pub unsafe fn outsw_rep(is_asize_32: bool, seg: i32) {
|
|
659
|
+
string_instruction(is_asize_32, seg, Instruction::Outs, Size::W, Rep::Z)
|
|
660
|
+
}
|
|
661
|
+
#[no_mangle]
|
|
662
|
+
pub unsafe fn outsd_rep(is_asize_32: bool, seg: i32) {
|
|
663
|
+
string_instruction(is_asize_32, seg, Instruction::Outs, Size::D, Rep::Z)
|
|
664
|
+
}
|
|
665
|
+
#[no_mangle]
|
|
666
|
+
pub unsafe fn outsb_no_rep(is_asize_32: bool, seg: i32) {
|
|
667
|
+
string_instruction(is_asize_32, seg, Instruction::Outs, Size::B, Rep::None)
|
|
668
|
+
}
|
|
669
|
+
#[no_mangle]
|
|
670
|
+
pub unsafe fn outsw_no_rep(is_asize_32: bool, seg: i32) {
|
|
671
|
+
string_instruction(is_asize_32, seg, Instruction::Outs, Size::W, Rep::None)
|
|
672
|
+
}
|
|
673
|
+
#[no_mangle]
|
|
674
|
+
pub unsafe fn outsd_no_rep(is_asize_32: bool, seg: i32) {
|
|
675
|
+
string_instruction(is_asize_32, seg, Instruction::Outs, Size::D, Rep::None)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
#[no_mangle]
|
|
679
|
+
pub unsafe fn insb_rep(is_asize_32: bool) {
|
|
680
|
+
string_instruction(is_asize_32, 0, Instruction::Ins, Size::B, Rep::Z)
|
|
681
|
+
}
|
|
682
|
+
#[no_mangle]
|
|
683
|
+
pub unsafe fn insw_rep(is_asize_32: bool) {
|
|
684
|
+
string_instruction(is_asize_32, 0, Instruction::Ins, Size::W, Rep::Z)
|
|
685
|
+
}
|
|
686
|
+
#[no_mangle]
|
|
687
|
+
pub unsafe fn insd_rep(is_asize_32: bool) {
|
|
688
|
+
string_instruction(is_asize_32, 0, Instruction::Ins, Size::D, Rep::Z)
|
|
689
|
+
}
|
|
690
|
+
#[no_mangle]
|
|
691
|
+
pub unsafe fn insb_no_rep(is_asize_32: bool) {
|
|
692
|
+
string_instruction(is_asize_32, 0, Instruction::Ins, Size::B, Rep::None)
|
|
693
|
+
}
|
|
694
|
+
#[no_mangle]
|
|
695
|
+
pub unsafe fn insw_no_rep(is_asize_32: bool) {
|
|
696
|
+
string_instruction(is_asize_32, 0, Instruction::Ins, Size::W, Rep::None)
|
|
697
|
+
}
|
|
698
|
+
#[no_mangle]
|
|
699
|
+
pub unsafe fn insd_no_rep(is_asize_32: bool) {
|
|
700
|
+
string_instruction(is_asize_32, 0, Instruction::Ins, Size::D, Rep::None)
|
|
701
|
+
}
|