mod bluescreen;
use crate::render::GraphicsRenderState;
use crate::render::RenderControl;
use crate::render::RenderState;
use crate::render::TextRenderState;
use crate::spec;
use crate::spec::CrtControlReg;
use crate::spec::VGA_FUNCTION_SELECT_AND;
use crate::spec::VGA_FUNCTION_SELECT_NORMAL;
use crate::spec::VGA_FUNCTION_SELECT_OR;
use crate::spec::VgaAttribReg;
use crate::spec::VgaGraphicsReg;
use crate::spec::VgaPort;
use crate::spec::VgaSequencerReg;
use chipset_device::io::IoError;
use chipset_device::io::IoResult;
use framebuffer::FramebufferLocalControl;
use guestmem::GuestMemory;
use guestmem::MapRom;
use guestmem::UnmapRom;
use inspect::Inspect;
use memory_range::MemoryRange;
use pci_core::spec::cfg_space::HeaderType00;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Index;
use std::ops::IndexMut;
use vmcore::vmtime::VmTimeAccess;
use vmcore::vmtime::VmTimeSource;
use zerocopy::FromBytes;
use zerocopy::FromZeros;
use zerocopy::Immutable;
use zerocopy::IntoBytes;
use zerocopy::KnownLayout;
#[derive(Inspect)]
pub struct Emulator {
state: VgaAddressingVars,
text: TextModeState,
#[inspect(skip)]
vmtime: VmTimeAccess,
#[inspect(skip)]
control: FramebufferLocalControl,
#[inspect(skip)]
rom: Option<Box<dyn MapRom>>,
#[inspect(skip)]
mapped_rom: Option<Box<dyn UnmapRom>>,
#[inspect(skip)]
vram: GuestMemory,
vram_size: u32,
is_mode_change_pending: bool,
is_full_refresh_pending: bool,
is_delayed_redraw_timer_set: bool,
is_legacy_writes_timer_set: bool,
suppress_initial_activation: bool,
palette_change_count: u32,
pixel_values_palette_count: u32,
#[inspect(skip)]
mapping_table: [u32; 256],
#[inspect(skip)]
render_control: RenderControl,
}
#[derive(Inspect, Clone, PartialEq, Eq)]
pub struct TextModeState {
pub current_text_columns: u16,
pub lo_res_text_mode: bool,
pub mono_text_mode: bool,
pub character_set_512: bool,
pub char_set_1: u8,
pub char_set_2: u8,
pub text_char_width: u8,
pub text_char_height: u8,
pub text_rows: u8,
pub blinking_enabled: bool,
pub blinking_state: bool,
pub draw_text_cursor: bool,
pub cursor_first_scanline: u16,
pub cursor_last_scanline: u16,
pub cursor_row: u16,
pub cursor_col: u16,
pub cursor_blink_state: bool,
}
impl TextModeState {
fn new() -> Self {
Self {
current_text_columns: spec::TOTAL_VGA_HIRES_TEXT_COLUMNS.into(),
lo_res_text_mode: false,
mono_text_mode: false,
character_set_512: false,
char_set_1: 0,
char_set_2: 0,
text_char_width: spec::VGA_HIRES_CHARACTER_WIDTH,
text_char_height: spec::DEFAULT_VGA_CHARACTER_HEIGHT,
text_rows: spec::TOTAL_VGA_NORMAL_TEXT_ROWS,
blinking_enabled: false,
blinking_state: false,
draw_text_cursor: false,
cursor_first_scanline: 0,
cursor_last_scanline: 0,
cursor_row: 0,
cursor_col: 0,
cursor_blink_state: true,
}
}
}
#[derive(Inspect)]
struct VgaAddressingVars {
persistent_state: VideoS3DeviceState,
video_start_bus_range_offset: u64,
video_end_bus_range_offset: u64,
video_window_offset: u32,
chain_4_mode: bool,
odd_even_mode: bool,
dbl_word_mode: bool,
write_mode: u8,
plane_write_mask: u8,
data_rotate_value: u8,
set_reset_value: u8,
set_reset_mask: u8,
function_select: u8,
plane_3_in_use: bool,
plane_write_mask32: u32, set_reset_mask32: u32, set_reset_value32: u32, set_reset_mask_value32: u32, pixel_mask32: u32,
read_mode: u8,
plane_read_num: u8,
color_compare_value: u8, color_dont_care: u8, color_compare_mask32: u32, color_dont_care_mask32: u32, }
impl VgaAddressingVars {
fn new(vram_size: usize) -> Self {
Self {
persistent_state: VideoS3DeviceState::new(vram_size),
video_start_bus_range_offset: 0xa0000,
video_end_bus_range_offset: 0xc0000,
video_window_offset: 0,
chain_4_mode: false,
odd_even_mode: true,
dbl_word_mode: false,
write_mode: 0,
plane_write_mask: 0xf,
data_rotate_value: 0,
set_reset_value: 0,
set_reset_mask: 0,
function_select: 0,
plane_3_in_use: false,
plane_write_mask32: 0,
set_reset_mask32: 0,
set_reset_value32: 0,
set_reset_mask_value32: 0,
pixel_mask32: 0,
read_mode: 0,
plane_read_num: 0,
color_compare_value: 0,
color_dont_care: 0,
color_compare_mask32: 0,
color_dont_care_mask32: 0,
}
}
}
#[derive(Inspect)]
struct VideoS3DeviceState {
crt_control_regs: RegisterSet<CrtControlReg, 256>,
crt_control_regs_shadow: RegisterSet<CrtControlReg, 256>,
vga_sequencer_regs: RegisterSet<VgaSequencerReg, 32>,
vga_sequencer_regs_shadow: RegisterSet<VgaSequencerReg, 32>,
pub vga_attrib_regs: RegisterSet<VgaAttribReg, 32>,
vga_graphics_regs: RegisterSet<VgaGraphicsReg, 16>,
vga_graphics_regs_shadow: RegisterSet<VgaGraphicsReg, 16>,
crt_control_index_reg: CrtControlReg,
vga_seq_index_reg: VgaSequencerReg,
vga_seq_index_reg_shadow: u8,
vga_attrib_reg_index: u8,
vga_attrib_reg_flip_flop: bool,
vga_graphics_reg_index: VgaGraphicsReg,
vga_graphics_reg_index_shadow: u8,
#[inspect(skip)]
pel_colors: [PelColor; 256],
pel_reg_write_index: u16,
pel_reg_read_index: u16,
pel_mask_register: u8,
text_mode: bool,
video_enabled: bool,
misc_output_reg: u8,
crt_regs_locked: bool,
horizontal_retrace: bool,
horizontal_retrace_count: u8,
adj_pcvideo_height: u16,
pcvideo_height: u16,
pcvideo_width: u16,
line_offset_pixels: u16,
line_compare_value: u16,
cur_page_start_offset: u32,
video_pci_status: u32,
interrupt_line_info: u32,
bits_per_pixel: u8,
enhanced_dac_mode: bool,
latched_read_value: u32,
s3: S3ControllerState,
#[inspect(hex)]
expansion_rom_base: u32,
}
impl VideoS3DeviceState {
fn new(vram_size: usize) -> Self {
let crt_control_regs = RegisterSet::new([
(
CrtControlReg::S3_CHIP_REVISION_NUMBER_REGISTER,
spec::S3_TRIO_CHIPSET_REV_NUMBER,
),
(
CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_2_REGISTER,
0xA,
),
(
CrtControlReg::S3_DEVICE_ID_HI_REGISTER,
(spec::PCI_DEVICE_ID >> 8) as u8,
),
(
CrtControlReg::S3_DEVICE_ID_LO_REGISTER,
spec::PCI_DEVICE_ID as u8,
),
(
CrtControlReg::S3_DEVICE_REVISION_REGISTER,
spec::PCI_REVISION,
),
(
CrtControlReg::S3_CONFIGURATION_1_REGISTER,
if vram_size == 0x100000 {
0xC2
} else if vram_size == 0x200000 {
0x82
} else {
0x02
},
),
(CrtControlReg::HORIZONTAL_TOTAL_REGISTER, 0),
(CrtControlReg::HORIZONTAL_DISPLAY_END_REGISTER, 0),
(CrtControlReg::START_HORIZONTAL_BLANK_REGISTER, 0),
(CrtControlReg::END_HORIZONTAL_BLANK_REGISTER, 0),
(CrtControlReg::START_HORIZONTAL_RETRACE_REGISTER, 0),
(CrtControlReg::END_HORIZONTAL_RETRACE_REGISTER, 0),
(CrtControlReg::VERTICAL_TOTAL_REGISTER, 0),
(CrtControlReg::OVERFLOW_REGISTER, 0),
(CrtControlReg::PRESET_ROW_SCAN_REGISTER, 0),
(CrtControlReg::MAX_SCANLINE_REGISTER, 0),
(CrtControlReg::CURSOR_START_REGISTER, 0),
(CrtControlReg::CURSOR_END_REGISTER, 0),
(CrtControlReg::START_ADDRESS_HI_REGISTER, 0),
(CrtControlReg::START_ADDRESS_LO_REGISTER, 0),
(CrtControlReg::CURSOR_LOCATION_HI_REGISTER, 0),
(CrtControlReg::CURSOR_LOCATION_LO_REGISTER, 0),
(CrtControlReg::VERTICAL_RETRACE_HI_REGISTER, 0),
(CrtControlReg::VERTICAL_RETRACE_LO_REGISTER, 0),
(CrtControlReg::VERTICAL_DISPLAY_END_REGISTER, 0),
(CrtControlReg::OFFSET_REGISTER, 0),
(CrtControlReg::UNDERLINE_LOCATION_REGISTER, 0),
(CrtControlReg::START_VERTICAL_BLANK_REGISTER, 0),
(CrtControlReg::END_VERTICAL_BLANK_REGISTER, 0),
(CrtControlReg::MODE_CONTROL_REGISTER, 0),
(CrtControlReg::LINE_COMPARE_REGISTER, 0),
(CrtControlReg::UNSUPPORTED_22_REGISTER, 0),
(CrtControlReg::UNSUPPORTED_24_REGISTER, 0),
(CrtControlReg::S3_MEMORY_CONFIGURATION_REGISTER, 0),
(CrtControlReg::S3_BACKWARD_COMPATIBILITY_1_REGISTER, 0),
(CrtControlReg::S3_BACKWARD_COMPATIBILITY_2_REGISTER, 0),
(CrtControlReg::S3_BACKWARD_COMPATIBILITY_3_REGISTER, 0),
(CrtControlReg::S3_REGISTER_LOCK_REGISTER, 0),
(CrtControlReg::S3_CONFIGURATION_2_REGISTER, 0),
(CrtControlReg::S3_UNLOCK_VGA_REGISTERS_1_REGISTER, 0),
(CrtControlReg::S3_UNLOCK_VGA_REGISTERS_2_REGISTER, 0),
(CrtControlReg::S3_MISC_1_REGISTER, 0),
(CrtControlReg::S3_DATA_TRANSFER_REGISTER, 0),
(CrtControlReg::S3_INTERLACE_START_REGISTER, 0),
(CrtControlReg::S3_SYSTEM_CONFIGURATION_REGISTER, 0),
(CrtControlReg::S3_BIOS_FLAG_REGISTER, 0),
(CrtControlReg::S3_MODE_CONTROL_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MODE_REGISTER, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_1, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_2, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_3, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_4, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_5, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_6, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_7, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_8, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_9, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_10, 0),
(CrtControlReg::S3_HW_CURSOR_DEAD_11, 0),
(CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_1_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_2_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_BIOS_FLAG_1_REGISTER, 0),
(CrtControlReg::S3_MMIO_DEAD_1, 0),
(CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_2_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_DAC_CONTROL_REGISTER, 0),
(CrtControlReg::S3_EXTERNAL_SYNC_CONTROL_1_REGISTER, 0),
(CrtControlReg::S3_EXTERNAL_SYNC_CONTROL_2_REGISTER, 0),
(CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_CONTROL_REGISTER, 0),
(
CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_1_REGISTER,
0,
),
(CrtControlReg::S3_EXTENDED_BIOS_FLAG_2_REGISTER, 0),
(CrtControlReg::S3_GENERAL_OUTPUT_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_HORIZONTAL_OVERFLOW_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_VERTICAL_OVERFLOW_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_BUS_GRANT_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_3_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_4_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_5_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MISC_CONTROL_0_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MISC_CONTROL_1_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_MISC_CONTROL_2_REGISTER, 0),
(CrtControlReg::S3_CONFIGURATION_3_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_3_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_4_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_BIOS_FLAG_3_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_BIOS_FLAG_4_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_BIOS_FLAG_5_REGISTER, 0),
(CrtControlReg::S3_EXTENDED_BIOS_FLAG_6_REGISTER, 0),
(CrtControlReg::CONFIGURATION_4_REGISTER, 0),
(CrtControlReg::CUSTOM_VS_1_REGISTER, 0),
(CrtControlReg::CUSTOM_VS_2_REGISTER, 0),
(CrtControlReg::CUSTOM_VS_BIOS_LOGO_REGISTER, 0),
(CrtControlReg::CUSTOM_VS_GENERAL_EXTENSION_REGISTER, 0),
]);
let vga_sequencer_regs = RegisterSet::new([
(VgaSequencerReg::PLANE_WRITE_MASK_REGISTER, 0xf),
(VgaSequencerReg::RESET_REGISTER, 0),
(VgaSequencerReg::CLOCKING_MODE_REGISTER, 0),
(VgaSequencerReg::CHARACTER_FONT_SELECT_REGISTER, 0),
(VgaSequencerReg::MEMORY_MODE_CONTROL_REGISTER, 0),
(
VgaSequencerReg::UNLOCK_S3_EXTENDED_SEQUENCER_REGISTERS_REGISTER,
0,
),
(VgaSequencerReg::UNSUPPORTED_09_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_0A_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_0B_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_0D_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_10_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_11_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_12_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_13_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_14_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_15_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_16_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_17_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_18_REGISTER, 0),
(VgaSequencerReg::UNSUPPORTED_1C_REGISTER, 0),
]);
let vga_attrib_regs = RegisterSet::new((0..=0x14).map(|n| (VgaAttribReg(n), 0)));
let vga_graphics_regs = RegisterSet::new([
(VgaGraphicsReg::SET_RESET_DATA_REGISTER, 0),
(VgaGraphicsReg::ENABLE_SET_RESET_DATA_REGISTER, 0),
(VgaGraphicsReg::COLOR_COMPARE_REGISTER, 0),
(VgaGraphicsReg::RASTER_OP_ROTATE_COUNT_REGISTER, 0),
(VgaGraphicsReg::READ_PLANE_SELECT_REGISTER, 0),
(VgaGraphicsReg::MODE_REGISTER, 0),
(VgaGraphicsReg::MEMORY_MAP_MODE_CONTROL_REGISTER, 0),
(VgaGraphicsReg::COLOR_DONT_CARE_REGISTER, 0),
(VgaGraphicsReg::BIT_MASK_REGISTER, 0xff),
(VgaGraphicsReg(9), 9),
(VgaGraphicsReg(10), 10),
(VgaGraphicsReg(11), 11),
(VgaGraphicsReg(12), 12),
(VgaGraphicsReg(13), 13),
(VgaGraphicsReg(14), 14),
(VgaGraphicsReg(15), 15),
]);
Self {
crt_control_regs_shadow: crt_control_regs.clone(),
crt_control_regs,
vga_sequencer_regs_shadow: vga_sequencer_regs.clone(),
vga_sequencer_regs,
vga_attrib_regs,
vga_graphics_regs_shadow: vga_graphics_regs.clone(),
vga_graphics_regs,
crt_control_index_reg: CrtControlReg(0),
vga_seq_index_reg: VgaSequencerReg(0),
vga_seq_index_reg_shadow: 0,
vga_attrib_reg_index: 0,
vga_attrib_reg_flip_flop: true,
vga_graphics_reg_index: VgaGraphicsReg(0),
vga_graphics_reg_index_shadow: 0,
pel_colors: FromZeros::new_zeroed(),
pel_reg_write_index: 0,
pel_reg_read_index: 0,
pel_mask_register: 0xFF,
text_mode: true,
video_enabled: true,
misc_output_reg: spec::DEFAULT_MISC_OUTPUT_REG_VALUE,
crt_regs_locked: false,
horizontal_retrace: false,
horizontal_retrace_count: 0,
adj_pcvideo_height: 400,
pcvideo_height: 400,
pcvideo_width: spec::MAX_VGA_PIXELS_PER_ROW,
line_offset_pixels: 0,
line_compare_value: 0,
cur_page_start_offset: 0,
video_pci_status: 0,
interrupt_line_info: 0xFF,
bits_per_pixel: 4,
enhanced_dac_mode: false,
latched_read_value: 0,
s3: S3ControllerState::new(),
expansion_rom_base: 0,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
struct PelColor {
red: u8,
green: u8,
blue: u8,
}
#[derive(Inspect)]
struct S3ControllerState {
enhanced_mode: bool,
linear_mapping: bool,
#[inspect(hex)]
addr_window_size: u32,
#[inspect(hex)]
addr_window_base: u32,
addr_window_offset: u32,
#[inspect(hex)]
linear_addr_window: u32,
bits_per_pixel: u16,
adv_function_control_reg: u16,
screen_pixel_width: u16,
}
impl S3ControllerState {
fn new() -> Self {
Self {
enhanced_mode: false,
linear_mapping: false,
addr_window_size: 0,
addr_window_base: 0,
addr_window_offset: 0,
linear_addr_window: 0xa0000,
bits_per_pixel: 0,
adv_function_control_reg: 0,
screen_pixel_width: 1024,
}
}
}
#[derive(Clone)]
pub struct RegisterSet<T, const N: usize>([u8; N], PhantomData<fn(T)>);
impl<T: From<u8> + Debug, const N: usize> Inspect for RegisterSet<T, N> {
fn inspect(&self, req: inspect::Request<'_>) {
let mut resp = req.respond();
for (i, v) in self.0.iter().enumerate() {
resp.hex(&format!("{:#x?}", T::from(i as u8)), v);
}
}
}
impl<T: Into<u8>, const N: usize> RegisterSet<T, N> {
fn new(values: impl IntoIterator<Item = (T, u8)>) -> Self {
let mut this = Self([0x80; N], PhantomData);
for (x, y) in values {
this[x] = y;
}
this
}
}
impl<T: Into<u8>, const N: usize> Index<T> for RegisterSet<T, N> {
type Output = u8;
fn index(&self, index: T) -> &Self::Output {
&self.0[index.into() as usize]
}
}
impl<T: Into<u8>, const N: usize> IndexMut<T> for RegisterSet<T, N> {
fn index_mut(&mut self, index: T) -> &mut Self::Output {
&mut self.0[index.into() as usize]
}
}
const SPLASH_SCREEN_BYTES_PER_PIXEL: u8 = 2;
const _SPLASH_SCREEN_WIDTH: u16 = 640;
const _SPLASH_SCREEN_HEIGHT: u16 = 400;
fn expand_mask(template: u8) -> u32 {
let mut mask = 0;
if template & 0x1 != 0 {
mask |= make_mask_for_byte(0);
}
if template & 0x2 != 0 {
mask |= make_mask_for_byte(1);
}
if template & 0x4 != 0 {
mask |= make_mask_for_byte(2);
}
if template & 0x8 != 0 {
mask |= make_mask_for_byte(3);
}
mask
}
fn make_mask_for_byte(byte_number: u32) -> u32 {
0xFF << (byte_number * 8)
}
impl Emulator {
pub fn reset(&mut self) {
self.state = VgaAddressingVars::new(self.vram_size as usize);
self.control.unmap();
self.mapped_rom = None;
self.update_render_state();
}
pub fn new(
control: FramebufferLocalControl,
vram: GuestMemory,
vmtime: &VmTimeSource,
rom: Option<Box<dyn MapRom>>,
render_control: RenderControl,
) -> Self {
let vram_size = control.len();
Self {
state: VgaAddressingVars::new(vram_size),
text: TextModeState::new(),
vmtime: vmtime.access("vga"),
control,
vram,
rom,
mapped_rom: None,
vram_size: vram_size as u32,
is_mode_change_pending: false,
is_full_refresh_pending: false,
is_delayed_redraw_timer_set: false,
is_legacy_writes_timer_set: false,
suppress_initial_activation: false,
palette_change_count: 1,
pixel_values_palette_count: 0,
mapping_table: [0; 256],
render_control,
}
}
pub fn notify_pci_config_access_write(
&mut self,
in_reg_address: u16,
io_data: u32,
) -> IoResult {
tracing::trace!(
reg = ?HeaderType00(in_reg_address),
io_data,
"pci config write"
);
match HeaderType00(in_reg_address) {
HeaderType00::STATUS_COMMAND => {
self.state.persistent_state.video_pci_status = io_data & 0x07FF;
}
HeaderType00::BIST_HEADER => return IoResult::Err(IoError::InvalidRegister),
HeaderType00::BAR0 => {
self.s3_set_linear_address_base(io_data as u64 & 0xFC000000); }
HeaderType00::BAR1
| HeaderType00::BAR2
| HeaderType00::BAR3
| HeaderType00::BAR4
| HeaderType00::BAR5 => return IoResult::Err(IoError::InvalidRegister),
HeaderType00::LATENCY_INTERRUPT => {
self.state.persistent_state.interrupt_line_info = io_data;
}
HeaderType00::EXPANSION_ROM_BASE => {
if let Some(rom) = &self.rom {
let reg = io_data & 0xFFFF0001;
if reg != self.state.persistent_state.expansion_rom_base {
self.state.persistent_state.expansion_rom_base = reg;
self.mapped_rom = None;
if reg & 1 != 0 {
match rom.map_rom((reg & !1).into(), 0, rom.len()) {
Ok(mapping) => self.mapped_rom = Some(mapping),
Err(err) => {
tracing::error!(
error = &err as &dyn std::error::Error,
"failed to map expansion ROM"
);
}
}
}
}
}
}
reg => {
tracing::warn!(?reg, data = io_data, "unhandled vga config space write");
return IoResult::Err(IoError::InvalidRegister);
}
}
IoResult::Ok
}
pub fn notify_pci_config_access_read(
&self,
in_reg_address: u16,
io_data: &mut u32,
) -> IoResult {
tracing::trace!(
reg = ?HeaderType00(in_reg_address),
"pci config read"
);
*io_data = match HeaderType00(in_reg_address) {
HeaderType00::DEVICE_VENDOR => {
spec::PCI_VENDOR_ID as u32 | ((spec::PCI_DEVICE_ID as u32) << 16)
}
HeaderType00::STATUS_COMMAND => self.state.persistent_state.video_pci_status,
HeaderType00::CLASS_REVISION => {
((spec::PCI_VIDEO_CLASS_CODE as u32) << 24) | (spec::PCI_REVISION as u32)
}
HeaderType00::BIST_HEADER => {
0
}
HeaderType00::SUBSYSTEM_ID => spec::PCI_SUBSYSTEM.into(),
HeaderType00::BAR0 => self.state.persistent_state.s3.linear_addr_window & 0xFC000000,
HeaderType00::BAR1
| HeaderType00::BAR2
| HeaderType00::BAR3
| HeaderType00::BAR4
| HeaderType00::BAR5 => {
return IoResult::Err(IoError::InvalidRegister);
}
HeaderType00::LATENCY_INTERRUPT => {
let pci_irq_lane_a = 1;
(pci_irq_lane_a << 8) | (self.state.persistent_state.interrupt_line_info & 0xFF)
}
HeaderType00::EXPANSION_ROM_BASE => {
self.state.persistent_state.expansion_rom_base & 0xFFFF0001
}
reg => {
tracing::warn!(?reg, "unhandled vga config space read");
return IoResult::Err(IoError::InvalidRegister);
}
};
tracing::trace!(
reg = ?HeaderType00(in_reg_address),
io_data,
"pci config read finished"
);
IoResult::Ok
}
pub fn notify_mmio_read(&mut self, address: u64, data: &mut [u8]) {
if (address >= self.state.video_start_bus_range_offset)
&& (address < self.state.video_end_bus_range_offset)
{
let start = (address - self.state.video_start_bus_range_offset) as u32;
match self.state.read_mode {
spec::VGA_READ_MODE_0 => {
for (offset, byte) in data.iter_mut().enumerate().rev() {
*byte = self.handle_vgaread0(start + offset as u32);
}
}
spec::VGA_READ_MODE_1 => {
for (offset, byte) in data.iter_mut().enumerate().rev() {
*byte = self.handle_vgaread1(start + offset as u32);
}
}
_ => unreachable!(),
}
} else {
data.fill(!0);
}
}
pub fn notify_mmio_write(&mut self, address: u64, data: &[u8]) {
if (address >= self.state.video_start_bus_range_offset)
&& (address < self.state.video_end_bus_range_offset)
{
let start = (address - self.state.video_start_bus_range_offset) as u32;
match self.state.write_mode {
spec::VGA_WRITE_MODE_0 => {
for (offset, &byte) in data.iter().enumerate().rev() {
self.handle_vgawrite0(start + offset as u32, byte);
}
}
spec::VGA_WRITE_MODE_1 => {
for (offset, _) in data.iter().enumerate().rev() {
self.handle_vgawrite1(start + offset as u32);
}
}
spec::VGA_WRITE_MODE_2 => {
for (offset, &byte) in data.iter().enumerate().rev() {
self.handle_vgawrite2(start + offset as u32, byte);
}
}
spec::VGA_WRITE_MODE_3 => {
for (offset, &byte) in data.iter().enumerate().rev() {
self.handle_vgawrite3(start + offset as u32, byte);
}
}
_ => {}
}
}
self.update_render_state();
}
fn adjust_vga_address(
&self,
io_vga_addr: &mut u32,
io_plane_mask: &mut u32,
io_plane_to_read: &mut u32,
) -> bool {
let mut vga_addr = *io_vga_addr;
let mut plane_mask = *io_plane_mask;
let mut read_plane = *io_plane_to_read;
let mut within_vram = true;
vga_addr += self.state.video_window_offset;
if !self.state.persistent_state.s3.enhanced_mode {
if self.state.chain_4_mode {
read_plane = vga_addr & 0x3;
plane_mask = make_mask_for_byte(read_plane);
vga_addr &= !0x3;
} else if self.state.odd_even_mode {
if (vga_addr & 1) == 0 {
plane_mask &= make_mask_for_byte(0) | make_mask_for_byte(2);
} else {
plane_mask &= make_mask_for_byte(1) | make_mask_for_byte(3);
}
read_plane = vga_addr & 1;
vga_addr &= !0x1;
}
vga_addr <<= 2;
}
let vram_size = self.vram_size;
if vga_addr > (vram_size - 4) {
within_vram = false;
}
*io_vga_addr = vga_addr;
*io_plane_mask = plane_mask;
*io_plane_to_read = read_plane;
within_vram
}
fn read_vram<T: IntoBytes + FromBytes + Immutable + KnownLayout>(&self, address: u32) -> T {
self.vram
.read_plain(address.into())
.expect("framebuffer is mapped")
}
fn write_vram<T: IntoBytes + Immutable + KnownLayout>(&self, address: u32, value: T) {
self.vram
.write_plain(address.into(), &value)
.expect("framebuffer is mapped")
}
fn handle_vgawrite0(&mut self, vga_addr: u32, write_data: u8) {
let mut plane_to_read = 0;
let mut vga_addr = vga_addr;
let mut plane_mask = self.state.plane_write_mask32;
let latched_value = self.state.persistent_state.latched_read_value;
let pixel_mask = self.state.pixel_mask32;
let function = self.state.function_select;
if self.adjust_vga_address(&mut vga_addr, &mut plane_mask, &mut plane_to_read) {
let mut new_value = (write_data as u32)
| ((write_data as u32) << 8)
| ((write_data as u32) << 16)
| ((write_data as u32) << 24);
if self.state.data_rotate_value != 0 {
new_value = (new_value << self.state.data_rotate_value)
| (new_value >> (32 - self.state.data_rotate_value));
}
new_value &= !self.state.set_reset_mask32;
new_value |= self.state.set_reset_mask_value32;
if function != VGA_FUNCTION_SELECT_NORMAL {
if function == VGA_FUNCTION_SELECT_AND {
new_value &= latched_value;
} else if function == VGA_FUNCTION_SELECT_OR {
new_value |= latched_value;
} else {
new_value ^= latched_value;
}
}
let old_value: u32 = self.read_vram(vga_addr);
new_value = (new_value & pixel_mask) | (latched_value & !pixel_mask);
new_value = (new_value & plane_mask) | (old_value & !plane_mask);
self.write_vram(vga_addr, new_value);
}
}
fn handle_vgawrite1(&mut self, vga_addr: u32) {
let mut vga_addr = vga_addr;
let mut plane_to_read = 0;
let mut plane_mask = self.state.plane_write_mask32;
if self.adjust_vga_address(&mut vga_addr, &mut plane_mask, &mut plane_to_read) {
let old_value: u32 = self.read_vram(vga_addr);
let mut new_value = self.state.persistent_state.latched_read_value & plane_mask;
new_value |= old_value & !plane_mask;
self.write_vram(vga_addr, new_value);
}
}
fn handle_vgawrite2(&mut self, vga_addr: u32, write_data: u8) {
let mut vga_addr = vga_addr;
let mut plane_mask = self.state.plane_write_mask32;
let latched_value = self.state.persistent_state.latched_read_value;
let pixel_mask = self.state.pixel_mask32;
let mut plane_to_read = 0;
if self.adjust_vga_address(&mut vga_addr, &mut plane_mask, &mut plane_to_read) {
let mut new_value = expand_mask(write_data);
if self.state.function_select != VGA_FUNCTION_SELECT_NORMAL {
if self.state.function_select == VGA_FUNCTION_SELECT_AND {
new_value &= latched_value;
} else if self.state.function_select == VGA_FUNCTION_SELECT_OR {
new_value |= latched_value;
} else {
new_value ^= latched_value;
}
}
let old_value: u32 = self.read_vram(vga_addr);
new_value = (new_value & pixel_mask) | (latched_value & !pixel_mask);
new_value = (new_value & plane_mask) | (old_value & !plane_mask);
self.write_vram(vga_addr, new_value);
}
}
fn handle_vgawrite3(&mut self, vga_addr: u32, write_data: u8) {
let mut vga_addr = vga_addr;
let mut plane_mask = self.state.plane_write_mask32;
let latched_value = self.state.persistent_state.latched_read_value;
let mut pixel_mask = self.state.pixel_mask32;
let function = self.state.function_select;
let mut plane_to_read = 0;
if self.adjust_vga_address(&mut vga_addr, &mut plane_mask, &mut plane_to_read) {
let v = write_data as u32;
let mut new_value = v | (v << 8) | (v << 16) | (v << 24);
if self.state.data_rotate_value != 0 {
new_value = (new_value << self.state.data_rotate_value)
| (new_value >> (32 - self.state.data_rotate_value));
}
pixel_mask &= new_value;
new_value = self.state.set_reset_value32;
if function != VGA_FUNCTION_SELECT_NORMAL {
if function == VGA_FUNCTION_SELECT_AND {
new_value &= latched_value;
} else if function == VGA_FUNCTION_SELECT_OR {
new_value |= latched_value;
} else {
new_value ^= latched_value;
}
}
let old_value: u32 = self.read_vram(vga_addr);
new_value = (new_value & pixel_mask) | (latched_value & !pixel_mask);
new_value = (new_value & plane_mask) | (old_value & !plane_mask);
self.write_vram(vga_addr, new_value);
}
}
fn handle_vgaread0(&mut self, vga_addr: u32) -> u8 {
let mut plane_to_access = self.state.plane_read_num as u32;
let mut vga_addr = vga_addr;
let mut plane_mask = 0;
if self.adjust_vga_address(&mut vga_addr, &mut plane_mask, &mut plane_to_access) {
self.state.persistent_state.latched_read_value = self.read_vram(vga_addr);
self.read_vram(vga_addr + plane_to_access)
} else {
0xFF
}
}
fn handle_vgaread1(&mut self, vga_addr: u32) -> u8 {
let mut vga_addr = vga_addr;
let mut plane_mask = 0;
let mut plane_to_access = 0;
if self.adjust_vga_address(&mut vga_addr, &mut plane_mask, &mut plane_to_access) {
let color_compare_mask = self.state.color_compare_mask32;
let color_dont_care_mask = self.state.color_dont_care_mask32;
let cur_value = self.read_vram(vga_addr);
self.state.persistent_state.latched_read_value = cur_value;
let mut compare_value = !(color_compare_mask ^ cur_value);
compare_value |= color_dont_care_mask;
(compare_value & (compare_value >> 8) & (compare_value >> 16) & (compare_value >> 24))
as u8
} else {
0xFF
}
}
pub fn io_port_read(&mut self, address: u16, _access_size: u16) -> u32 {
match address & 0xFFF0 {
_ if address == spec::S3_ADV_FUNCTION_CONTROL_PORT => self
.state
.persistent_state
.s3
.adv_function_control_reg
.into(),
spec::VGA_HARDWARE_PORT_RANGE => match VgaPort(address) {
VgaPort::INDEX_DATA_REG_ATTR_PORT | VgaPort::ATTRIBUTE_READ_PORT => {
self.attribute_io_read(VgaPort(address)).into()
}
VgaPort::SEQ_INDEX_REGISTER_PORT | VgaPort::SEQ_DATA_REGISTER_PORT => {
self.sequencer_io_read(VgaPort(address)).into()
}
VgaPort::GRAPHICS_INDEX_REG_PORT | VgaPort::GRAPHICS_DATA_REG_PORT => {
self.graphic_controller_io_read(VgaPort(address)).into()
}
address => self.read_vga_port(address).into(),
},
spec::MDA_HARDWARE_PORT_RANGE => self
.read_cga(spec::CGA_HARDWARE_PORT_RANGE | (address & 0xF))
.into(),
spec::CGA_HARDWARE_PORT_RANGE => self.read_cga(address).into(),
_ => !0,
}
}
pub fn io_port_write(&mut self, address: u16, access_size: u16, data: u32) {
match address & 0xFFF0 {
_ if address == spec::S3_ADV_FUNCTION_CONTROL_PORT => {
self.state.persistent_state.s3.adv_function_control_reg = data as u16;
self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_MISC_CONTROL_1_REGISTER] &= !0x01;
self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_MISC_CONTROL_1_REGISTER] |= data as u8 & 0x01;
self.calculate_graphics_mode_variables();
}
spec::VGA_HARDWARE_PORT_RANGE => match VgaPort(address) {
VgaPort::INDEX_DATA_REG_ATTR_PORT | VgaPort::ATTRIBUTE_READ_PORT => {
self.attribute_io_write(VgaPort(address), data)
}
VgaPort::SEQ_INDEX_REGISTER_PORT | VgaPort::SEQ_DATA_REGISTER_PORT => {
self.sequencer_io_write(VgaPort(address), access_size, data)
}
VgaPort::GRAPHICS_INDEX_REG_PORT | VgaPort::GRAPHICS_DATA_REG_PORT => {
self.graphic_controller_io_write(VgaPort(address), access_size, data)
}
address => self.write_vga_port(address, data as u8),
},
spec::MDA_HARDWARE_PORT_RANGE => self.write_cga(
spec::CGA_HARDWARE_PORT_RANGE | (address & 0xF),
&data.to_ne_bytes()[..access_size as usize],
),
spec::CGA_HARDWARE_PORT_RANGE => {
self.write_cga(address, &data.to_ne_bytes()[..access_size as usize])
}
_ => {}
}
self.update_render_state();
}
fn read_cga(&mut self, port: u16) -> u8 {
match port {
spec::CGA_INDEX_REGISTER_PORT => self.state.persistent_state.crt_control_index_reg.0,
spec::CGA_DATA_REGISTER_PORT => match self.state.persistent_state.crt_control_index_reg
{
CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_3_REGISTER
| CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_4_REGISTER => {
self.state.persistent_state.crt_control_regs_shadow
[self.state.persistent_state.crt_control_index_reg]
}
reg => self.state.persistent_state.crt_control_regs[reg],
},
spec::CGA_INPUT_STATUS_REG1_PORT => {
self.state.persistent_state.vga_attrib_reg_flip_flop = true;
let mut val = 0xF4;
if self.is_vertical_retrace_active() {
val |= 0x9;
}
if self.state.persistent_state.horizontal_retrace {
val |= 0x1;
}
self.state.persistent_state.horizontal_retrace_count += 1;
if self.state.persistent_state.horizontal_retrace_count > 10 {
self.state.persistent_state.horizontal_retrace_count = 0;
self.state.persistent_state.horizontal_retrace =
!self.state.persistent_state.horizontal_retrace;
}
val
}
spec::CGA_MODE_CONTROL_REGISTER_PORT => 0x50,
spec::CGA_UNKNOWN_PORT_3D3 => 0x80,
spec::CGA_UNKNOWN_PORT_3D6 => 0x80,
spec::CGA_UNKNOWN_PORT_3D7 => 0x80,
spec::CGA_UNKNOWN_PORT_3DB => 0x80,
spec::CGA_UNKNOWN_PORT_3DC => 0x80,
spec::CGA_UNKNOWN_PORT_3DD => 0x80,
spec::CGA_UNKNOWN_PORT_3DE => 0x80,
spec::CGA_UNKNOWN_PORT_3DF => 0x80,
_ => 0xff,
}
}
fn write_cga(&mut self, port: u16, data: &[u8]) {
for (&value, port) in data.iter().zip(port..) {
match port {
spec::CGA_INDEX_REGISTER_PORT => {
self.state.persistent_state.crt_control_index_reg = CrtControlReg(value)
}
spec::CGA_DATA_REGISTER_PORT => self.write_crt_control_register(
self.state.persistent_state.crt_control_index_reg,
value,
),
_ => {}
}
}
}
fn attribute_io_read(&mut self, io_address: VgaPort) -> u8 {
if io_address == VgaPort::INDEX_DATA_REG_ATTR_PORT {
self.state.persistent_state.vga_attrib_reg_index
} else {
let reg_address = self.state.persistent_state.vga_attrib_reg_index & 0x1F;
self.state.persistent_state.vga_attrib_regs[VgaAttribReg(reg_address)]
}
}
fn attribute_io_write(&mut self, io_address: VgaPort, write_data: u32) {
if io_address == VgaPort::INDEX_DATA_REG_ATTR_PORT {
if self.state.persistent_state.vga_attrib_reg_flip_flop {
self.state.persistent_state.vga_attrib_reg_index = write_data as u8;
} else {
self.write_vga_attribute_reg(
VgaAttribReg(self.state.persistent_state.vga_attrib_reg_index & 0x1F),
write_data as u8,
);
}
self.state.persistent_state.vga_attrib_reg_flip_flop =
!self.state.persistent_state.vga_attrib_reg_flip_flop;
}
}
fn sequencer_io_read(&mut self, io_address: VgaPort) -> u8 {
if io_address == VgaPort::SEQ_INDEX_REGISTER_PORT {
self.state.persistent_state.vga_seq_index_reg_shadow
} else {
let reg_address = self.state.persistent_state.vga_seq_index_reg;
if reg_address == VgaSequencerReg::PLANE_WRITE_MASK_REGISTER {
self.state.persistent_state.vga_sequencer_regs_shadow[reg_address]
} else {
self.state.persistent_state.vga_sequencer_regs[reg_address]
}
}
}
fn sequencer_io_write(&mut self, io_address: VgaPort, access_size: u16, write_data: u32) {
if io_address == VgaPort::SEQ_INDEX_REGISTER_PORT {
self.state.persistent_state.vga_seq_index_reg =
VgaSequencerReg(write_data as u8 & 0x1F);
self.state.persistent_state.vga_seq_index_reg_shadow = write_data as u8;
if access_size > 1 {
self.write_vga_sequence_reg(
self.state.persistent_state.vga_seq_index_reg,
(write_data >> 8) as u8,
);
}
} else {
self.write_vga_sequence_reg(
self.state.persistent_state.vga_seq_index_reg,
write_data as u8,
);
}
}
fn graphic_controller_io_read(&mut self, io_address: VgaPort) -> u8 {
if io_address == VgaPort::GRAPHICS_INDEX_REG_PORT {
self.state.persistent_state.vga_graphics_reg_index_shadow
} else {
let reg_address = self.state.persistent_state.vga_graphics_reg_index;
match reg_address {
VgaGraphicsReg::SET_RESET_DATA_REGISTER
| VgaGraphicsReg::ENABLE_SET_RESET_DATA_REGISTER
| VgaGraphicsReg::COLOR_COMPARE_REGISTER
| VgaGraphicsReg::READ_PLANE_SELECT_REGISTER
| VgaGraphicsReg::COLOR_DONT_CARE_REGISTER => {
self.state.persistent_state.vga_graphics_regs_shadow[reg_address]
}
_ => self.state.persistent_state.vga_graphics_regs[reg_address],
}
}
}
fn graphic_controller_io_write(
&mut self,
io_address: VgaPort,
access_size: u16,
write_data: u32,
) {
if io_address == VgaPort::GRAPHICS_INDEX_REG_PORT {
self.state.persistent_state.vga_graphics_reg_index =
VgaGraphicsReg(write_data as u8 & 0xF);
self.state.persistent_state.vga_graphics_reg_index_shadow = write_data as u8;
if access_size > 1 {
self.write_vga_graphics_control_reg(
self.state.persistent_state.vga_graphics_reg_index,
(write_data >> 8) as u8,
);
}
} else {
self.write_vga_graphics_control_reg(
self.state.persistent_state.vga_graphics_reg_index,
(write_data) as u8,
);
}
}
fn read_vga_port(&mut self, port_num: VgaPort) -> u8 {
let data_read;
match port_num {
VgaPort::INDEX_DATA_REG_ATTR_PORT
| VgaPort::ATTRIBUTE_READ_PORT
| VgaPort::SEQ_INDEX_REGISTER_PORT
| VgaPort::SEQ_DATA_REGISTER_PORT
| VgaPort::GRAPHICS_INDEX_REG_PORT
| VgaPort::GRAPHICS_DATA_REG_PORT => {
tracing::warn!("unexpected");
data_read = 0xFF;
}
VgaPort::MISC_OUTPUT_READ_PORT => {
data_read = self.state.persistent_state.misc_output_reg;
}
VgaPort::INPUT_STATUS_REG0_PORT => {
data_read = if self.is_vertical_retrace_active() {
0x80
} else {
0
};
}
VgaPort::FEATURE_CONTROL_REG_PORT => {
data_read = 0x00;
}
VgaPort::PEL_MASK_REGISTER_PORT => {
data_read = self.state.persistent_state.pel_mask_register;
}
VgaPort::PEL_ADDRESS_WRITE_REGISTER_PORT => {
if (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_DAC_CONTROL_REGISTER]
& 0x4)
!= 0
{
tracing::warn!("unexpected dac state");
}
data_read = (self.state.persistent_state.pel_reg_write_index / 3) as u8;
}
VgaPort::DAC_STATUS_REGISTER_PORT => {
data_read = 0;
}
VgaPort::PEL_DATA_REGISTER_PORT => {
let reg_address = (self.state.persistent_state.pel_reg_read_index / 3) as usize;
let pel_entry = self.state.persistent_state.pel_colors[reg_address];
let reg_address =
self.state.persistent_state.pel_reg_read_index - reg_address as u16 * 3;
if reg_address == 0 {
data_read = pel_entry.red;
} else if reg_address == 1 {
data_read = pel_entry.green;
} else {
data_read = pel_entry.blue;
}
self.state.persistent_state.pel_reg_read_index += 1;
if self.state.persistent_state.pel_reg_read_index as usize
>= 3 * self.state.persistent_state.pel_colors.len()
{
self.state.persistent_state.pel_reg_read_index = 0;
}
}
VgaPort::SUBSYSTEM_ENABLE_PORT => {
data_read = 0x80;
}
_ => {
data_read = 0xFF;
}
}
data_read
}
fn write_vga_port(&mut self, port_num: VgaPort, data_to_write: u8) {
match port_num {
VgaPort::INDEX_DATA_REG_ATTR_PORT
| VgaPort::SEQ_INDEX_REGISTER_PORT
| VgaPort::SEQ_DATA_REGISTER_PORT
| VgaPort::GRAPHICS_INDEX_REG_PORT
| VgaPort::GRAPHICS_DATA_REG_PORT => {
tracing::warn!(?port_num, "unexpected vga write");
}
VgaPort::MISC_OUTPUT_WRITE_PORT => {
self.state.persistent_state.misc_output_reg = data_to_write;
self.calculate_monitor_timing();
}
VgaPort::SUBSYSTEM_ENABLE_PORT => {
}
VgaPort::GRAPHICS_POS_REGISTER2_PORT | VgaPort::GRAPHICS_POS_REGISTER1_PORT => {
}
VgaPort::PEL_ADDRESS_WRITE_REGISTER_PORT => {
self.state.persistent_state.pel_reg_write_index = data_to_write as u16 * 3;
}
VgaPort::PEL_ADDRESS_READ_REGISTER_PORT => {
self.state.persistent_state.pel_reg_read_index = data_to_write as u16 * 3;
}
VgaPort::PEL_DATA_REGISTER_PORT => {
self.write_pel_data_register(data_to_write);
}
VgaPort::PEL_MASK_REGISTER_PORT => {
self.state.persistent_state.pel_mask_register = data_to_write;
}
_ => {
}
}
}
fn is_vertical_retrace_active(&self) -> bool {
let period = 1e6 / 600.; let time = self.vmtime.now();
let us = (time.as_100ns() / 10) as f64;
let time_into_cycle = us % period;
time_into_cycle < 0.04 * period
}
fn write_crt_control_register(&mut self, reg: CrtControlReg, mut value: u8) {
tracing::trace!(?reg, value, "write crt control register");
let old_value = self.state.persistent_state.crt_control_regs[reg];
if old_value == value && reg != CrtControlReg::CUSTOM_VS_GENERAL_EXTENSION_REGISTER {
return;
}
if self.state.persistent_state.crt_regs_locked {
#[expect(clippy::comparison_chain)]
if reg == CrtControlReg::OVERFLOW_REGISTER {
value &= 0x10;
value |= self.state.persistent_state.crt_control_regs[reg] & !0x10;
} else if reg < CrtControlReg::OVERFLOW_REGISTER {
return;
}
}
self.state.persistent_state.crt_control_regs[reg] = value;
match reg {
CrtControlReg::OVERFLOW_REGISTER
| CrtControlReg::MAX_SCANLINE_REGISTER
| CrtControlReg::LINE_COMPARE_REGISTER => {
self.calculate_graphics_mode_variables();
self.calculate_monitor_timing();
self.calculate_text_mode_variables();
}
CrtControlReg::START_ADDRESS_HI_REGISTER | CrtControlReg::START_ADDRESS_LO_REGISTER => {
self.calculate_page_offset();
}
CrtControlReg::OFFSET_REGISTER => {
self.calculate_line_offset_pixels();
}
CrtControlReg::UNDERLINE_LOCATION_REGISTER => {
self.state.dbl_word_mode = (value & spec::CRT_UNDERLINE_MODE_DWMASK) != 0;
self.update_access_vars();
}
CrtControlReg::MODE_CONTROL_REGISTER => {
self.calculate_graphics_mode_variables();
}
CrtControlReg::CURSOR_START_REGISTER
| CrtControlReg::CURSOR_END_REGISTER
| CrtControlReg::CURSOR_LOCATION_HI_REGISTER
| CrtControlReg::CURSOR_LOCATION_LO_REGISTER => {
self.calculate_text_cursor_variables();
}
CrtControlReg::START_HORIZONTAL_BLANK_REGISTER => {
self.calculate_text_mode_variables();
}
CrtControlReg::HORIZONTAL_TOTAL_REGISTER
| CrtControlReg::END_HORIZONTAL_BLANK_REGISTER
| CrtControlReg::START_HORIZONTAL_RETRACE_REGISTER
| CrtControlReg::END_HORIZONTAL_RETRACE_REGISTER
| CrtControlReg::VERTICAL_TOTAL_REGISTER
| CrtControlReg::PRESET_ROW_SCAN_REGISTER
| CrtControlReg::VERTICAL_RETRACE_HI_REGISTER
| CrtControlReg::END_VERTICAL_BLANK_REGISTER => {
}
CrtControlReg::VERTICAL_RETRACE_LO_REGISTER => {
self.state.persistent_state.crt_regs_locked = value & 0x80 != 0;
}
CrtControlReg::HORIZONTAL_DISPLAY_END_REGISTER
| CrtControlReg::VERTICAL_DISPLAY_END_REGISTER
| CrtControlReg::START_VERTICAL_BLANK_REGISTER => {
self.calculate_monitor_timing();
}
CrtControlReg::S3_DEVICE_ID_HI_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] =
(spec::PCI_DEVICE_ID >> 8) as u8;
}
CrtControlReg::S3_DEVICE_ID_LO_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] = spec::PCI_DEVICE_ID as u8;
}
CrtControlReg::S3_DEVICE_REVISION_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] = spec::PCI_REVISION;
}
CrtControlReg::S3_CHIP_REVISION_NUMBER_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] =
spec::S3_TRIO_CHIPSET_REV_NUMBER;
}
CrtControlReg::S3_MEMORY_CONFIGURATION_REGISTER => {
self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_3_REGISTER] = 0;
self.update_s3_state();
}
CrtControlReg::S3_BACKWARD_COMPATIBILITY_1_REGISTER => {
tracing::warn!("unexpected register access");
}
CrtControlReg::S3_BACKWARD_COMPATIBILITY_2_REGISTER => {
if old_value & 0xD7 != value & 0xD7 {
tracing::warn!("unexpected register access");
}
}
CrtControlReg::S3_REGISTER_LOCK_REGISTER => {
if self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_UNLOCK_VGA_REGISTERS_1_REGISTER]
& 0xCC
== 0x48
{
self.update_access_vars();
} else {
self.state.persistent_state.crt_control_regs[reg] = old_value;
}
}
CrtControlReg::S3_UNLOCK_VGA_REGISTERS_1_REGISTER => {
}
CrtControlReg::S3_UNLOCK_VGA_REGISTERS_2_REGISTER => {}
CrtControlReg::S3_CONFIGURATION_1_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] =
(value & 0xFC) | (old_value & 0x3);
self.update_s3_state();
}
CrtControlReg::S3_CONFIGURATION_2_REGISTER => self.update_s3_state(),
CrtControlReg::S3_BACKWARD_COMPATIBILITY_3_REGISTER
| CrtControlReg::S3_MISC_1_REGISTER
| CrtControlReg::S3_DATA_TRANSFER_REGISTER
| CrtControlReg::S3_INTERLACE_START_REGISTER
| CrtControlReg::S3_EXTENDED_BIOS_FLAG_1_REGISTER
| CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_2_REGISTER
| CrtControlReg::S3_EXTERNAL_SYNC_CONTROL_1_REGISTER
| CrtControlReg::S3_EXTERNAL_SYNC_CONTROL_2_REGISTER => {
}
CrtControlReg::S3_SYSTEM_CONFIGURATION_REGISTER => {
}
CrtControlReg::S3_MODE_CONTROL_REGISTER => self.calculate_monitor_timing(),
CrtControlReg::S3_EXTENDED_MODE_REGISTER => self.calculate_line_offset_pixels(),
CrtControlReg::S3_HW_CURSOR_DEAD_1
| CrtControlReg::S3_HW_CURSOR_DEAD_2
| CrtControlReg::S3_HW_CURSOR_DEAD_3
| CrtControlReg::S3_HW_CURSOR_DEAD_4
| CrtControlReg::S3_HW_CURSOR_DEAD_5
| CrtControlReg::S3_HW_CURSOR_DEAD_6
| CrtControlReg::S3_HW_CURSOR_DEAD_7
| CrtControlReg::S3_HW_CURSOR_DEAD_8
| CrtControlReg::S3_HW_CURSOR_DEAD_9
| CrtControlReg::S3_HW_CURSOR_DEAD_10
| CrtControlReg::S3_HW_CURSOR_DEAD_11 => {
tracing::warn!("hw cursor accessed");
}
CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_1_REGISTER => {
self.update_s3_state();
}
CrtControlReg::S3_GENERAL_OUTPUT_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] &= 0xF0;
self.state.persistent_state.crt_control_regs[reg] |= old_value & 0x0F;
}
CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_CONTROL_REGISTER => {
self.s3_setup_linear_address_window();
self.update_access_vars();
}
CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_2_REGISTER => {
self.calculate_page_offset();
self.update_access_vars();
self.calculate_line_offset_pixels();
}
CrtControlReg::S3_EXTENDED_DAC_CONTROL_REGISTER => {
}
CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_1_REGISTER
| CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_2_REGISTER => {
let new_base = (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_2_REGISTER]
as u64
| ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_1_REGISTER]
as u64)
<< 8))
* 64
* 1024;
self.s3_set_linear_address_base(new_base);
}
CrtControlReg::S3_EXTENDED_BIOS_FLAG_2_REGISTER
| CrtControlReg::S3_EXTENDED_BUS_GRANT_REGISTER => {
tracing::warn!(?reg, value, "unexpected access");
}
CrtControlReg::S3_EXTENDED_VERTICAL_OVERFLOW_REGISTER
| CrtControlReg::S3_EXTENDED_HORIZONTAL_OVERFLOW_REGISTER => {
self.calculate_monitor_timing();
}
CrtControlReg::S3_BIOS_FLAG_REGISTER
| CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_3_REGISTER
| CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_5_REGISTER
| CrtControlReg::S3_EXTENDED_MISC_CONTROL_0_REGISTER
| CrtControlReg::S3_CONFIGURATION_3_REGISTER
| CrtControlReg::S3_EXTENDED_BIOS_FLAG_3_REGISTER
| CrtControlReg::S3_EXTENDED_BIOS_FLAG_4_REGISTER
| CrtControlReg::S3_EXTENDED_BIOS_FLAG_5_REGISTER
| CrtControlReg::S3_EXTENDED_BIOS_FLAG_6_REGISTER
| CrtControlReg::CONFIGURATION_4_REGISTER => {
}
CrtControlReg::S3_EXTENDED_MEMORY_CONTROL_4_REGISTER => {
if value & 0x60 != 0 {
tracing::warn!(?reg, value, "unexpected bit");
}
self.state.persistent_state.crt_control_regs[reg] &= 0x60;
}
CrtControlReg::S3_EXTENDED_MISC_CONTROL_1_REGISTER => {
self.state.persistent_state.s3.adv_function_control_reg &= !0x0001;
self.state.persistent_state.s3.adv_function_control_reg |= value as u16 & 0x01;
self.calculate_graphics_mode_variables();
}
CrtControlReg::S3_EXTENDED_MISC_CONTROL_2_REGISTER => {
self.calculate_graphics_mode_variables();
self.calculate_monitor_timing();
}
CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_3_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] &= 0x3F;
self.state.persistent_state.crt_control_regs_shadow[reg] = value;
self.calculate_page_offset();
}
CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_4_REGISTER => {
self.state.persistent_state.crt_control_regs[reg] &= 0x3F;
self.state.persistent_state.crt_control_regs_shadow[reg] = value;
self.update_access_vars();
}
CrtControlReg::CUSTOM_VS_1_REGISTER | CrtControlReg::CUSTOM_VS_2_REGISTER => {
self.update_s3_state();
}
CrtControlReg::CUSTOM_VS_BIOS_LOGO_REGISTER => {
self.record_video_mode_change();
self.force_screen_redraw();
}
CrtControlReg::CUSTOM_VS_GENERAL_EXTENSION_REGISTER => {
self.handle_general_extension_register_write(value);
}
_ => {}
}
}
fn handle_general_extension_register_write(&mut self, requested_operation: u8) {
match requested_operation {
spec::BIOS_CLEAR_SCREEN_CODE => {
self.vram
.fill_at(0, 0, self.vram_size as usize)
.expect("framebuffer is mapped");
}
spec::SYNTHVID_BLUE_SCREEN_CODE => {
self.do_blue_screen_mode_change();
self.suppress_initial_activation = false;
self.request_activation();
}
_ => {
}
}
}
fn write_vga_sequence_reg(&mut self, reg_index: VgaSequencerReg, new_value: u8) {
let old_reg_value = self.state.persistent_state.vga_sequencer_regs[reg_index];
if old_reg_value != new_value {
self.state.persistent_state.vga_sequencer_regs[reg_index] = new_value;
match reg_index {
VgaSequencerReg::RESET_REGISTER => {
}
VgaSequencerReg::CLOCKING_MODE_REGISTER => {
if (old_reg_value ^ new_value) & 0x20 != 0 {
self.state.persistent_state.video_enabled = (0x20 & new_value) == 0;
self.force_screen_redraw();
}
if (old_reg_value ^ new_value) & 0x08 != 0 {
self.calculate_monitor_timing();
}
}
VgaSequencerReg::PLANE_WRITE_MASK_REGISTER => {
self.state.persistent_state.vga_sequencer_regs_shadow[reg_index] = new_value;
let masked = new_value & 0xF;
self.state.persistent_state.vga_sequencer_regs[reg_index] = masked;
if self.state.plane_write_mask != masked {
self.state.plane_write_mask = masked;
self.update_access_vars();
}
}
VgaSequencerReg::CHARACTER_FONT_SELECT_REGISTER => {
self.calculate_text_mode_variables();
}
VgaSequencerReg::MEMORY_MODE_CONTROL_REGISTER => {
self.state.chain_4_mode = (new_value & spec::SEQ_MEM_MODE_CHAIN4_MASK) != 0;
self.state.odd_even_mode = (new_value & spec::SEQ_MODE_ODD_EVEN_MASK) == 0;
self.update_access_vars();
self.calculate_graphics_mode_variables();
self.calculate_monitor_timing();
}
VgaSequencerReg::UNLOCK_S3_EXTENDED_SEQUENCER_REGISTERS_REGISTER => {
}
VgaSequencerReg::UNSUPPORTED_09_REGISTER
| VgaSequencerReg::UNSUPPORTED_0A_REGISTER
| VgaSequencerReg::UNSUPPORTED_0B_REGISTER
| VgaSequencerReg::UNSUPPORTED_0D_REGISTER
| VgaSequencerReg::UNSUPPORTED_10_REGISTER
| VgaSequencerReg::UNSUPPORTED_11_REGISTER
| VgaSequencerReg::UNSUPPORTED_12_REGISTER
| VgaSequencerReg::UNSUPPORTED_13_REGISTER
| VgaSequencerReg::UNSUPPORTED_14_REGISTER
| VgaSequencerReg::UNSUPPORTED_15_REGISTER
| VgaSequencerReg::UNSUPPORTED_16_REGISTER
| VgaSequencerReg::UNSUPPORTED_17_REGISTER
| VgaSequencerReg::UNSUPPORTED_18_REGISTER
| VgaSequencerReg::UNSUPPORTED_1C_REGISTER => {}
_ => {
}
}
}
}
fn write_vga_attribute_reg(&mut self, attrib_reg: VgaAttribReg, new_value: u8) {
let old_value = self.state.persistent_state.vga_attrib_regs[attrib_reg];
if old_value != new_value {
self.state.persistent_state.vga_attrib_regs[attrib_reg] = new_value;
if (attrib_reg >= VgaAttribReg::PALETTE_0_REGISTER)
&& (attrib_reg <= VgaAttribReg::PALETTE_F_REGISTER)
{
self.mark_palette_dirty();
} else {
match attrib_reg {
VgaAttribReg::MODE_CONTROL_REGISTER => {
let was_text_mode = self.state.persistent_state.text_mode;
self.state.persistent_state.text_mode =
(new_value & spec::ATTRIBUTE_CONTROLLER_MODE_CONTROL_AG_MASK) == 0;
self.force_screen_redraw();
if was_text_mode != self.state.persistent_state.text_mode {
self.record_video_mode_change();
self.mark_palette_dirty();
}
if self.state.persistent_state.text_mode {
self.calculate_text_mode_variables();
}
}
VgaAttribReg::COLOR_PLANE_ENABLE_REGISTER
| VgaAttribReg::PIXEL_PADDING_REGISTER => {
self.mark_palette_dirty();
self.calculate_graphics_mode_variables();
}
VgaAttribReg::HORIZONTAL_PIXEL_PANNING_REGISTER => {
self.force_screen_redraw();
}
VgaAttribReg::VGA_EXTENSION_REGISTER_16 => {
self.state.persistent_state.vga_attrib_regs[attrib_reg] &= !(1 << 4);
}
_ => {}
}
}
}
}
fn write_vga_graphics_control_reg(&mut self, graphics_reg: VgaGraphicsReg, new_value: u8) {
let old_value = self.state.persistent_state.vga_graphics_regs[graphics_reg];
if old_value != new_value {
self.state.persistent_state.vga_graphics_regs[graphics_reg] = new_value;
match graphics_reg {
VgaGraphicsReg::SET_RESET_DATA_REGISTER => {
let masked = new_value & 0xF;
self.state.set_reset_value = masked;
self.state.persistent_state.vga_graphics_regs[graphics_reg] = masked;
self.state.persistent_state.vga_graphics_regs_shadow[graphics_reg] = new_value;
self.update_access_vars();
}
VgaGraphicsReg::ENABLE_SET_RESET_DATA_REGISTER => {
let masked = new_value & 0xF;
self.state.set_reset_mask = masked;
self.state.persistent_state.vga_graphics_regs[graphics_reg] = masked;
self.state.persistent_state.vga_graphics_regs_shadow[graphics_reg] = new_value;
self.update_access_vars();
}
VgaGraphicsReg::COLOR_COMPARE_REGISTER => {
let masked = new_value & 0xF;
self.state.color_compare_value = masked;
self.state.persistent_state.vga_graphics_regs[graphics_reg] = masked;
self.state.persistent_state.vga_graphics_regs_shadow[graphics_reg] = new_value;
self.state.color_compare_mask32 = expand_mask(self.state.color_compare_value);
}
VgaGraphicsReg::RASTER_OP_ROTATE_COUNT_REGISTER => {
self.state.data_rotate_value = new_value & 0x7;
if self.state.data_rotate_value != 0 {
self.state.data_rotate_value = 8 - self.state.data_rotate_value;
}
self.state.function_select = (new_value >> 3) & 0x3;
self.update_access_vars();
}
VgaGraphicsReg::READ_PLANE_SELECT_REGISTER => {
let masked = new_value & 0x3;
self.state.plane_read_num = masked;
self.state.persistent_state.vga_graphics_regs[graphics_reg] = masked;
self.state.persistent_state.vga_graphics_regs_shadow[graphics_reg] = new_value;
}
VgaGraphicsReg::MODE_REGISTER => {
self.state.write_mode = new_value & 0x3;
self.state.read_mode = (new_value >> 3) & 0x1;
if ((new_value ^ old_value) & 0x60) != 0 {
self.calculate_graphics_mode_variables();
self.calculate_monitor_timing();
}
}
VgaGraphicsReg::MEMORY_MAP_MODE_CONTROL_REGISTER => {
self.calculate_vga_address_range();
self.update_access_vars();
}
VgaGraphicsReg::COLOR_DONT_CARE_REGISTER => {
let masked = new_value & 0xF;
self.state.color_dont_care = masked;
self.state.persistent_state.vga_graphics_regs[graphics_reg] = masked;
self.state.persistent_state.vga_graphics_regs_shadow[graphics_reg] = new_value;
self.state.color_dont_care_mask32 = !expand_mask(self.state.color_dont_care);
}
VgaGraphicsReg::BIT_MASK_REGISTER => {
let v = new_value as u32;
self.state.pixel_mask32 = v | (v << 8) | (v << 16) | (v << 24);
}
_ => {}
}
}
}
fn update_access_vars(&mut self) {
assert_eq!(
self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_4_REGISTER]
& !0x3F,
0
);
let mut new_video_offset = self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_4_REGISTER]
as u32;
if new_video_offset == 0 || !self.state.persistent_state.s3.enhanced_mode {
new_video_offset = (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_REGISTER_LOCK_REGISTER] as u32
& 0xF)
| ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_2_REGISTER]
as u32
& 0xC)
<< 2);
}
new_video_offset <<= 16;
if self.state.video_window_offset != new_video_offset {
self.state.video_window_offset = new_video_offset;
self.s3_setup_linear_address_window();
}
if self.state.persistent_state.s3.enhanced_mode {
self.state.dbl_word_mode = false;
} else {
self.state.dbl_word_mode = (self.state.persistent_state.crt_control_regs
[CrtControlReg::UNDERLINE_LOCATION_REGISTER]
& spec::CRT_UNDERLINE_MODE_DWMASK)
!= 0;
let pixel_mask = self.state.persistent_state.vga_graphics_regs
[VgaGraphicsReg::BIT_MASK_REGISTER] as u32;
self.state.pixel_mask32 =
pixel_mask | (pixel_mask << 8) | (pixel_mask << 16) | (pixel_mask << 24);
self.state.plane_write_mask32 = expand_mask(self.state.plane_write_mask);
self.state.plane_3_in_use = (self.state.plane_write_mask32 & 0xFF000000) != 0;
self.state.set_reset_mask32 = expand_mask(self.state.set_reset_mask);
self.state.set_reset_value32 = expand_mask(self.state.set_reset_value);
self.state.set_reset_mask_value32 =
self.state.set_reset_value32 & self.state.set_reset_mask32;
}
}
fn s3_set_linear_address_base(&mut self, new_base: u64) {
let converted_base = (new_base / (64 * 1024)) as u32;
if new_base != self.state.persistent_state.s3.linear_addr_window as u64 {
self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_2_REGISTER] =
converted_base as u8;
self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_POSITION_1_REGISTER] =
(converted_base >> 8) as u8;
self.state.persistent_state.s3.linear_addr_window = new_base as u32;
self.s3_setup_linear_address_window();
}
}
fn s3_setup_linear_address_window(&mut self) {
let use_linear_addr = (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_CONTROL_REGISTER]
& 0x10)
!= 0;
let window_size_code = self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_LINEAR_ADDRESS_WINDOW_CONTROL_REGISTER]
& 0x3;
let mut window_size = if window_size_code == 0 {
64 * 1024
} else {
(1 << (window_size_code - 1)) * (1024 * 1024)
};
if window_size > self.vram_size {
window_size = self.vram_size;
}
let mut window_base = self.state.persistent_state.s3.linear_addr_window;
window_base &= !(window_size - 1);
assert!(!(window_base == 0 && use_linear_addr));
if use_linear_addr {
self.control.map(
window_base.into(),
Some(MemoryRange::new(0..window_size.into())),
);
} else {
self.control.unmap();
}
self.state.persistent_state.s3.addr_window_offset = self.state.video_window_offset;
self.state.persistent_state.s3.linear_mapping = use_linear_addr;
self.state.persistent_state.s3.addr_window_size = window_size;
self.state.persistent_state.s3.addr_window_base = window_base;
}
fn calculate_graphics_mode_variables(&mut self) {
let current_dac_mode = self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_MISC_CONTROL_2_REGISTER]
& 0xF0;
let new_bits_per_pixel;
if (self.state.persistent_state.s3.adv_function_control_reg & 0x1) != 0 {
self.state.persistent_state.enhanced_dac_mode = true;
if current_dac_mode == 0x70 {
tracing::warn!("We don't support 24-bpp mode");
}
if current_dac_mode == 0xD0 {
new_bits_per_pixel = 32;
} else if current_dac_mode == 0x50 {
new_bits_per_pixel = 16;
} else if current_dac_mode == 0x30 {
new_bits_per_pixel = 15;
} else {
new_bits_per_pixel = 8;
}
} else {
self.state.persistent_state.enhanced_dac_mode = false;
let shift_reg_value =
(self.state.persistent_state.vga_graphics_regs[VgaGraphicsReg::MODE_REGISTER] >> 5)
& 0x3;
if shift_reg_value >= 2 {
new_bits_per_pixel = 8;
} else if shift_reg_value == 1 {
new_bits_per_pixel = 2;
} else if (self.state.persistent_state.crt_control_regs
[CrtControlReg::MODE_CONTROL_REGISTER]
& 0x20)
== 0
&& self.state.persistent_state.vga_attrib_regs
[VgaAttribReg::COLOR_PLANE_ENABLE_REGISTER]
== 1
{
new_bits_per_pixel = 1;
} else {
new_bits_per_pixel = 4;
}
}
if self.state.persistent_state.bits_per_pixel != new_bits_per_pixel {
self.state.persistent_state.bits_per_pixel = new_bits_per_pixel;
self.record_video_mode_change();
self.mark_palette_dirty();
self.calculate_monitor_timing();
}
let new_line_compare = self.get_line_compare_value();
if new_line_compare != self.state.persistent_state.line_compare_value {
self.state.persistent_state.line_compare_value = new_line_compare;
self.force_screen_redraw();
}
}
fn calculate_monitor_timing(&mut self) {
let mut vertical_end_reg = 1 + self.state.persistent_state.crt_control_regs
[CrtControlReg::VERTICAL_DISPLAY_END_REGISTER]
as u16;
if self.state.persistent_state.crt_control_regs[CrtControlReg::OVERFLOW_REGISTER] & (1 << 1)
!= 0
{
vertical_end_reg += 256;
}
if self.state.persistent_state.crt_control_regs[CrtControlReg::OVERFLOW_REGISTER] & (1 << 6)
!= 0
{
vertical_end_reg += 512;
}
if self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_VERTICAL_OVERFLOW_REGISTER]
& (1 << 1)
!= 0
{
vertical_end_reg += 1024;
}
let vertical_blank_reg = 1
+ (self.state.persistent_state.crt_control_regs
[CrtControlReg::START_VERTICAL_BLANK_REGISTER] as u16
| ((self.state.persistent_state.crt_control_regs[CrtControlReg::OVERFLOW_REGISTER]
as u16
& (1 << 3))
<< 5)
| ((self.state.persistent_state.crt_control_regs
[CrtControlReg::MAX_SCANLINE_REGISTER] as u16
& (1 << 5))
<< 4)
| ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_VERTICAL_OVERFLOW_REGISTER]
as u16
& (1 << 2))
<< 8));
if vertical_blank_reg < vertical_end_reg {
vertical_end_reg = vertical_blank_reg - 1;
}
let mut horizontal_end_reg = 1 + self.state.persistent_state.crt_control_regs
[CrtControlReg::HORIZONTAL_DISPLAY_END_REGISTER]
as u16;
if self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_HORIZONTAL_OVERFLOW_REGISTER]
& (1 << 1)
!= 0
{
horizontal_end_reg += 256;
}
horizontal_end_reg *= 8;
let mut new_width = horizontal_end_reg;
let mut new_height = vertical_end_reg;
if self.state.persistent_state.bits_per_pixel == 16 {
new_width /= 2;
} else if self.state.persistent_state.bits_per_pixel == 2 {
new_width *= 2;
} else if self.state.persistent_state.bits_per_pixel == 4
&& (self.state.persistent_state.vga_sequencer_regs
[VgaSequencerReg::CLOCKING_MODE_REGISTER]
& 0x08)
!= 0
{
new_width *= 2;
}
if self.state.persistent_state.crt_control_regs[CrtControlReg::S3_MODE_CONTROL_REGISTER]
& (1 << 5)
!= 0
{
new_height *= 2;
}
if (new_width as u32 * new_height as u32 * (self.get_current_video_depth() / 8) as u32)
> self.vram_size
{
return;
}
let mut mode_changed = false;
if self.state.persistent_state.pcvideo_width != new_width {
self.state.persistent_state.pcvideo_width = new_width;
mode_changed = true;
}
if self.state.persistent_state.pcvideo_height != new_height {
self.state.persistent_state.pcvideo_height = new_height;
mode_changed = true;
}
if self.state.persistent_state.adj_pcvideo_height != new_height {
self.state.persistent_state.adj_pcvideo_height = new_height;
mode_changed = true;
}
if mode_changed {
if self.state.persistent_state.text_mode {
self.calculate_text_mode_variables();
}
self.record_video_mode_change();
}
}
fn calculate_text_mode_variables(&mut self) {
let temp_byte = self.state.persistent_state.vga_sequencer_regs
[VgaSequencerReg::CHARACTER_FONT_SELECT_REGISTER];
let old_value = self.text.char_set_1;
self.text.char_set_1 = ((temp_byte >> 1) & 0x6) | ((temp_byte >> 5) & 0x1);
if self.text.char_set_1 != old_value {
self.force_screen_redraw();
}
let old_value = self.text.char_set_2;
self.text.char_set_2 = ((temp_byte << 1) & 0x6) | ((temp_byte >> 4) & 0x1);
if self.text.char_set_2 != old_value {
self.force_screen_redraw();
}
self.text.character_set_512 = self.text.char_set_1 != self.text.char_set_2;
let old_value = self.text.text_char_height;
let temp_byte = (self.state.persistent_state.crt_control_regs
[CrtControlReg::MAX_SCANLINE_REGISTER]
& 0x1F)
+ 1;
let mut screen_rows = self.state.persistent_state.pcvideo_height;
if !(320..=480).contains(&screen_rows) {
screen_rows = 400;
}
if temp_byte >= 8 {
self.text.text_char_height = temp_byte;
} else {
self.text.text_char_height = spec::DEFAULT_VGA_CHARACTER_HEIGHT;
}
if self.text.text_char_height != old_value {
self.record_video_mode_change();
}
let old_value = self.text.text_rows;
self.text.text_rows = (screen_rows / self.text.text_char_height as u16) as u8;
if self.text.text_rows > spec::TOTAL_VGA_MAX_TEXT_ROWS {
self.text.text_rows = spec::TOTAL_VGA_MAX_TEXT_ROWS;
}
if self.text.text_rows != old_value {
self.record_video_mode_change();
}
let is_lo_res = self.state.persistent_state.crt_control_regs
[CrtControlReg::START_HORIZONTAL_BLANK_REGISTER]
<= spec::TOTAL_VGA_LORES_TEXT_COLUMNS;
if is_lo_res != self.text.lo_res_text_mode {
self.record_video_mode_change();
}
self.text.lo_res_text_mode = is_lo_res;
self.text.current_text_columns = if is_lo_res {
spec::TOTAL_VGA_LORES_TEXT_COLUMNS.into()
} else {
spec::TOTAL_VGA_HIRES_TEXT_COLUMNS.into()
};
self.text.text_char_width = if is_lo_res {
spec::VGA_LORES_CHARACTER_WIDTH
} else {
spec::VGA_HIRES_CHARACTER_WIDTH
};
let attr_mode_control =
self.state.persistent_state.vga_attrib_regs[VgaAttribReg::MODE_CONTROL_REGISTER];
self.text.blinking_enabled = (attr_mode_control & 0x08) != 0;
self.evaluate_text_blink_timer_active_status();
self.text.mono_text_mode = (attr_mode_control & 0x02) != 0;
self.calculate_text_cursor_variables();
}
fn calculate_text_cursor_variables(&mut self) {
self.text.draw_text_cursor = (self.state.persistent_state.crt_control_regs
[CrtControlReg::CURSOR_START_REGISTER]
& spec::CURSOR_ENABLED_FLAG)
== 0;
self.evaluate_cursor_blink_timer_active_status();
let start_scan_line = (self.state.persistent_state.crt_control_regs
[CrtControlReg::CURSOR_START_REGISTER]
& spec::CURSOR_SCAN_LINE_MASK) as u16;
let end_scan_line = (self.state.persistent_state.crt_control_regs
[CrtControlReg::CURSOR_END_REGISTER]
& spec::CURSOR_SCAN_LINE_MASK) as u16;
let char_height = (self.state.persistent_state.crt_control_regs
[CrtControlReg::MAX_SCANLINE_REGISTER]
& spec::CRT_MAX_SCAN_LINE_MASK) as u16
+ 1;
self.text.cursor_first_scanline = start_scan_line;
let cursor_height = if end_scan_line >= start_scan_line && start_scan_line <= char_height {
end_scan_line - start_scan_line + 1
} else {
0
};
self.text.cursor_last_scanline = self.text.cursor_first_scanline + cursor_height;
self.text.cursor_first_scanline += 1;
let mut cursor_loc = (((self.state.persistent_state.crt_control_regs
[CrtControlReg::CURSOR_LOCATION_HI_REGISTER] as u16)
<< 8)
& 0x3F00)
| self.state.persistent_state.crt_control_regs
[CrtControlReg::CURSOR_LOCATION_LO_REGISTER] as u16;
cursor_loc =
cursor_loc.saturating_sub(self.state.persistent_state.cur_page_start_offset as u16);
self.text.cursor_row = cursor_loc / self.text.current_text_columns;
self.text.cursor_col = cursor_loc % self.text.current_text_columns;
}
fn calculate_line_offset_pixels(&mut self) {
let line_offset = self.get_line_offset_pixels();
if line_offset != self.state.persistent_state.line_offset_pixels {
self.state.persistent_state.line_offset_pixels = line_offset;
self.calculate_monitor_timing();
self.force_screen_redraw();
}
}
fn calculate_page_offset(&mut self) {
let new_offset = self.get_page_offset();
if self.state.persistent_state.cur_page_start_offset != new_offset {
self.state.persistent_state.cur_page_start_offset = new_offset;
if self.state.persistent_state.s3.enhanced_mode {
self.force_screen_redraw();
}
}
}
fn update_s3_state(&mut self) {
let pixel_len = (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_1_REGISTER]
>> 4)
& 0x3;
match pixel_len {
0 => {
self.state.persistent_state.s3.bits_per_pixel = 8;
}
1 => {
self.state.persistent_state.s3.bits_per_pixel = 16;
}
2 | 3 => {
if pixel_len == 2 {
tracing::warn!("unexpected pixel length 2");
}
self.state.persistent_state.s3.bits_per_pixel = 32;
}
_ => unreachable!(),
}
let screen_width_code = ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_1_REGISTER]
>> 6)
& 0x3)
| ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_1_REGISTER]
<< 2)
& 0x4);
match screen_width_code {
0 => {
self.state.persistent_state.s3.screen_pixel_width = 1024;
}
1 => {
self.state.persistent_state.s3.screen_pixel_width = 640;
}
2 => {
self.state.persistent_state.s3.screen_pixel_width = 800;
}
3 => {
self.state.persistent_state.s3.screen_pixel_width = 1280;
}
4 => {
self.state.persistent_state.s3.screen_pixel_width = 1152;
}
5 => {
self.state.persistent_state.s3.screen_pixel_width = 896;
}
6 => {
self.state.persistent_state.s3.screen_pixel_width = 1600;
}
code => {
tracing::warn!(code, "unexpected screen width code");
}
}
let enhanced_mode = (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_MEMORY_CONFIGURATION_REGISTER]
& 0x08)
!= 0;
if self.state.persistent_state.s3.enhanced_mode != enhanced_mode {
self.state.persistent_state.s3.enhanced_mode = enhanced_mode;
self.update_access_vars();
self.force_screen_redraw();
}
self.calculate_page_offset();
}
fn calculate_vga_address_range(&mut self) {
let range_start;
let range_end;
let address_mode = (self.state.persistent_state.vga_graphics_regs
[VgaGraphicsReg::MEMORY_MAP_MODE_CONTROL_REGISTER]
>> 2)
& 0x3;
match address_mode {
0 => {
range_start = 0xA0000;
range_end = 0xC0000;
}
1 => {
range_start = 0xA0000;
range_end = 0xB0000;
}
2 => {
range_start = 0xB0000;
range_end = 0xB8000;
}
3 => {
range_start = 0xB8000;
range_end = 0xC0000;
}
_ => unreachable!(),
}
self.state.video_start_bus_range_offset = range_start;
self.state.video_end_bus_range_offset = range_end;
}
fn write_pel_data_register(&mut self, data: u8) {
let pel_reg_entry = self.state.persistent_state.pel_colors.as_mut_bytes();
let len = pel_reg_entry.len();
if pel_reg_entry[self.state.persistent_state.pel_reg_write_index as usize] != data {
pel_reg_entry[self.state.persistent_state.pel_reg_write_index as usize] = data;
self.mark_palette_dirty();
}
self.state.persistent_state.pel_reg_write_index += 1;
if self.state.persistent_state.pel_reg_write_index as usize >= len {
self.state.persistent_state.pel_reg_write_index = 0;
}
}
fn is_text_mode(&self) -> bool {
self.state.persistent_state.video_enabled && self.state.persistent_state.text_mode
}
fn _is_vga_mode(&self) -> bool {
self.state.persistent_state.video_enabled
&& !self.state.persistent_state.text_mode
&& !self.state.persistent_state.s3.enhanced_mode
}
fn _is_legacy_mode(&self) -> bool {
self._is_vga_mode() || self.is_text_mode()
}
fn request_activation(&mut self) {
if self.suppress_initial_activation {
self.suppress_initial_activation = false;
} else {
todo!("ActivationRequested.Fire()");
}
}
fn record_video_mode_change(&mut self) {
if self.suppress_initial_activation {
self.suppress_initial_activation = false;
}
if !self.is_mode_change_pending {
self.is_mode_change_pending = true;
self.set_mode_change_timer();
}
}
fn set_delayed_redraw_timer(&mut self) {
self.is_delayed_redraw_timer_set = true;
self.set_multi_purpose_timer();
}
fn set_multi_purpose_timer(&mut self) {
}
fn set_mode_change_timer(&mut self) {
}
fn evaluate_text_blink_timer_active_status(&mut self) {
}
fn evaluate_cursor_blink_timer_active_status(&mut self) {
}
fn rebuild_mapping_table(&mut self) {
if self.palette_change_count == self.pixel_values_palette_count {
return;
}
let max_index = if self.state.persistent_state.bits_per_pixel >= 8 {
255
} else {
15
};
for pixel_value_index in 0..=max_index {
self.mapping_table[pixel_value_index as usize] =
self.vgacolor_index_to_argb32(pixel_value_index);
}
self.pixel_values_palette_count = self.palette_change_count;
}
fn update_render_state(&mut self) {
self.rebuild_mapping_table();
let render_state = if self.is_text_mode() {
RenderState::Text(TextRenderState {
text: self.text.clone(),
cur_page_start_offset: self.state.persistent_state.cur_page_start_offset,
mapping_table: self.mapping_table,
})
} else {
RenderState::Graphics(GraphicsRenderState {
bits_per_pixel: self.state.persistent_state.bits_per_pixel,
cur_page_start_offset: self.state.persistent_state.cur_page_start_offset,
line_offset_pixels: self.state.persistent_state.line_offset_pixels,
width: self.state.persistent_state.pcvideo_width,
height: self.state.persistent_state.adj_pcvideo_height,
pixel_double: self.state.persistent_state.vga_sequencer_regs
[VgaSequencerReg::CLOCKING_MODE_REGISTER]
& 0x08
!= 0,
pixel_pan: self.state.persistent_state.vga_attrib_regs
[VgaAttribReg::HORIZONTAL_PIXEL_PANNING_REGISTER]
& 7,
mapping_table: self.mapping_table,
})
};
self.render_control.update(render_state);
}
fn vgacolor_index_to_argb32(&self, index: u8) -> u32 {
let color_reg_index = if (self.state.persistent_state.bits_per_pixel < 8)
&& (!self.state.persistent_state.enhanced_dac_mode)
{
let palette_reg_index = index
& self.state.persistent_state.vga_attrib_regs
[VgaAttribReg::COLOR_PLANE_ENABLE_REGISTER];
let palette_reg_value =
self.state.persistent_state.vga_attrib_regs[VgaAttribReg(palette_reg_index & 0xF)];
if (self.state.persistent_state.vga_attrib_regs[VgaAttribReg::MODE_CONTROL_REGISTER]
& spec::ATTRIBUTE_CONTROLLER_MODE_CONTROL_IPS_MASK)
== 0
{
(palette_reg_value & 0x3F)
| ((self.state.persistent_state.vga_attrib_regs
[VgaAttribReg::PIXEL_PADDING_REGISTER]
<< 4)
& 0xC0)
} else {
(palette_reg_value & 0x0F)
| ((self.state.persistent_state.vga_attrib_regs
[VgaAttribReg::PIXEL_PADDING_REGISTER]
<< 4)
& 0xF0)
}
} else {
index
};
let color_comp =
self.state.persistent_state.pel_colors[color_reg_index as usize].red & 0x3F;
let red = ((color_comp << 2) | (color_comp >> 4)) as u32;
let color_comp =
self.state.persistent_state.pel_colors[color_reg_index as usize].green & 0x3F;
let green = ((color_comp << 2) | (color_comp >> 4)) as u32;
let color_comp =
self.state.persistent_state.pel_colors[color_reg_index as usize].blue & 0x3F;
let blue = ((color_comp << 2) | (color_comp >> 4)) as u32;
0xFF000000 | (red << 16) | (green << 8) | blue
}
fn mark_palette_dirty(&mut self) {
self.palette_change_count += 1;
self.force_screen_redraw();
}
fn force_screen_redraw(&mut self) {
if !self.is_mode_change_pending && !self.is_full_refresh_pending {
self.is_full_refresh_pending = true;
self.set_delayed_redraw_timer();
}
}
fn get_line_offset_pixels(&self) -> u16 {
let mut line_offset =
self.state.persistent_state.crt_control_regs[CrtControlReg::OFFSET_REGISTER] as u16 * 2;
if (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_2_REGISTER]
& 0x30)
!= 0
{
line_offset += (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_2_REGISTER]
as u16
& 0x30)
<< 5;
} else if (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_MODE_REGISTER]
& 0x4)
!= 0
{
line_offset += 1 << 9;
}
if line_offset == 0 {
line_offset = 512;
}
line_offset
}
fn get_line_compare_value(&self) -> u16 {
self.state.persistent_state.crt_control_regs[CrtControlReg::LINE_COMPARE_REGISTER] as u16
| ((self.state.persistent_state.crt_control_regs[CrtControlReg::OVERFLOW_REGISTER]
as u16
& 0x10)
<< 4)
| ((self.state.persistent_state.crt_control_regs[CrtControlReg::MAX_SCANLINE_REGISTER]
as u16
& 0x40)
<< 3)
| ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_VERTICAL_OVERFLOW_REGISTER] as u16
& 0x40)
<< 4)
}
fn get_page_offset(&self) -> u32 {
let mut new_offset = self.state.persistent_state.crt_control_regs
[CrtControlReg::START_ADDRESS_LO_REGISTER] as u32;
new_offset |= (self.state.persistent_state.crt_control_regs
[CrtControlReg::START_ADDRESS_HI_REGISTER] as u32)
<< 8;
if self.state.persistent_state.s3.enhanced_mode {
if self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_3_REGISTER]
== 0
{
new_offset |= ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_MEMORY_CONFIGURATION_REGISTER]
as u32)
<< (16 - 4))
& 0x00030000;
new_offset |= ((self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_2_REGISTER]
as u32)
<< 18)
& 0x000C0000;
} else {
new_offset |= (self.state.persistent_state.crt_control_regs
[CrtControlReg::S3_EXTENDED_SYSTEM_CONTROL_3_REGISTER]
as u32)
<< 16;
}
}
new_offset
}
fn displaying_splash_screen(&self) -> bool {
self.state.persistent_state.crt_control_regs[CrtControlReg::CUSTOM_VS_BIOS_LOGO_REGISTER]
!= 0xFF
}
fn get_current_video_depth(&self) -> u8 {
if self.displaying_splash_screen() {
SPLASH_SCREEN_BYTES_PER_PIXEL * 8
} else if self.state.persistent_state.text_mode {
0
} else {
self.state.persistent_state.bits_per_pixel
}
}
fn _get_current_video_width(&self) -> u16 {
if self.displaying_splash_screen() {
_SPLASH_SCREEN_WIDTH
} else if self.state.persistent_state.text_mode {
spec::TOTAL_VGA_HIRES_TEXT_COLUMNS as u16 * spec::VGA_HIRES_CHARACTER_WIDTH as u16
} else {
self.state.persistent_state.pcvideo_width
}
}
fn _get_current_video_height(&self) -> u16 {
if self.displaying_splash_screen() {
_SPLASH_SCREEN_HEIGHT
} else if self.state.persistent_state.text_mode {
self.text.text_rows as u16 * self.text.text_char_height as u16
} else {
self.state.persistent_state.adj_pcvideo_height
}
}
}