Converted to a simple BMP viewer
This commit is contained in:
parent
875e73d758
commit
5f9d992d6d
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ Cargo.lock
|
||||
elf2le
|
||||
a.exe
|
||||
new.elf
|
||||
Makefile
|
||||
|
@ -9,12 +9,8 @@ edition = "2021"
|
||||
[dependencies]
|
||||
heapless = { version = "0.7.16", features = ["ufmt-impl"] }
|
||||
spin = "0.9.4"
|
||||
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
bitvec = { version = "1.0.1", default-features = false, features = ["atomic", "alloc"] }
|
||||
itertools = { version = "0.11", default-features = false, features = ["use_alloc"] }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
2
dos.json
2
dos.json
@ -7,7 +7,7 @@
|
||||
"exe-suffix": ".elf",
|
||||
"linker-flavor": "gcc",
|
||||
"linker-is-gnu": true,
|
||||
"llvm-target": "i586-unknown-none-none",
|
||||
"llvm-target": "i386-unknown-none-none",
|
||||
"max-atomic-width": 64,
|
||||
"position-independent-executables": false,
|
||||
"relocation-model": "static",
|
||||
|
131
src/bmp.rs
Normal file
131
src/bmp.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use core::mem::size_of;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use bitvec::{view::BitView, field::BitField};
|
||||
|
||||
use crate::println;
|
||||
use crate::vga::Vga18;
|
||||
|
||||
#[repr(packed)]
|
||||
struct PackedBmpHeader {
|
||||
bmp_type: u16,
|
||||
size: u32,
|
||||
reserved: u32,
|
||||
offset: u32,
|
||||
header_size: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
planes: u16,
|
||||
bpp: u16,
|
||||
compression: u32,
|
||||
size_image: u32,
|
||||
xppm: u32,
|
||||
yppm: u32,
|
||||
colors_used: u32,
|
||||
colors_important: u32,
|
||||
}
|
||||
pub struct BmpHeader {
|
||||
pub bmp_type: u16,
|
||||
pub size: u32,
|
||||
pub reserved: u32,
|
||||
pub offset: u32,
|
||||
pub header_size: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub planes: u16,
|
||||
pub bpp: u16,
|
||||
pub compression: u32,
|
||||
pub size_image: u32,
|
||||
pub xppm: u32,
|
||||
pub yppm: u32,
|
||||
pub colors_used: u32,
|
||||
pub colors_important: u32,
|
||||
}
|
||||
impl From::<PackedBmpHeader> for BmpHeader {
|
||||
fn from(value: PackedBmpHeader) -> Self {
|
||||
Self { bmp_type: value.bmp_type, size: value.size, reserved: value.reserved, offset: value.offset, header_size: value.header_size, width: value.width, height: value.height, planes: value.planes, bpp: value.bpp, compression: value.compression, size_image: value.size_image, xppm: value.xppm, yppm: value.yppm, colors_used: value.colors_used, colors_important: value.colors_important }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bmp {
|
||||
pub header: BmpHeader,
|
||||
pub palette_table: alloc::vec::Vec<Vga18>,
|
||||
pub data: Box<[u8]>
|
||||
}
|
||||
|
||||
fn load_data(header: &PackedBmpHeader, raw: &[u8]) -> Option<Box<[u8]>> {
|
||||
match header.bpp {
|
||||
8 => {
|
||||
let row_size = (((8 * header.width + 31) >> 5) << 2) as usize;
|
||||
let chunks = raw[..row_size * header.height as usize]
|
||||
.chunks_exact(row_size).rev()
|
||||
.flat_map(|f| f[..header.width as usize].iter().cloned());
|
||||
Some(chunks.collect())
|
||||
},
|
||||
bpp @ (4|2|1) => {
|
||||
let row_size = ((bpp as usize * header.width as usize + 31) >> 5) << 2;
|
||||
let chunks = raw[..row_size * header.height as usize]
|
||||
.chunks_exact(row_size).rev()
|
||||
.map(|row| row.view_bits::<bitvec::order::Msb0>()
|
||||
.chunks(bpp as usize).take(header.width as usize)
|
||||
.map(|b| b.load_le::<u8>()));
|
||||
Some(chunks.flatten().collect())
|
||||
}
|
||||
bpp => {
|
||||
println!("Unsupported BPP {}", bpp);
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_bmp(source: &[u8]) -> Option<Bmp> {
|
||||
// i'm sorry but i love loading file headers like this
|
||||
let header: PackedBmpHeader = unsafe {
|
||||
let mut copy: [u8; size_of::<PackedBmpHeader>()] = [0; size_of::<PackedBmpHeader>()];
|
||||
copy.copy_from_slice(&source[..size_of::<PackedBmpHeader>()]);
|
||||
core::mem::transmute(copy)
|
||||
};
|
||||
|
||||
// 'BM'
|
||||
if header.bmp_type != 0x4D42 {
|
||||
println!("Invalid BMP type");
|
||||
return None
|
||||
}
|
||||
if header.size as usize != source.len() {
|
||||
println!("BMP size does not match file length!");
|
||||
return None
|
||||
}
|
||||
if header.offset as usize > source.len() {
|
||||
println!("BMP bitmap offset greater than file length!");
|
||||
return None
|
||||
}
|
||||
if header.compression != 0 {
|
||||
println!("Compressed BMPs are not supported");
|
||||
return None
|
||||
}
|
||||
if {
|
||||
let row_size = ((header.bpp as usize * header.width as usize + 31) >> 5) << 2;
|
||||
let (calc_size, overflow) = row_size.overflowing_mul(header.height as usize);
|
||||
(calc_size > (source.len() - header.offset as usize)) | overflow
|
||||
} {
|
||||
println!("File cannot possibly contain full BMP data!");
|
||||
return None
|
||||
}
|
||||
|
||||
let color_table = &source[(header.offset - header.colors_used * 4) as usize..];
|
||||
let palette: alloc::vec::Vec<_> = color_table.iter()
|
||||
.tuples().take(header.colors_used as usize)
|
||||
.map(|(b,g,r,_)| Vga18 { red:r>>2,green:g>>2,blue:b>>2 })
|
||||
.collect();
|
||||
|
||||
let raw_data = &source[header.offset as usize..];
|
||||
let data = load_data(&header, raw_data);
|
||||
|
||||
Some(Bmp {
|
||||
header: BmpHeader::from(header),
|
||||
palette_table: palette,
|
||||
data: data?
|
||||
})
|
||||
}
|
BIN
src/chicken.bmp
Normal file
BIN
src/chicken.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
217
src/dpmi.rs
217
src/dpmi.rs
@ -1,9 +1,11 @@
|
||||
use core::{arch::asm, fmt::{Arguments, Write}};
|
||||
#![allow(dead_code)]
|
||||
use core::{arch::asm, fmt::{Arguments, Write}, ffi::CStr, str::FromStr};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
pub struct DpmiRegs {
|
||||
pub edi: u32,
|
||||
pub esi: u32,
|
||||
@ -29,19 +31,21 @@ impl DpmiRegs {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// BIOS function INT 10,0 Set video mode
|
||||
pub fn set_video_mode(mode: u8) {
|
||||
let mut regs = DpmiRegs::zero();
|
||||
regs.eax = mode as u32;
|
||||
real_int(0x10, &mut regs);
|
||||
}
|
||||
|
||||
// BIOS function INT 16,0 Wait for keystroke and read
|
||||
pub fn getchar() -> u16 {
|
||||
let mut regs = DpmiRegs::zero();
|
||||
real_int(0x16, &mut regs);
|
||||
regs.eax as u16
|
||||
}
|
||||
|
||||
// BIOS function INT 16,1 Get keyboard status
|
||||
pub fn kb_status() -> Option<u16> {
|
||||
let mut regs = DpmiRegs::zero();
|
||||
regs.eax = 0x100;
|
||||
@ -55,17 +59,167 @@ pub fn kb_status() -> Option<u16> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_psp() -> *const u8 {
|
||||
let psp: u32;
|
||||
pub fn get_psp(buff: &mut [u8; 256]) {
|
||||
// DOS DPMI function 21h,AH 62h - Get PSP Selector
|
||||
// Out: EBX = PSP selector
|
||||
// this really was hurting me so i just did it in assembly
|
||||
unsafe { asm!(
|
||||
"int 0x21",
|
||||
in("ax") 0x5100_u16,
|
||||
out("ebx") psp
|
||||
"push esi",
|
||||
"push edi",
|
||||
"mov ds, bx",
|
||||
"xor esi, esi",
|
||||
"mov ecx, 256",
|
||||
"rep movsb",
|
||||
"pop edi",
|
||||
"pop esi",
|
||||
"push es",
|
||||
"pop ds",
|
||||
in("ax") 0x6200_u16,
|
||||
out("ebx") _,
|
||||
in("edi") buff.as_mut_ptr(),
|
||||
); }
|
||||
(psp << 4) as *const u8
|
||||
}
|
||||
|
||||
pub fn get_args() -> alloc::vec::Vec<alloc::string::String> {
|
||||
let mut buff = [0_u8; 256];
|
||||
get_psp(&mut buff);
|
||||
let cmd_byte_cnt = buff[0x80];
|
||||
let cmd_line = &buff[0x81..0x100.min(0x81+cmd_byte_cnt as usize)];
|
||||
// this is a horrible way of doing this, but i'm lazy
|
||||
core::str::from_utf8(cmd_line).unwrap()
|
||||
.split_whitespace()
|
||||
.map(|s| alloc::string::String::from_str(s).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
handle: u32,
|
||||
size: u32
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn open(string: &CStr) -> Option<Self> {
|
||||
// DOS DPMI function 21h, AH 3Dh - Open File
|
||||
// In:
|
||||
// AL = access mode
|
||||
// DS:EDX = pointer to ASCIIZ file name
|
||||
// Out:
|
||||
// if successful:
|
||||
// CF clear
|
||||
// EAX = file handle
|
||||
//
|
||||
// if failed:
|
||||
// CF set
|
||||
// EAX = DOS error code
|
||||
let err: u8;
|
||||
let eax: u32;
|
||||
unsafe { asm!(
|
||||
"int 0x21",
|
||||
"setc bl",
|
||||
inout("eax") 0x00003D00_u32 => eax,
|
||||
in("edx") string.as_ptr(),
|
||||
inout("bl") 0_u8 => err
|
||||
);}
|
||||
|
||||
// Error TODO return error code instead of printing
|
||||
if err == 1 {
|
||||
crate::println!("Could not open file \"{}\" ({}).", string.to_str().unwrap(), eax);
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut file = Self {
|
||||
handle: eax,
|
||||
size: 0
|
||||
};
|
||||
let size = file.find_size()?;
|
||||
file.size = size;
|
||||
|
||||
return Some(file);
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u32 { self.size }
|
||||
|
||||
fn seek(&mut self, origin: u8) -> Option<u32> {
|
||||
// DOS DPMI function 21h, AH 42h - Set Current File Position
|
||||
// In:
|
||||
// AH = 42h
|
||||
// AL = origin of move:
|
||||
//
|
||||
// 00h = start
|
||||
// 01h = current
|
||||
// 02h = end
|
||||
//
|
||||
// ECX:EDX = file position
|
||||
// Out:
|
||||
// if successful:
|
||||
// CF clear
|
||||
// EDX:EAX = current file position
|
||||
//
|
||||
// if failed:
|
||||
// CF set
|
||||
// EAX = DOS error code
|
||||
let err: u32;
|
||||
let eax: u32;
|
||||
let edx: u32;
|
||||
unsafe { asm!(
|
||||
"int 0x21",
|
||||
"setc bl",
|
||||
inout("eax") 0x00004200_u32 | origin as u32 => eax,
|
||||
in("ecx") 0,
|
||||
inout("edx") 0 => edx,
|
||||
inout("ebx") self.handle => err
|
||||
);}
|
||||
|
||||
// Error TODO return error code
|
||||
if err == 1 { return None }
|
||||
|
||||
return Some(eax | (edx << 16));
|
||||
}
|
||||
|
||||
fn find_size(&mut self) -> Option<u32> {
|
||||
let size = self.seek(2);
|
||||
self.seek(0)?;
|
||||
return size;
|
||||
}
|
||||
|
||||
pub fn read(&mut self, buffer: &mut [u8]) -> Option<u32> {
|
||||
// DOS DPMI function 21h, AH 3Fh - Read File
|
||||
// In:
|
||||
// AH = 3Fh
|
||||
// EBX = file handle
|
||||
// ECX = number of bytes to read (size)
|
||||
// DS:EDX = pointer to buffer to read to (addr)
|
||||
// Out:
|
||||
// if successful:
|
||||
// CF clear
|
||||
// EAX = number of bytes read
|
||||
//
|
||||
// if failed:
|
||||
// CF set
|
||||
// EAX = DOS error code
|
||||
let err: u32;
|
||||
let eax: u32;
|
||||
unsafe { asm!(
|
||||
"int 0x21",
|
||||
"mov ebx, 0",
|
||||
"setc bl",
|
||||
inout("eax") 0x00003F00_u32 => eax,
|
||||
inout("ebx") self.handle => err,
|
||||
in("ecx") buffer.len(),
|
||||
in("edx") buffer.as_mut_ptr(),
|
||||
);}
|
||||
|
||||
// Error TODO return error code
|
||||
if err == 1 { return None }
|
||||
|
||||
return Some(eax);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn real_int(int: u8, regs: &mut DpmiRegs) {
|
||||
// DPMI function 0300h - Simulate Real Mode Interrupt
|
||||
// TODO get error codes from AX/CF
|
||||
unsafe { asm!(
|
||||
"int 0x31",
|
||||
in("bx") 0x0000_u16 | int as u16,
|
||||
@ -75,6 +229,52 @@ pub fn real_int(int: u8, regs: &mut DpmiRegs) {
|
||||
);}
|
||||
}
|
||||
|
||||
pub struct IntHandler {
|
||||
interrupt: u8,
|
||||
selector: u16,
|
||||
offset: u32
|
||||
}
|
||||
impl IntHandler {
|
||||
pub fn new(interrupt: u8) -> Self {
|
||||
// get old handler
|
||||
// DPMI function 0204h - Get Protected Mode Interrupt Vector
|
||||
let selector: u16;
|
||||
let offset: u32;
|
||||
unsafe { asm!(
|
||||
"int 0x31",
|
||||
in("ax") 0x0204,
|
||||
in("bl") interrupt,
|
||||
out("edx") offset,
|
||||
out("cx") selector
|
||||
); }
|
||||
Self { interrupt, offset, selector }
|
||||
}
|
||||
pub fn set_handler(&mut self, f: extern "x86-interrupt" fn()) {
|
||||
// install new handler
|
||||
// DPMI function 0205h - Set Protected Mode Interrupt Vector
|
||||
let func_ptr = f as *const () as u32;
|
||||
unsafe { asm!(
|
||||
"mov cx, cs",
|
||||
"int 0x31",
|
||||
inout("ax") 0x0205 => _,
|
||||
in("bl") self.interrupt,
|
||||
in("edx") func_ptr,
|
||||
out("cx") _
|
||||
); }
|
||||
}
|
||||
pub fn restore_handler(&mut self) {
|
||||
// restore original handler
|
||||
// DPMI function 0205h - Set Protected Mode Interrupt Vector
|
||||
unsafe { asm!(
|
||||
"int 0x31",
|
||||
inout("ax") 0x0205 => _,
|
||||
in("bl") self.interrupt,
|
||||
in("cx") self.selector,
|
||||
in("edx") self.offset,
|
||||
); }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dpmi_print(string: &str) {
|
||||
// DOS DPMI function 21h, AH 9h
|
||||
// Prints string pointed to
|
||||
@ -106,14 +306,13 @@ macro_rules! print {
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => (_raw_print!("\r\n$"));
|
||||
() => ($crate::_raw_print!("\r\n$"));
|
||||
($($arg:tt)*) => ($crate::_raw_print!("{}\r\n$", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn _print(args: Arguments) {
|
||||
let mut s: alloc::string::String = alloc::string::String::new();
|
||||
//let mut s: heapless::String<20> = heapless::String::new();
|
||||
s.write_fmt(args).unwrap();
|
||||
dpmi_print(s.as_str());
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ use core::fmt::Write;
|
||||
use heapless::String;
|
||||
|
||||
pub struct DpmiAlloc { }
|
||||
impl DpmiAlloc { const fn new() -> Self { DpmiAlloc { } } }
|
||||
impl DpmiAlloc {
|
||||
const fn new() -> Self { DpmiAlloc {} }
|
||||
}
|
||||
|
||||
unsafe impl core::alloc::GlobalAlloc for DpmiAlloc {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
@ -24,6 +26,7 @@ unsafe impl core::alloc::GlobalAlloc for DpmiAlloc {
|
||||
"int 0x21",
|
||||
"mov eax, esi",
|
||||
"mov esi, edx",
|
||||
// We can calculate the handle ourself
|
||||
inout("eax") 0x0FF91 => _handle,
|
||||
inout("ebx") layout.size() as u32 => ptr,
|
||||
out("edx") _
|
||||
@ -47,7 +50,7 @@ unsafe impl core::alloc::GlobalAlloc for DpmiAlloc {
|
||||
"int 0x21",
|
||||
"mov esi, edx",
|
||||
in("eax") 0x0FF92,
|
||||
in("ebx") ptr as u32 - 0x10, // handle should be just 0x10 less than the pointer
|
||||
in("ebx") ptr as u32 - 0x10, // in dos32a the handle should be just 0x10 less than the pointer
|
||||
out("edx") _
|
||||
);
|
||||
}
|
||||
|
149
src/main.rs
149
src/main.rs
@ -10,25 +10,154 @@ extern crate alloc;
|
||||
mod dpmi;
|
||||
mod dpmi_alloc;
|
||||
mod panic;
|
||||
mod vga;
|
||||
mod bmp;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
debug_trap!();
|
||||
println!("Done.");
|
||||
}
|
||||
use alloc::{vec, ffi::CString};
|
||||
use bmp::Bmp;
|
||||
use vga::Mode13hDisplay;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! debug_trap {
|
||||
() => { unsafe { asm!("int 0x1"); } };
|
||||
}
|
||||
const TEST_BMP: &[u8; 5318] = include_bytes!("chicken.bmp");
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn start() {
|
||||
unsafe { asm!(
|
||||
"mov ax, es",
|
||||
"push ds",
|
||||
"pop es",
|
||||
); }
|
||||
main();
|
||||
dpmi::dpmi_exit();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = dpmi::get_args();
|
||||
let filename = {
|
||||
if args.is_empty() || args[0].is_empty() {
|
||||
println!("Filename required.");
|
||||
println!("Press Q to exit, or any key to continue with the default image.");
|
||||
match dpmi::getchar() as u8 {
|
||||
b'q' | b'Q' => return,
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
Some(&*args[0])
|
||||
}
|
||||
};
|
||||
|
||||
// Try to load BMP file from filename, or else use the included test image
|
||||
let bmp = {
|
||||
let mut bmp_buff;
|
||||
let src = if let Some(filename) = filename {
|
||||
println!("Loading BMP from {}...", filename);
|
||||
let mut file = match dpmi::File::open(&CString::new(filename).unwrap()) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
println!("Could not open file.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
println!("File size: {} bytes", file.get_size());
|
||||
|
||||
bmp_buff = vec![0; file.get_size() as usize];
|
||||
file.read(&mut bmp_buff);
|
||||
|
||||
&bmp_buff[..]
|
||||
} else {
|
||||
TEST_BMP
|
||||
};
|
||||
if let Some(bmp) = bmp::load_bmp(src) {
|
||||
bmp
|
||||
} else {
|
||||
println!("Could not open BMP. Exiting");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!("Width x Height x BPP: {}x{}x{}", bmp.header.width, bmp.header.height, bmp.header.bpp);
|
||||
println!("Colors Used, Important: {},{}", bmp.header.colors_used, bmp.header.colors_important);
|
||||
println!("Arrow keys to move, 1-9 to change speed, Q to exit.");
|
||||
println!("Press any key to continue.");
|
||||
dpmi::getchar();
|
||||
|
||||
// mode 13h, 320x200 256 color graphics
|
||||
dpmi::set_video_mode(0x13);
|
||||
// Get screen buffer
|
||||
let mut vga = Mode13hDisplay::default();
|
||||
|
||||
// sets the VGA screen palette to the BMP color palette
|
||||
vga::set_vga_dac_colors(0, &bmp.palette_table);
|
||||
|
||||
// set up new keyboard handler
|
||||
// could do getchar, but this is more fun
|
||||
let mut kb_handler = dpmi::IntHandler::new(9);
|
||||
kb_handler.set_handler(keyboard_int_handler);
|
||||
|
||||
let mut last_scancode = 0xFF;
|
||||
let mut delta = 1;
|
||||
let mut pos = Position { x: 0, y: 0 };
|
||||
loop {
|
||||
let scancode = { *SCANCODE.read() };
|
||||
if scancode != last_scancode {
|
||||
match scancode {
|
||||
0x48 => { pos.y += delta; }, // up
|
||||
0x4B => { pos.x -= delta; }, // left
|
||||
0x4D => { pos.x += delta; }, // right
|
||||
0x50 => { pos.y -= delta; }, // down
|
||||
s @ 0x02..=0x0A => { delta = s as isize - 1; }, // 1-9
|
||||
0x10 => break, // q
|
||||
_ => {}
|
||||
}
|
||||
last_scancode = scancode;
|
||||
draw_loop(&mut vga, &bmp, &pos);
|
||||
}
|
||||
// halt processor so we don't burn the CPU
|
||||
unsafe { asm!("hlt"); }
|
||||
}
|
||||
|
||||
// restore old keyboard handler
|
||||
kb_handler.restore_handler();
|
||||
|
||||
// mode 3h, text mode graphics, DOS default
|
||||
dpmi::set_video_mode(0x3);
|
||||
}
|
||||
|
||||
fn draw_loop(vga: &mut Mode13hDisplay, bmp: &Bmp, pos: &Position) {
|
||||
vga.clear();
|
||||
vga.copy_to_screen(pos.x, pos.y, bmp.header.width as usize, bmp.header.height as usize, &bmp.data);
|
||||
// TODO wait for blanking interval
|
||||
vga.flush();
|
||||
}
|
||||
|
||||
struct Position {
|
||||
x: isize,
|
||||
y: isize,
|
||||
}
|
||||
|
||||
static SCANCODE: spin::RwLock<u8> = spin::RwLock::new(0);
|
||||
pub extern "x86-interrupt" fn keyboard_int_handler() {
|
||||
let old_ds: u16;
|
||||
unsafe { asm!(
|
||||
"mov bx, ds",
|
||||
"mov ax, es",
|
||||
"mov ds, ax",
|
||||
out("bx") old_ds
|
||||
); }
|
||||
|
||||
let code: u8;
|
||||
unsafe { asm!(
|
||||
"in al, 0x60",
|
||||
out("al") code
|
||||
); }
|
||||
|
||||
let mut scan = SCANCODE.write();
|
||||
*scan = code;
|
||||
drop(scan);
|
||||
|
||||
unsafe { asm!(
|
||||
"mov ds, bx",
|
||||
"out 0x20, al",
|
||||
in("al") 0x20_u8,
|
||||
in("bx") old_ds
|
||||
); }
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::dpmi::dpmi_exit;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
//crate::println!("Panic! {}", info);
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
crate::println!("Panic! {}", info);
|
||||
dpmi_exit();
|
||||
}
|
||||
|
||||
|
87
src/vga.rs
Normal file
87
src/vga.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use core::arch::asm;
|
||||
|
||||
#[derive(Copy,Clone,Default,PartialEq)]
|
||||
pub struct Vga18 {
|
||||
pub red: u8,
|
||||
pub green: u8,
|
||||
pub blue: u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mode13h_vga_arr() -> &'static mut [[u8; 320]; 200] {
|
||||
unsafe { &mut *(0xa0000 as *mut [[u8; 320]; 200]) }
|
||||
}
|
||||
|
||||
unsafe fn outb(port: u16, data: u8) {
|
||||
asm! {
|
||||
"out dx, al",
|
||||
in("dx") port,
|
||||
in("al") data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vga_dac_colors(start_index: u8, colors: &[Vga18]) {
|
||||
if colors.is_empty() { return }
|
||||
unsafe { outb(0x3c8, start_index); }
|
||||
for (i, &Vga18 { red, green, blue }) in colors.iter().enumerate() {
|
||||
if i + start_index as usize >= 256 {
|
||||
break
|
||||
}
|
||||
unsafe {
|
||||
outb(0x3c9, red);
|
||||
outb(0x3c9, green);
|
||||
outb(0x3c9, blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mode13hDisplay {
|
||||
buffer: [[u8; 320]; 200]
|
||||
}
|
||||
|
||||
impl Default for Mode13hDisplay {
|
||||
fn default() -> Self {
|
||||
Self { buffer: [[0; 320]; 200] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mode13hDisplay {
|
||||
#[allow(unused)]
|
||||
pub fn flush(&self) {
|
||||
let vga = mode13h_vga_arr();
|
||||
*vga = self.buffer;
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.buffer = [[0; 320]; 200];
|
||||
}
|
||||
|
||||
pub fn copy_to_screen(&mut self, screen_col: isize, screen_line: isize, src_width: usize, src_height: usize, bytes: &[u8]) {
|
||||
let x = screen_col;
|
||||
let y = screen_line;
|
||||
|
||||
for l in 0..src_height {
|
||||
let l_y = l as isize + y;
|
||||
// past screen
|
||||
if l_y >= 200 { break; }
|
||||
// before screen
|
||||
else if l_y < 0 { continue; }
|
||||
// past screen
|
||||
if -x >= src_width as isize { break; }
|
||||
|
||||
let line_len = if x >= 0 {
|
||||
(320 - x).min(src_width as isize)
|
||||
} else {
|
||||
320.min(src_width as isize + x)
|
||||
};
|
||||
|
||||
let src_off = if x >= 0 {
|
||||
l * src_width
|
||||
} else { ((l * src_width) as isize - x) as usize };
|
||||
|
||||
let x_adj = x.max(0);
|
||||
|
||||
self.buffer[l_y as usize][x_adj as usize..x_adj as usize+line_len as usize].copy_from_slice(&bytes[src_off..src_off+line_len as usize]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user