Move video mode selection to generic code
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
ENTRY(kstart)
|
||||
ENTRY(start)
|
||||
OUTPUT_FORMAT(elf32-i386)
|
||||
|
||||
SECTIONS {
|
||||
|
||||
+131
@@ -16,8 +16,139 @@ extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate uefi_std as std;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp;
|
||||
use redoxfs::Disk;
|
||||
|
||||
use self::os::{Os, OsKey, OsMemoryEntry, OsVideoMode};
|
||||
|
||||
#[macro_use]
|
||||
mod os;
|
||||
|
||||
mod arch;
|
||||
mod logger;
|
||||
|
||||
fn main<
|
||||
D: Disk,
|
||||
M: Iterator<Item=OsMemoryEntry>,
|
||||
V: Iterator<Item=OsVideoMode>
|
||||
>(os: &mut dyn Os<D, M, V>) -> Option<OsVideoMode> {
|
||||
let mut modes = Vec::new();
|
||||
for mode in os.video_modes() {
|
||||
let mut aspect_w = mode.width;
|
||||
let mut aspect_h = mode.height;
|
||||
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
|
||||
while aspect_w % i == 0 && aspect_h % i == 0 {
|
||||
aspect_w /= i;
|
||||
aspect_h /= i;
|
||||
}
|
||||
}
|
||||
|
||||
modes.push((
|
||||
mode,
|
||||
format!("{:>4}x{:<4} {:>3}:{:<3}", mode.width, mode.height, aspect_w, aspect_h)
|
||||
));
|
||||
}
|
||||
|
||||
// Sort modes by pixel area, reversed
|
||||
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
|
||||
|
||||
println!();
|
||||
println!("Arrow keys and enter select mode");
|
||||
println!();
|
||||
print!(" ");
|
||||
|
||||
let (off_x, off_y) = os.get_text_position();
|
||||
let rows = 12;
|
||||
//TODO 0x4F03 VBE function to get current mode
|
||||
let mut selected = modes.get(0).map_or(0, |x| x.0.id);
|
||||
while ! modes.is_empty() {
|
||||
let mut row = 0;
|
||||
let mut col = 0;
|
||||
for (mode, text) in modes.iter() {
|
||||
if row >= rows {
|
||||
col += 1;
|
||||
row = 0;
|
||||
}
|
||||
|
||||
os.set_text_position(off_x + col * 20, off_y + row);
|
||||
os.set_text_highlight(mode.id == selected);
|
||||
|
||||
print!("{}", text);
|
||||
|
||||
row += 1;
|
||||
}
|
||||
|
||||
// Read keypress
|
||||
match os.get_key() {
|
||||
OsKey::Left => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||
if mode_i < rows {
|
||||
while mode_i < modes.len() {
|
||||
mode_i += rows;
|
||||
}
|
||||
}
|
||||
mode_i -= rows;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
OsKey::Right => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||
mode_i += rows;
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
OsKey::Up => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||
if mode_i % rows == 0 {
|
||||
mode_i += rows;
|
||||
if mode_i > modes.len() {
|
||||
mode_i = modes.len();
|
||||
}
|
||||
}
|
||||
mode_i -= 1;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
OsKey::Down => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||
mode_i += 1;
|
||||
if mode_i % rows == 0 {
|
||||
mode_i -= rows;
|
||||
}
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i - mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
OsKey::Enter => {
|
||||
break;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
os.set_text_position(0, off_y + rows);
|
||||
os.set_text_highlight(false);
|
||||
println!();
|
||||
|
||||
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||
if let Some((mode, _text)) = modes.get(mode_i) {
|
||||
return Some(*mode);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,14 +1,65 @@
|
||||
use core::{cmp, mem, ptr};
|
||||
|
||||
use crate::os::{OsMemoryEntry, OsMemoryKind};
|
||||
|
||||
use super::{MEMORY_MAP_ADDR, thunk::ThunkData};
|
||||
|
||||
#[repr(packed)]
|
||||
struct MemoryMapEntry {
|
||||
pub base: u64,
|
||||
pub length: u64,
|
||||
pub size: u64,
|
||||
pub kind: u32,
|
||||
}
|
||||
|
||||
pub struct MemoryMapIter {
|
||||
thunk15: extern "C" fn(),
|
||||
data: ThunkData,
|
||||
first: bool,
|
||||
}
|
||||
|
||||
impl MemoryMapIter {
|
||||
pub fn new(thunk15: extern "C" fn()) -> Self {
|
||||
Self {
|
||||
thunk15,
|
||||
data: ThunkData::new(),
|
||||
first: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for MemoryMapIter {
|
||||
type Item=OsMemoryEntry;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.first {
|
||||
self.first = false;
|
||||
} else if self.data.ebx == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.data.eax = 0xE820;
|
||||
self.data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
|
||||
self.data.edx = 0x534D4150;
|
||||
self.data.edi = MEMORY_MAP_ADDR as u32;
|
||||
|
||||
unsafe { self.data.with(self.thunk15); }
|
||||
|
||||
//TODO: return error?
|
||||
assert_eq!({ self.data.eax }, 0x534D4150);
|
||||
assert_eq!({ self.data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
|
||||
|
||||
let entry = unsafe { ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry) };
|
||||
Some(Self::Item {
|
||||
base: entry.base,
|
||||
size: entry.size,
|
||||
kind: match entry.kind {
|
||||
1 => OsMemoryKind::Free,
|
||||
3 => OsMemoryKind::Reclaim,
|
||||
_ => OsMemoryKind::Reserved,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
|
||||
let mut heap_limits = None;
|
||||
let mut data = ThunkData::new();
|
||||
@@ -29,10 +80,10 @@ pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
|
||||
if
|
||||
entry.kind == 1 &&
|
||||
entry.base <= heap_start as u64 &&
|
||||
(entry.base + entry.length) >= heap_start as u64
|
||||
(entry.base + entry.size) >= heap_start as u64
|
||||
{
|
||||
let heap_end = cmp::min(
|
||||
entry.base + entry.length,
|
||||
entry.base + entry.size,
|
||||
usize::MAX as u64
|
||||
) as usize;
|
||||
if heap_end >= heap_start {
|
||||
|
||||
+92
-191
@@ -1,25 +1,22 @@
|
||||
use alloc::{
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cmp,
|
||||
convert::TryFrom,
|
||||
ptr,
|
||||
slice,
|
||||
};
|
||||
use linked_list_allocator::LockedHeap;
|
||||
use log::error;
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::logger::LOGGER;
|
||||
use crate::arch::paging_create;
|
||||
use crate::logger::LOGGER;
|
||||
use crate::os::{Os, OsKey};
|
||||
|
||||
use self::disk::DiskBios;
|
||||
use self::memory_map::memory_map;
|
||||
use self::memory_map::{memory_map, MemoryMapIter};
|
||||
use self::thunk::ThunkData;
|
||||
use self::vbe::{VbeCardInfo, VbeModeInfo};
|
||||
use self::vbe::VideoModeIter;
|
||||
use self::vga::{VgaTextColor, Vga};
|
||||
|
||||
#[macro_use]
|
||||
@@ -73,8 +70,80 @@ pub struct KernelArgs {
|
||||
acpi_rsdps_size: u64,
|
||||
}
|
||||
|
||||
pub struct OsBios {
|
||||
boot_disk: usize,
|
||||
thunk10: extern "C" fn(),
|
||||
thunk13: extern "C" fn(),
|
||||
thunk15: extern "C" fn(),
|
||||
thunk16: extern "C" fn(),
|
||||
}
|
||||
|
||||
impl Os<
|
||||
DiskBios,
|
||||
MemoryMapIter,
|
||||
VideoModeIter
|
||||
> for OsBios {
|
||||
fn disk(&self) -> DiskBios {
|
||||
DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13)
|
||||
}
|
||||
|
||||
fn memory(&self) -> MemoryMapIter {
|
||||
MemoryMapIter::new(self.thunk15)
|
||||
}
|
||||
|
||||
fn video_modes(&self) -> VideoModeIter {
|
||||
VideoModeIter::new(self.thunk10)
|
||||
}
|
||||
|
||||
fn set_video_mode(&self, id: u32) {
|
||||
// Set video mode
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F02;
|
||||
data.ebx = id;
|
||||
unsafe { data.with(self.thunk10); }
|
||||
//TODO: check result
|
||||
}
|
||||
|
||||
fn get_key(&self) -> OsKey {
|
||||
// Read keypress
|
||||
let mut data = ThunkData::new();
|
||||
unsafe { data.with(self.thunk16); }
|
||||
match (data.eax >> 8) as u8 {
|
||||
0x4B => OsKey::Left,
|
||||
0x4D => OsKey::Right,
|
||||
0x48 => OsKey::Up,
|
||||
0x50 => OsKey::Down,
|
||||
0x1C => OsKey::Enter,
|
||||
_ => OsKey::Other,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text_position(&self) -> (usize, usize) {
|
||||
let vga = VGA.lock();
|
||||
(vga.x, vga.y)
|
||||
}
|
||||
|
||||
fn set_text_position(&self, x: usize, y: usize) {
|
||||
//TODO: ensure this is inside bounds!
|
||||
let mut vga = VGA.lock();
|
||||
vga.x = x;
|
||||
vga.y = y;
|
||||
}
|
||||
|
||||
fn set_text_highlight(&self, highlight: bool) {
|
||||
let mut vga = VGA.lock();
|
||||
if highlight {
|
||||
vga.bg = VgaTextColor::White;
|
||||
vga.fg = VgaTextColor::Black;
|
||||
} else {
|
||||
vga.bg = VgaTextColor::DarkGray;
|
||||
vga.fg = VgaTextColor::White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kstart(
|
||||
pub unsafe extern "C" fn start(
|
||||
kernel_entry: extern "C" fn(
|
||||
page_table: usize,
|
||||
stack: u64,
|
||||
@@ -113,8 +182,16 @@ pub unsafe extern "C" fn kstart(
|
||||
|
||||
ALLOCATOR.lock().init(heap_start, heap_size);
|
||||
|
||||
let mut os = OsBios {
|
||||
boot_disk,
|
||||
thunk10,
|
||||
thunk13,
|
||||
thunk15,
|
||||
thunk16,
|
||||
};
|
||||
|
||||
// Locate kernel on RedoxFS
|
||||
let disk = DiskBios::new(u8::try_from(boot_disk).unwrap(), thunk13);
|
||||
let disk = os.disk();
|
||||
|
||||
//TODO: get block from partition table
|
||||
let block = 1024 * 1024 / redoxfs::BLOCK_SIZE;
|
||||
@@ -131,177 +208,7 @@ pub unsafe extern "C" fn kstart(
|
||||
}
|
||||
println!(": {} MiB", fs.header.1.size / 1024 / 1024);
|
||||
|
||||
let mut modes = Vec::new();
|
||||
{
|
||||
// Get card info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F00;
|
||||
data.edi = VBE_CARD_INFO_ADDR as u32;
|
||||
data.with(thunk10);
|
||||
if data.eax == 0x004F {
|
||||
let card_info = ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo);
|
||||
|
||||
let mut mode_ptr = card_info.videomodeptr as *const u16;
|
||||
loop {
|
||||
// Ask for linear frame buffer with mode
|
||||
let mode = *mode_ptr | (1 << 14);
|
||||
if mode == 0xFFFF {
|
||||
break;
|
||||
}
|
||||
mode_ptr = mode_ptr.add(1);
|
||||
|
||||
// Get mode info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F01;
|
||||
data.ecx = mode as u32;
|
||||
data.edi = VBE_MODE_INFO_ADDR as u32;
|
||||
data.with(thunk10);
|
||||
if data.eax == 0x004F {
|
||||
let mode_info = ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo);
|
||||
|
||||
// We only support 32-bits per pixel modes
|
||||
if mode_info.bitsperpixel != 32 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let w = mode_info.xresolution as u32;
|
||||
let h = mode_info.yresolution as u32;
|
||||
|
||||
let mut aspect_w = w;
|
||||
let mut aspect_h = h;
|
||||
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
|
||||
while aspect_w % i == 0 && aspect_h % i == 0 {
|
||||
aspect_w /= i;
|
||||
aspect_h /= i;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: support resolutions that are not perfect multiples of 4
|
||||
if w % 4 != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
modes.push((
|
||||
mode,
|
||||
w, h,
|
||||
mode_info.physbaseptr,
|
||||
format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h)
|
||||
));
|
||||
} else {
|
||||
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, { data.eax });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
|
||||
}
|
||||
}
|
||||
|
||||
// Sort modes by pixel area, reversed
|
||||
modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2)));
|
||||
|
||||
println!();
|
||||
println!("Arrow keys and enter select mode");
|
||||
println!();
|
||||
print!(" ");
|
||||
|
||||
//TODO 0x4F03 VBE function to get current mode
|
||||
let off_x = VGA.lock().x;
|
||||
let off_y = VGA.lock().y;
|
||||
let rows = 12;
|
||||
let mut selected = modes.get(0).map_or(0, |x| x.0);
|
||||
while ! modes.is_empty() {
|
||||
let mut row = 0;
|
||||
let mut col = 0;
|
||||
for (mode, _w, _h, _ptr, text) in modes.iter() {
|
||||
if row >= rows {
|
||||
col += 1;
|
||||
row = 0;
|
||||
}
|
||||
|
||||
VGA.lock().x = off_x + col * 20;
|
||||
VGA.lock().y = off_y + row;
|
||||
|
||||
if *mode == selected {
|
||||
VGA.lock().bg = VgaTextColor::White;
|
||||
VGA.lock().fg = VgaTextColor::Black;
|
||||
} else {
|
||||
VGA.lock().bg = VgaTextColor::DarkGray;
|
||||
VGA.lock().fg = VgaTextColor::White;
|
||||
}
|
||||
|
||||
print!("{}", text);
|
||||
|
||||
row += 1;
|
||||
}
|
||||
|
||||
// Read keypress
|
||||
let mut data = ThunkData::new();
|
||||
data.with(thunk16);
|
||||
match (data.eax >> 8) as u8 {
|
||||
0x4B /* Left */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if mode_i < rows {
|
||||
while mode_i < modes.len() {
|
||||
mode_i += rows;
|
||||
}
|
||||
}
|
||||
mode_i -= rows;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x4D /* Right */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
mode_i += rows;
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x48 /* Up */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if mode_i % rows == 0 {
|
||||
mode_i += rows;
|
||||
if mode_i > modes.len() {
|
||||
mode_i = modes.len();
|
||||
}
|
||||
}
|
||||
mode_i -= 1;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x50 /* Down */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
mode_i += 1;
|
||||
if mode_i % rows == 0 {
|
||||
mode_i -= rows;
|
||||
}
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i - mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x1C /* Enter */ => {
|
||||
break;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
VGA.lock().x = 0;
|
||||
VGA.lock().y = off_y + rows;
|
||||
VGA.lock().bg = VgaTextColor::DarkGray;
|
||||
VGA.lock().fg = VgaTextColor::White;
|
||||
println!();
|
||||
let mode_opt = crate::main(&mut os);
|
||||
|
||||
let kernel = {
|
||||
let node = fs.find_node("kernel", fs.header.1.root)
|
||||
@@ -355,17 +262,11 @@ pub unsafe extern "C" fn kstart(
|
||||
|
||||
let mut env = String::with_capacity(4096);
|
||||
|
||||
if let Some(mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if let Some((mode, w, h, ptr, _text)) = modes.get(mode_i) {
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F02;
|
||||
data.ebx = *mode as u32;
|
||||
data.with(thunk10);
|
||||
|
||||
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", ptr));
|
||||
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", w));
|
||||
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", h));
|
||||
}
|
||||
if let Some(mode) = mode_opt {
|
||||
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", mode.base));
|
||||
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", mode.width));
|
||||
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", mode.height));
|
||||
os.set_video_mode(mode.id);
|
||||
}
|
||||
env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block));
|
||||
env.push_str("REDOXFS_UUID=");
|
||||
|
||||
+128
-45
@@ -1,54 +1,137 @@
|
||||
use core::ptr;
|
||||
use log::error;
|
||||
|
||||
use crate::os::OsVideoMode;
|
||||
|
||||
use super::{ThunkData, VBE_CARD_INFO_ADDR, VBE_MODE_INFO_ADDR};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(packed)]
|
||||
pub struct VbeCardInfo {
|
||||
pub signature: [u8; 4],
|
||||
pub version: u16,
|
||||
pub oemstring: u32,
|
||||
pub capabilities: u32,
|
||||
pub videomodeptr: u32,
|
||||
pub totalmemory: u16,
|
||||
pub oemsoftwarerev: u16,
|
||||
pub oemvendornameptr: u32,
|
||||
pub oemproductnameptr: u32,
|
||||
pub oemproductrevptr: u32,
|
||||
pub reserved: [u8; 222],
|
||||
pub oemdata: [u8; 256],
|
||||
pub signature: [u8; 4],
|
||||
pub version: u16,
|
||||
pub oemstring: u32,
|
||||
pub capabilities: u32,
|
||||
pub videomodeptr: u32,
|
||||
pub totalmemory: u16,
|
||||
pub oemsoftwarerev: u16,
|
||||
pub oemvendornameptr: u32,
|
||||
pub oemproductnameptr: u32,
|
||||
pub oemproductrevptr: u32,
|
||||
pub reserved: [u8; 222],
|
||||
pub oemdata: [u8; 256],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(packed)]
|
||||
pub struct VbeModeInfo {
|
||||
pub attributes: u16,
|
||||
pub win_a: u8,
|
||||
pub win_b: u8,
|
||||
pub granularity: u16,
|
||||
pub winsize: u16,
|
||||
pub segment_a: u16,
|
||||
pub segment_b: u16,
|
||||
pub winfuncptr: u32,
|
||||
pub bytesperscanline: u16,
|
||||
pub xresolution: u16,
|
||||
pub yresolution: u16,
|
||||
pub xcharsize: u8,
|
||||
pub ycharsize: u8,
|
||||
pub numberofplanes: u8,
|
||||
pub bitsperpixel: u8,
|
||||
pub numberofbanks: u8,
|
||||
pub memorymodel: u8,
|
||||
pub banksize: u8,
|
||||
pub numberofimagepages: u8,
|
||||
pub unused: u8,
|
||||
pub redmasksize: u8,
|
||||
pub redfieldposition: u8,
|
||||
pub greenmasksize: u8,
|
||||
pub greenfieldposition: u8,
|
||||
pub bluemasksize: u8,
|
||||
pub bluefieldposition: u8,
|
||||
pub rsvdmasksize: u8,
|
||||
pub rsvdfieldposition: u8,
|
||||
pub directcolormodeinfo: u8,
|
||||
pub physbaseptr: u32,
|
||||
pub offscreenmemoryoffset: u32,
|
||||
pub offscreenmemsize: u16,
|
||||
pub reserved: [u8; 206],
|
||||
pub attributes: u16,
|
||||
pub win_a: u8,
|
||||
pub win_b: u8,
|
||||
pub granularity: u16,
|
||||
pub winsize: u16,
|
||||
pub segment_a: u16,
|
||||
pub segment_b: u16,
|
||||
pub winfuncptr: u32,
|
||||
pub bytesperscanline: u16,
|
||||
pub xresolution: u16,
|
||||
pub yresolution: u16,
|
||||
pub xcharsize: u8,
|
||||
pub ycharsize: u8,
|
||||
pub numberofplanes: u8,
|
||||
pub bitsperpixel: u8,
|
||||
pub numberofbanks: u8,
|
||||
pub memorymodel: u8,
|
||||
pub banksize: u8,
|
||||
pub numberofimagepages: u8,
|
||||
pub unused: u8,
|
||||
pub redmasksize: u8,
|
||||
pub redfieldposition: u8,
|
||||
pub greenmasksize: u8,
|
||||
pub greenfieldposition: u8,
|
||||
pub bluemasksize: u8,
|
||||
pub bluefieldposition: u8,
|
||||
pub rsvdmasksize: u8,
|
||||
pub rsvdfieldposition: u8,
|
||||
pub directcolormodeinfo: u8,
|
||||
pub physbaseptr: u32,
|
||||
pub offscreenmemoryoffset: u32,
|
||||
pub offscreenmemsize: u16,
|
||||
pub reserved: [u8; 206],
|
||||
}
|
||||
|
||||
pub struct VideoModeIter {
|
||||
thunk10: extern "C" fn(),
|
||||
mode_ptr: *const u16,
|
||||
}
|
||||
|
||||
impl VideoModeIter {
|
||||
pub fn new(thunk10: extern "C" fn()) -> Self {
|
||||
// Get card info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F00;
|
||||
data.edi = VBE_CARD_INFO_ADDR as u32;
|
||||
unsafe { data.with(thunk10); }
|
||||
let mode_ptr = if data.eax == 0x004F {
|
||||
let card_info = unsafe { ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo) };
|
||||
card_info.videomodeptr as *const u16
|
||||
} else {
|
||||
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
|
||||
ptr::null()
|
||||
};
|
||||
Self {
|
||||
thunk10,
|
||||
mode_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for VideoModeIter {
|
||||
type Item = OsVideoMode;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.mode_ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
loop {
|
||||
// Set bit 14 to get linear frame buffer
|
||||
let mode = unsafe { *self.mode_ptr } | (1 << 14);
|
||||
if mode == 0xFFFF {
|
||||
return None;
|
||||
}
|
||||
self.mode_ptr = unsafe { self.mode_ptr.add(1) };
|
||||
|
||||
// Get mode info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F01;
|
||||
data.ecx = mode as u32;
|
||||
data.edi = VBE_MODE_INFO_ADDR as u32;
|
||||
unsafe { data.with(self.thunk10); }
|
||||
if data.eax == 0x004F {
|
||||
let mode_info = unsafe { ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo) };
|
||||
|
||||
// We only support 32-bits per pixel modes
|
||||
if mode_info.bitsperpixel != 32 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let width = mode_info.xresolution as u32;
|
||||
let height = mode_info.yresolution as u32;
|
||||
|
||||
//TODO: support resolutions that are not perfect multiples of 4
|
||||
if width % 4 != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(OsVideoMode {
|
||||
id: mode as u32,
|
||||
width,
|
||||
height,
|
||||
base: mode_info.physbaseptr as u64,
|
||||
});
|
||||
} else {
|
||||
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, { data.eax });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use redoxfs::Disk;
|
||||
|
||||
#[cfg(all(target_arch = "x86", target_os = "none"))]
|
||||
pub use self::bios::*;
|
||||
|
||||
@@ -11,3 +13,54 @@ pub use self::uefi::*;
|
||||
#[cfg(target_os = "uefi")]
|
||||
#[macro_use]
|
||||
mod uefi;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum OsKey {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Enter,
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum OsMemoryKind {
|
||||
Free,
|
||||
Reclaim,
|
||||
Reserved,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct OsMemoryEntry {
|
||||
pub base: u64,
|
||||
pub size: u64,
|
||||
pub kind: OsMemoryKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct OsVideoMode {
|
||||
pub id: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub base: u64,
|
||||
}
|
||||
|
||||
pub trait Os<
|
||||
D: Disk,
|
||||
M: Iterator<Item=OsMemoryEntry>,
|
||||
V: Iterator<Item=OsVideoMode>
|
||||
> {
|
||||
fn disk(&self) -> D;
|
||||
|
||||
fn memory(&self) -> M;
|
||||
|
||||
fn video_modes(&self) -> V;
|
||||
fn set_video_mode(&self, id: u32);
|
||||
|
||||
fn get_key(&self) -> OsKey;
|
||||
|
||||
fn get_text_position(&self) -> (usize, usize);
|
||||
fn set_text_position(&self, x: usize, y: usize);
|
||||
fn set_text_highlight(&self, highlight: bool);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user