Skip to main content

native_v86_core/
native_runtime.rs

1use crate::cpu::{apic, cpu, global_pointers, ioapic, memory, pic};
2use crate::native_devices;
3use std::collections::VecDeque;
4use std::io::Write;
5use std::sync::{Mutex, OnceLock};
6use std::time::Instant;
7
8static START: OnceLock<Instant> = OnceLock::new();
9static UART0: OnceLock<Mutex<UartState>> = OnceLock::new();
10static PS2: OnceLock<Mutex<Ps2State>> = OnceLock::new();
11
12#[derive(Default)]
13struct Ps2State {
14    output: VecDeque<u8>,
15    command_byte: u8,
16    pending_command: u8,
17}
18
19fn ps2() -> &'static Mutex<Ps2State> {
20    PS2.get_or_init(|| {
21        Mutex::new(Ps2State {
22            command_byte: 0x01,
23            ..Ps2State::default()
24        })
25    })
26}
27
28#[derive(Default)]
29struct UartState {
30    ints: u8,
31    baud_rate: u16,
32    line_control: u8,
33    lsr: u8,
34    fifo_control: u8,
35    ier: u8,
36    iir: u8,
37    modem_control: u8,
38    modem_status: u8,
39    scratch: u8,
40    irq: u8,
41    input: VecDeque<u8>,
42}
43
44fn uart0() -> &'static Mutex<UartState> {
45    UART0.get_or_init(|| Mutex::new(UartState::default()))
46}
47
48fn ps2_read(port: i32) -> Option<i32> {
49    let mut controller = ps2().lock().ok()?;
50    match port {
51        0x60 => {
52            let value = controller.output.pop_front().unwrap_or(0);
53            let more = !controller.output.is_empty();
54            drop(controller);
55            unsafe {
56                crate::cpu::cpu::device_lower_irq(1);
57                if more {
58                    crate::cpu::cpu::device_raise_irq(1);
59                }
60            }
61            Some(value as i32)
62        }
63        0x64 => Some(if controller.output.is_empty() { 0 } else { 1 }),
64        _ => None,
65    }
66}
67
68fn ps2_write(port: i32, value: i32) -> bool {
69    let Ok(mut controller) = ps2().lock() else {
70        return false;
71    };
72    match port {
73        0x64 => {
74            controller.pending_command = value as u8;
75            true
76        }
77        0x60 => {
78            if controller.pending_command == 0x60 {
79                controller.command_byte = value as u8;
80            }
81            controller.pending_command = 0;
82            true
83        }
84        _ => false,
85    }
86}
87
88fn keycode_for_ascii(byte: u8) -> Option<(u8, bool)> {
89    let upper = byte.to_ascii_uppercase();
90    let shifted = byte.is_ascii_uppercase();
91    let code = match upper {
92        b'A' => 0x1E,
93        b'B' => 0x30,
94        b'C' => 0x2E,
95        b'D' => 0x20,
96        b'E' => 0x12,
97        b'F' => 0x21,
98        b'G' => 0x22,
99        b'H' => 0x23,
100        b'I' => 0x17,
101        b'J' => 0x24,
102        b'K' => 0x25,
103        b'L' => 0x26,
104        b'M' => 0x32,
105        b'N' => 0x31,
106        b'O' => 0x18,
107        b'P' => 0x19,
108        b'Q' => 0x10,
109        b'R' => 0x13,
110        b'S' => 0x1F,
111        b'T' => 0x14,
112        b'U' => 0x16,
113        b'V' => 0x2F,
114        b'W' => 0x11,
115        b'X' => 0x2D,
116        b'Y' => 0x15,
117        b'Z' => 0x2C,
118        b'1' | b'!' => 0x02,
119        b'2' | b'@' => 0x03,
120        b'3' | b'#' => 0x04,
121        b'4' | b'$' => 0x05,
122        b'5' | b'%' => 0x06,
123        b'6' | b'^' => 0x07,
124        b'7' | b'&' => 0x08,
125        b'8' | b'*' => 0x09,
126        b'9' | b'(' => 0x0A,
127        b'0' | b')' => 0x0B,
128        b'-' | b'_' => 0x0C,
129        b'=' | b'+' => 0x0D,
130        b'[' | b'{' => 0x1A,
131        b']' | b'}' => 0x1B,
132        b';' | b':' => 0x27,
133        b'\'' | b'"' => 0x28,
134        b'`' | b'~' => 0x29,
135        b'\\' | b'|' => 0x2B,
136        b',' | b'<' => 0x33,
137        b'.' | b'>' => 0x34,
138        b'/' | b'?' => 0x35,
139        b' ' => 0x39,
140        b'\n' | b'\r' => 0x1C,
141        b'\t' => 0x0F,
142        8 => 0x0E,
143        _ => return None,
144    };
145    let shifted = shifted
146        || matches!(byte, b'!'..=b'&' | b'('..=b'+' | b':' | b'<'..=b'>' | b'?' | b'@' | b'^' | b'_' | b'{' | b'|' | b'}' | b'~' | b'"');
147    Some((code, shifted))
148}
149
150pub fn inject_keyboard_text(text: &str) -> usize {
151    let Ok(mut controller) = ps2().lock() else {
152        return 0;
153    };
154    let mut count = 0;
155    for byte in text.bytes() {
156        let Some((code, shifted)) = keycode_for_ascii(byte) else {
157            continue;
158        };
159        if shifted {
160            controller.output.push_back(0x2A);
161        }
162        controller.output.push_back(code);
163        controller.output.push_back(code | 0x80);
164        if shifted {
165            controller.output.push_back(0xAA);
166        }
167        count += 1;
168    }
169    drop(controller);
170    if count > 0 {
171        unsafe { crate::cpu::cpu::device_raise_irq(1) };
172    }
173    count
174}
175
176fn uart_read(port: i32) -> i32 {
177    let offset = (port - 0x3F8) as u8;
178    let mut uart = uart0().lock().expect("UART0 mutex poisoned");
179    match offset {
180        0 if uart.line_control & 0x80 != 0 => (uart.baud_rate & 0xFF) as i32,
181        0 => uart.input.pop_front().unwrap_or(0) as i32,
182        1 if uart.line_control & 0x80 != 0 => (uart.baud_rate >> 8) as i32,
183        1 => (uart.ier & 0x0F) as i32,
184        2 => {
185            let fifo = if uart.fifo_control & 1 != 0 { 0xC0 } else { 0 };
186            (uart.iir | fifo) as i32
187        }
188        3 => uart.line_control as i32,
189        4 => uart.modem_control as i32,
190        5 => (uart.lsr | if uart.input.is_empty() { 0 } else { 0x01 }) as i32,
191        6 => uart.modem_status as i32,
192        7 => uart.scratch as i32,
193        _ => 0xFF,
194    }
195}
196
197fn restore_uart_state(state: &[serde_json::Value]) -> Result<(), String> {
198    if state.len() < 11 {
199        return Err(format!(
200            "UART state has {} fields; expected 11",
201            state.len()
202        ));
203    }
204    let mut uart = uart0()
205        .lock()
206        .map_err(|_| "UART0 mutex poisoned".to_owned())?;
207    uart.ints = state[0]
208        .as_i64()
209        .ok_or_else(|| "UART ints is not an integer".to_owned())? as u8;
210    uart.baud_rate = state[1]
211        .as_i64()
212        .ok_or_else(|| "UART baud rate is not an integer".to_owned())? as u16;
213    uart.line_control = state[2]
214        .as_i64()
215        .ok_or_else(|| "UART line control is not an integer".to_owned())?
216        as u8;
217    uart.lsr = state[3]
218        .as_i64()
219        .ok_or_else(|| "UART LSR is not an integer".to_owned())? as u8;
220    uart.fifo_control = state[4]
221        .as_i64()
222        .ok_or_else(|| "UART FIFO control is not an integer".to_owned())?
223        as u8;
224    uart.ier = state[5]
225        .as_i64()
226        .ok_or_else(|| "UART IER is not an integer".to_owned())? as u8;
227    uart.iir = state[6]
228        .as_i64()
229        .ok_or_else(|| "UART IIR is not an integer".to_owned())? as u8;
230    uart.modem_control = state[7]
231        .as_i64()
232        .ok_or_else(|| "UART modem control is not an integer".to_owned())?
233        as u8;
234    uart.modem_status = state[8]
235        .as_i64()
236        .ok_or_else(|| "UART modem status is not an integer".to_owned())?
237        as u8;
238    uart.scratch = state[9]
239        .as_i64()
240        .ok_or_else(|| "UART scratch is not an integer".to_owned())? as u8;
241    uart.irq = state[10]
242        .as_i64()
243        .ok_or_else(|| "UART IRQ is not an integer".to_owned())? as u8;
244    Ok(())
245}
246
247fn uart_write(port: i32, value: i32) {
248    let offset = (port - 0x3F8) as u8;
249    let byte = value as u8;
250    let mut output = None;
251    {
252        let mut uart = uart0().lock().expect("UART0 mutex poisoned");
253        match offset {
254            0 if uart.line_control & 0x80 != 0 => {
255                uart.baud_rate = (uart.baud_rate & 0xFF00) | byte as u16;
256            }
257            0 => output = Some(byte),
258            1 if uart.line_control & 0x80 != 0 => {
259                uart.baud_rate = (uart.baud_rate & 0x00FF) | ((byte as u16) << 8);
260            }
261            1 => uart.ier = byte & 0x0F,
262            2 => uart.fifo_control = byte,
263            3 => uart.line_control = byte,
264            4 => uart.modem_control = byte,
265            7 => uart.scratch = byte,
266            _ => {}
267        }
268    }
269    if let Some(byte) = output {
270        let mut stdout = std::io::stdout().lock();
271        let _ = stdout.write_all(&[byte]);
272        let _ = stdout.flush();
273    }
274}
275
276/// Minimal native host callbacks used by the v86 CPU core.
277/// Device-specific MMIO/port routing is intentionally represented as a small
278/// host surface first; concrete PC devices are added by the outer runtime.
279#[no_mangle]
280pub extern "C" fn cpu_exception_hook(_interrupt: i32) -> bool {
281    false
282}
283
284#[no_mangle]
285pub extern "C" fn microtick() -> f64 {
286    START.get_or_init(Instant::now).elapsed().as_secs_f64() * 1000.0
287}
288
289#[no_mangle]
290pub extern "C" fn run_hardware_timers(_acpi_enabled: bool, _now: f64) -> f64 {
291    0.0
292}
293
294#[no_mangle]
295pub extern "C" fn cpu_event_halt() {}
296
297#[no_mangle]
298pub extern "C" fn stop_idling() {}
299
300#[no_mangle]
301pub extern "C" fn get_rand_int() -> i32 {
302    0x1357_9BDF
303}
304
305#[no_mangle]
306pub extern "C" fn io_port_read8(port: i32) -> i32 {
307    if let Some(value) = ps2_read(port) {
308        value
309    } else if let Some(value) = native_devices::io_read8(port) {
310        value
311    } else if (0x3F8..=0x3FF).contains(&port) {
312        uart_read(port)
313    } else {
314        0xFF
315    }
316}
317
318#[no_mangle]
319pub extern "C" fn io_port_read16(port: i32) -> i32 {
320    native_devices::io_read16(port).unwrap_or(0xFFFF)
321}
322
323#[no_mangle]
324pub extern "C" fn io_port_read32(port: i32) -> i32 {
325    native_devices::io_read32(port).unwrap_or(-1)
326}
327
328#[no_mangle]
329pub extern "C" fn io_port_write8(port: i32, value: i32) {
330    if !ps2_write(port, value)
331        && !native_devices::io_write8(port, value)
332        && (0x3F8..=0x3FF).contains(&port)
333    {
334        uart_write(port, value);
335    }
336}
337
338#[no_mangle]
339pub extern "C" fn io_port_write16(port: i32, value: i32) {
340    if !native_devices::io_write16(port, value) {}
341}
342
343#[no_mangle]
344pub extern "C" fn io_port_write32(port: i32, value: i32) {
345    if !native_devices::io_write32(port, value) {}
346}
347
348#[no_mangle]
349pub extern "C" fn mmap_read8(addr: u32) -> i32 {
350    native_devices::mmio_read8(addr).unwrap_or(0xFF)
351}
352
353#[no_mangle]
354pub extern "C" fn mmap_read32(addr: u32) -> i32 {
355    native_devices::mmio_read32(addr).unwrap_or(-1)
356}
357
358#[no_mangle]
359pub extern "C" fn mmap_write8(addr: u32, value: i32) {
360    let _ = native_devices::mmio_write8(addr, value);
361}
362
363#[no_mangle]
364pub extern "C" fn mmap_write16(addr: u32, value: i32) {
365    let _ = native_devices::mmio_write16(addr, value);
366}
367
368#[no_mangle]
369pub extern "C" fn mmap_write32(addr: u32, value: i32) {
370    let _ = native_devices::mmio_write32(addr, value);
371}
372
373#[no_mangle]
374pub extern "C" fn mmap_write64(_addr: u32, _v0: i32, _v1: i32) {}
375
376#[no_mangle]
377pub extern "C" fn mmap_write128(_addr: u32, _v0: i32, _v1: i32, _v2: i32, _v3: i32) {}
378
379/// Native CPU state arena and guest memory owner.
380///
381/// v86's scalar CPU state uses the first 4 KiB of the arena. The guest RAM is
382/// allocated by the core memory module and addressed with 32-bit guest physical
383/// addresses, matching the original emulator model.
384pub struct NativeCpu {
385    state_arena: Box<[u8; 4096]>,
386    ram_bytes: u32,
387    vga_bytes: u32,
388    last_timer_tick: Instant,
389    screen_width: u32,
390    screen_height: u32,
391    screen_bpp: u32,
392    graphical_mode: bool,
393}
394
395impl NativeCpu {
396    pub fn new(ram_bytes: u32, vga_bytes: u32) -> Self {
397        assert!(ram_bytes > 0, "RAM size must be non-zero");
398        assert!(vga_bytes > 0, "VGA memory size must be non-zero");
399
400        let mut state_arena = Box::new([0u8; 4096]);
401        unsafe {
402            global_pointers::init(state_arena.as_mut_ptr());
403            let _ = memory::allocate_memory(ram_bytes);
404            let _ = memory::svga_allocate_memory(vga_bytes);
405            *global_pointers::memory_size = ram_bytes;
406            memory::vga_memory_size = vga_bytes;
407            cpu::reset_cpu();
408        }
409
410        Self {
411            state_arena,
412            ram_bytes,
413            vga_bytes,
414            last_timer_tick: Instant::now(),
415            screen_width: 80,
416            screen_height: 25,
417            screen_bpp: 0,
418            graphical_mode: false,
419        }
420    }
421
422    pub fn ram_bytes(&self) -> u32 {
423        self.ram_bytes
424    }
425
426    pub fn vga_bytes(&self) -> u32 {
427        self.vga_bytes
428    }
429
430    pub fn step(&mut self, max_instructions: u32) -> u32 {
431        unsafe {
432            let halted = *global_pointers::in_hlt;
433            let timer_due = self.last_timer_tick.elapsed() >= std::time::Duration::from_millis(1);
434            if halted || timer_due {
435                let now = microtick();
436                if *global_pointers::acpi_enabled {
437                    let _ = apic::apic_timer(now);
438                    cpu::handle_irqs();
439                } else {
440                    pic::set_irq(0);
441                    cpu::handle_irqs();
442                    pic::clear_irq(0);
443                    cpu::handle_irqs();
444                }
445                self.last_timer_tick = Instant::now();
446            }
447            cpu::main_loop_native_interpreter(max_instructions)
448        }
449    }
450
451    pub fn read_memory(&self, address: u32, output: &mut [u8]) -> bool {
452        if address.checked_add(output.len() as u32).is_none()
453            || address + output.len() as u32 > self.ram_bytes
454        {
455            return false;
456        }
457        unsafe {
458            output.copy_from_slice(std::slice::from_raw_parts(
459                memory::mem8.add(address as usize),
460                output.len(),
461            ));
462        }
463        true
464    }
465
466    pub fn write_memory(&mut self, address: u32, input: &[u8]) -> bool {
467        if address.checked_add(input.len() as u32).is_none()
468            || address + input.len() as u32 > self.ram_bytes
469        {
470            return false;
471        }
472        unsafe {
473            std::slice::from_raw_parts_mut(memory::mem8.add(address as usize), input.len())
474                .copy_from_slice(input);
475        }
476        true
477    }
478
479    pub fn instruction_pointer(&self) -> u32 {
480        unsafe { *global_pointers::instruction_pointer as u32 }
481    }
482
483    pub fn halted(&self) -> bool {
484        unsafe { *global_pointers::in_hlt }
485    }
486
487    pub fn state_arena(&self) -> &[u8; 4096] {
488        &self.state_arena
489    }
490
491    /// Return the restored SVGA framebuffer as packed RGB bytes.
492    /// The native runtime keeps the framebuffer in the same guest-visible
493    /// backing store used by v86's LFB mapping.
494    pub fn vga_framebuffer_rgb(&self) -> Option<(u32, u32, Vec<u8>)> {
495        if !self.graphical_mode || self.screen_width == 0 || self.screen_height == 0 {
496            return None;
497        }
498        let pixels = (self.screen_width as usize).checked_mul(self.screen_height as usize)?;
499        let mut output = vec![0u8; pixels.checked_mul(3)?];
500        unsafe {
501            if memory::vga_mem8.is_null() || self.screen_bpp != 32 {
502                return None;
503            }
504            let source_len = pixels.checked_mul(4)?;
505            if source_len > self.vga_bytes as usize {
506                return None;
507            }
508            let source = std::slice::from_raw_parts(memory::vga_mem8, source_len);
509            for (index, rgb) in output.chunks_exact_mut(3).enumerate() {
510                let pixel = &source[index * 4..index * 4 + 4];
511                rgb.copy_from_slice(&[pixel[2], pixel[1], pixel[0]]);
512            }
513        }
514        Some((self.screen_width, self.screen_height, output))
515    }
516
517    pub fn set_9p_root(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), String> {
518        native_devices::set_9p_root(path)
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use super::NativeCpu;
525
526    #[test]
527    fn native_interpreter_executes_reset_vector_hlt() {
528        let mut cpu = NativeCpu::new(128 * 1024 * 1024, 8 * 1024 * 1024);
529        assert!(cpu.write_memory(0xFFFF0, &[0xF4]));
530        assert_eq!(cpu.instruction_pointer(), 0xFFFF0);
531        assert_eq!(cpu.step(1), 1);
532        assert!(cpu.halted());
533    }
534}
535
536impl NativeCpu {
537    /// Restore the CPU scalar state and packed RAM representation from the
538    /// decoded v86 state object. Device arrays are intentionally left to the
539    /// outer native device graph, but CPU execution can continue after this
540    /// method completes.
541    pub fn restore_v86_state(
542        &mut self,
543        state: &serde_json::Value,
544        buffers: &[Vec<u8>],
545    ) -> Result<(), String> {
546        let slots = state
547            .as_array()
548            .ok_or_else(|| "v86 state is not an array".to_owned())?;
549
550        let memory_size = scalar(slots, 0)? as u32;
551        if memory_size != self.ram_bytes {
552            return Err(format!(
553                "state RAM is {memory_size} bytes, NativeCpu has {} bytes",
554                self.ram_bytes
555            ));
556        }
557
558        let segment_state = buffer_for(slots, buffers, 1)?;
559        if segment_state.len() != 16 {
560            return Err(format!(
561                "state[1] length {} != expected 16",
562                segment_state.len()
563            ));
564        }
565        unsafe {
566            std::slice::from_raw_parts_mut(global_pointers::segment_is_null as *mut u8, 8)
567                .copy_from_slice(&segment_state[..8]);
568            std::slice::from_raw_parts_mut(global_pointers::segment_access_bytes, 8)
569                .copy_from_slice(&segment_state[8..]);
570        }
571        copy_i32_buffer(slots, buffers, 2, unsafe {
572            std::slice::from_raw_parts_mut(global_pointers::segment_offsets as *mut u8, 32)
573        })?;
574        copy_u32_buffer(slots, buffers, 3, unsafe {
575            std::slice::from_raw_parts_mut(global_pointers::segment_limits as *mut u8, 32)
576        })?;
577
578        unsafe {
579            *global_pointers::memory_size = memory_size;
580            *global_pointers::protected_mode = scalar(slots, 4)? != 0;
581            *global_pointers::idtr_offset = scalar(slots, 5)? as i32;
582            *global_pointers::idtr_size = scalar(slots, 6)? as i32;
583            *global_pointers::gdtr_offset = scalar(slots, 7)? as i32;
584            *global_pointers::gdtr_size = scalar(slots, 8)? as i32;
585        }
586        copy_i32_buffer(slots, buffers, 10, unsafe {
587            std::slice::from_raw_parts_mut(global_pointers::cr as *mut u8, 32)
588        })?;
589        unsafe {
590            *global_pointers::cpl = scalar(slots, 11)? as u8;
591            *global_pointers::is_32 = scalar(slots, 13)? != 0;
592            *global_pointers::stack_size_32 = scalar(slots, 16)? != 0;
593            *global_pointers::in_hlt = scalar(slots, 17)? != 0;
594            *global_pointers::last_virt_eip = scalar(slots, 18)? as i32;
595            *global_pointers::eip_phys = scalar(slots, 19)? as i32;
596            *global_pointers::sysenter_cs = scalar(slots, 22)? as i32;
597            *global_pointers::sysenter_eip = scalar(slots, 23)? as i32;
598            *global_pointers::sysenter_esp = scalar(slots, 24)? as i32;
599            *global_pointers::prefixes = scalar(slots, 25)? as u8;
600            *global_pointers::flags = scalar(slots, 26)? as i32;
601            *global_pointers::flags_changed = scalar(slots, 27)? as i32;
602            *global_pointers::last_op1 = scalar(slots, 28)? as i32;
603            *global_pointers::last_op_size = scalar(slots, 30)? as i32;
604            *global_pointers::instruction_pointer = scalar(slots, 37)? as i32;
605            *global_pointers::previous_ip = scalar(slots, 38)? as i32;
606        }
607        copy_i32_buffer(slots, buffers, 39, unsafe {
608            std::slice::from_raw_parts_mut(global_pointers::reg32 as *mut u8, 32)
609        })?;
610        copy_u16_buffer(slots, buffers, 40, unsafe {
611            std::slice::from_raw_parts_mut(global_pointers::sreg as *mut u8, 16)
612        })?;
613        copy_i32_buffer(slots, buffers, 41, unsafe {
614            std::slice::from_raw_parts_mut(global_pointers::dreg as *mut u8, 32)
615        })?;
616        copy_u64_buffer(slots, buffers, 42, unsafe {
617            std::slice::from_raw_parts_mut(global_pointers::reg_pdpte as *mut u8, 32)
618        })?;
619
620        let tsc = buffer_for(slots, buffers, 43)?;
621        if tsc.len() >= 8 {
622            let low = u32::from_le_bytes(tsc[0..4].try_into().unwrap());
623            let high = u32::from_le_bytes(tsc[4..8].try_into().unwrap());
624            unsafe {
625                cpu::set_tsc(low, high);
626            }
627        }
628
629        if let Some(uart_state) = slots.get(54).and_then(serde_json::Value::as_array) {
630            restore_uart_state(uart_state)?;
631        }
632        if let Some(pic_state) = slots.get(60).and_then(serde_json::Value::as_array) {
633            let master = byte_array_from_state(pic_state, 13, "PIC master")?;
634            let slave_value = pic_state
635                .get(5)
636                .ok_or_else(|| "PIC state has no slave controller".to_owned())?;
637            let slave_array = slave_value
638                .as_array()
639                .ok_or_else(|| "PIC slave state is not an array".to_owned())?;
640            let slave = byte_array_from_values(slave_array, 13, "PIC slave")?;
641            pic::restore_state(&master, &slave);
642        }
643
644        if slots.get(46).is_some_and(|value| !value.is_null()) {
645            let apic_state = buffer_for(slots, buffers, 46)?;
646            apic::restore_state_bytes(apic_state)?;
647            unsafe {
648                *global_pointers::apic_enabled = true;
649                *global_pointers::acpi_enabled = true;
650            }
651        }
652        if slots.get(63).is_some_and(|value| !value.is_null()) {
653            let ioapic_state = buffer_for(slots, buffers, 63)?;
654            ioapic::restore_state_bytes(ioapic_state)?;
655        }
656
657        if let Some(vga_state) = slots.get(52).and_then(serde_json::Value::as_array) {
658            self.screen_width = vga_state
659                .get(15)
660                .and_then(serde_json::Value::as_i64)
661                .unwrap_or(0) as u32;
662            self.screen_height = vga_state
663                .get(16)
664                .and_then(serde_json::Value::as_i64)
665                .unwrap_or(0) as u32;
666            self.screen_bpp = vga_state
667                .get(19)
668                .and_then(serde_json::Value::as_i64)
669                .unwrap_or(0) as u32;
670            self.graphical_mode = vga_state
671                .get(9)
672                .and_then(serde_json::Value::as_bool)
673                .unwrap_or(false);
674            if let Some(value) = vga_state.get(39) {
675                let buffer_id = value
676                    .get("buffer_id")
677                    .and_then(serde_json::Value::as_u64)
678                    .ok_or_else(|| "VGA state[39] is not a typed buffer".to_owned())?
679                    as usize;
680                let svga = buffers
681                    .get(buffer_id)
682                    .ok_or_else(|| format!("VGA buffer id {buffer_id} is out of range"))?;
683                let vga_len = self.vga_bytes as usize;
684                if svga.len() > vga_len {
685                    return Err(format!(
686                        "VGA framebuffer {} exceeds allocated {} bytes",
687                        svga.len(),
688                        vga_len
689                    ));
690                }
691                unsafe {
692                    std::ptr::copy_nonoverlapping(svga.as_ptr(), memory::vga_mem8, svga.len());
693                }
694            }
695        }
696
697        unsafe {
698            *global_pointers::tss_size_32 = scalar(slots, 64)? != 0;
699        }
700        copy_buffer(slots, buffers, 66, unsafe {
701            std::slice::from_raw_parts_mut(global_pointers::reg_xmm as *mut u8, 128)
702        })?;
703        copy_buffer(slots, buffers, 67, unsafe {
704            std::slice::from_raw_parts_mut(global_pointers::fpu_st as *mut u8, 128)
705        })?;
706        unsafe {
707            *global_pointers::fpu_stack_empty = scalar(slots, 68)? as u8;
708            *global_pointers::fpu_stack_ptr = scalar(slots, 69)? as u8;
709            *global_pointers::fpu_control_word = scalar(slots, 70)? as u16;
710            *global_pointers::fpu_ip = scalar(slots, 71)? as i32;
711            *global_pointers::fpu_ip_selector = scalar(slots, 72)? as i32;
712            *global_pointers::fpu_dp = scalar(slots, 73)? as i32;
713            *global_pointers::fpu_dp_selector = scalar(slots, 74)? as i32;
714            *global_pointers::fpu_opcode = scalar(slots, 75)? as i32;
715            *global_pointers::last_result = slots
716                .get(86)
717                .and_then(serde_json::Value::as_i64)
718                .unwrap_or(0) as i32;
719            *global_pointers::fpu_status_word = slots
720                .get(87)
721                .and_then(serde_json::Value::as_i64)
722                .unwrap_or(0) as u16;
723            *global_pointers::mxcsr = slots
724                .get(88)
725                .and_then(serde_json::Value::as_i64)
726                .unwrap_or(0x1F80) as i32;
727        }
728
729        let packed_memory = buffer_for(slots, buffers, 77)?;
730        let bitmap = buffer_for(slots, buffers, 78)?;
731        unsafe {
732            std::ptr::write_bytes(memory::mem8, 0, self.ram_bytes as usize);
733        }
734        let page_count = self.ram_bytes as usize / 0x1000;
735        let mut packed_page = 0usize;
736        for page in 0..page_count {
737            if bitmap
738                .get(page >> 3)
739                .map_or(false, |byte| byte & (1 << (page & 7)) != 0)
740            {
741                let src_start = packed_page * 0x1000;
742                let src_end = src_start + 0x1000;
743                if src_end > packed_memory.len() {
744                    return Err("packed memory buffer is shorter than bitmap population".to_owned());
745                }
746                unsafe {
747                    std::ptr::copy_nonoverlapping(
748                        packed_memory.as_ptr().add(src_start),
749                        memory::mem8.add(page * 0x1000),
750                        0x1000,
751                    );
752                }
753                packed_page += 1;
754            }
755        }
756        if packed_page * 0x1000 != packed_memory.len() {
757            return Err(format!(
758                "packed memory has {} pages but bitmap references {}",
759                packed_memory.len() / 0x1000,
760                packed_page
761            ));
762        }
763
764        native_devices::restore_state(state, buffers)?;
765        cpu::update_state_flags();
766        unsafe {
767            cpu::full_clear_tlb();
768        }
769        Ok(())
770    }
771}
772
773fn buffer_for<'a>(
774    state: &[serde_json::Value],
775    buffers: &'a [Vec<u8>],
776    index: usize,
777) -> Result<&'a [u8], String> {
778    let buffer_id = state
779        .get(index)
780        .and_then(serde_json::Value::as_object)
781        .and_then(|object| object.get("buffer_id"))
782        .and_then(serde_json::Value::as_u64)
783        .ok_or_else(|| format!("state[{index}] is not a typed buffer"))?
784        as usize;
785    buffers
786        .get(buffer_id)
787        .map(Vec::as_slice)
788        .ok_or_else(|| format!("buffer id {buffer_id} is out of range"))
789}
790
791fn byte_array_from_state(
792    state: &[serde_json::Value],
793    len: usize,
794    name: &str,
795) -> Result<[u8; 13], String> {
796    byte_array_from_values(state, len, name)
797}
798
799fn byte_array_from_values(
800    state: &[serde_json::Value],
801    len: usize,
802    name: &str,
803) -> Result<[u8; 13], String> {
804    if len != 13 || state.len() < len {
805        return Err(format!("{name} has {} fields; expected {len}", state.len()));
806    }
807    let mut result = [0u8; 13];
808    for (index, value) in state.iter().take(len).enumerate() {
809        if index == 5 {
810            // v86 stores the slave PIC array at master[5]; Pic0 byte five is
811            // only a legacy dummy slot and is not part of the nested state.
812            continue;
813        }
814        result[index] = value
815            .as_i64()
816            .ok_or_else(|| format!("{name}[{index}] is not an integer"))?
817            as u8;
818    }
819    Ok(result)
820}
821
822fn scalar(state: &[serde_json::Value], index: usize) -> Result<i64, String> {
823    state
824        .get(index)
825        .and_then(serde_json::Value::as_i64)
826        .ok_or_else(|| format!("state[{index}] is not an integer scalar"))
827}
828
829fn copy_buffer(
830    state: &[serde_json::Value],
831    buffers: &[Vec<u8>],
832    index: usize,
833    target: &mut [u8],
834) -> Result<(), String> {
835    let source = buffer_for(state, buffers, index)?;
836    if source.len() != target.len() {
837        return Err(format!(
838            "state[{index}] length {} != expected {}",
839            source.len(),
840            target.len()
841        ));
842    }
843    target.copy_from_slice(source);
844    Ok(())
845}
846
847fn copy_i32_buffer(
848    state: &[serde_json::Value],
849    buffers: &[Vec<u8>],
850    index: usize,
851    target: &mut [u8],
852) -> Result<(), String> {
853    copy_buffer(state, buffers, index, target)
854}
855
856fn copy_u16_buffer(
857    state: &[serde_json::Value],
858    buffers: &[Vec<u8>],
859    index: usize,
860    target: &mut [u8],
861) -> Result<(), String> {
862    copy_buffer(state, buffers, index, target)
863}
864
865fn copy_u32_buffer(
866    state: &[serde_json::Value],
867    buffers: &[Vec<u8>],
868    index: usize,
869    target: &mut [u8],
870) -> Result<(), String> {
871    copy_buffer(state, buffers, index, target)
872}
873
874fn copy_u64_buffer(
875    state: &[serde_json::Value],
876    buffers: &[Vec<u8>],
877    index: usize,
878    target: &mut [u8],
879) -> Result<(), String> {
880    copy_buffer(state, buffers, index, target)
881}
882
883#[cfg(test)]
884mod keyboard_tests {
885    use super::keycode_for_ascii;
886
887    #[test]
888    fn maps_lowercase_without_shift() {
889        assert_eq!(keycode_for_ascii(b'a'), Some((0x1E, false)));
890        assert_eq!(keycode_for_ascii(b'z'), Some((0x2C, false)));
891    }
892
893    #[test]
894    fn maps_uppercase_and_punctuation_with_shift() {
895        assert_eq!(keycode_for_ascii(b'A'), Some((0x1E, true)));
896        assert_eq!(keycode_for_ascii(b'!'), Some((0x02, true)));
897        assert_eq!(keycode_for_ascii(b'_'), Some((0x0C, true)));
898    }
899
900    #[test]
901    fn maps_shell_control_characters() {
902        assert_eq!(keycode_for_ascii(b' '), Some((0x39, false)));
903        assert_eq!(keycode_for_ascii(b'\n'), Some((0x1C, false)));
904        assert_eq!(keycode_for_ascii(b'\t'), Some((0x0F, false)));
905    }
906}