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