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