From 5e07492252d1896c4182cd9237f0ab953ffa22ce Mon Sep 17 00:00:00 2001 From: Lucia Ceionia Date: Thu, 6 Jul 2023 15:23:26 -0500 Subject: [PATCH] initial commit --- .cargo/config.toml | 7 ++ .gitignore | 1 + Cargo.toml | 22 ++++++ dos.json | 30 +++++++ rust-toolchain | 1 + src/dpmi.rs | 120 ++++++++++++++++++++++++++++ src/dpmi_alloc.rs | 192 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 34 ++++++++ src/panic.rs | 8 ++ 9 files changed, 415 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 dos.json create mode 100644 rust-toolchain create mode 100644 src/dpmi.rs create mode 100644 src/dpmi_alloc.rs create mode 100644 src/main.rs create mode 100644 src/panic.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..b24da66 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,7 @@ +[unstable] +build-std = ["core", "compiler_builtins", "alloc"] +build-std-features = ["compiler-builtins-mem"] + +[build] +target = "dos.json" +rustflags = ["-C", "opt-level=z"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f260c4b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "rust-le-demo" +version = "0.1.0" +authors = ["lucia"] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +heapless = { version = "0.7.16", features = ["ufmt-impl"] } +spin = "0.9.4" + + +[profile.dev] +panic = "abort" +lto = "fat" +codegen-units = 1 + +[profile.release] +panic = "abort" +lto = "fat" +codegen-units = 1 diff --git a/dos.json b/dos.json new file mode 100644 index 0000000..ded751b --- /dev/null +++ b/dos.json @@ -0,0 +1,30 @@ +{ + "arch": "x86", + "cpu": "i386", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", + "dynamic-linking": false, + "executables": true, + "exe-suffix": ".elf", + "linker-flavor": "gcc", + "linker-is-gnu": true, + "llvm-target": "i586-unknown-none-none", + "max-atomic-width": 64, + "position-independent-executables": false, + "relocation-model": "static", + "disable-redzone": true, + "pre-link-args": { + "gcc": [ + "-m32", + "-nostdlib", + "-march=i386", + "-Wl,-r" + ] + }, + "relro-level": "full", + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "32", + "os": "none", + "vendor": "unknown", + "panic-strategy": "abort" +} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/src/dpmi.rs b/src/dpmi.rs new file mode 100644 index 0000000..58eda8c --- /dev/null +++ b/src/dpmi.rs @@ -0,0 +1,120 @@ +use core::{arch::asm, fmt::{Arguments, Write}}; + +extern crate alloc; + +#[allow(dead_code)] +#[repr(packed)] +pub struct DpmiRegs { + pub edi: u32, + pub esi: u32, + pub ebp: u32, + reserved_zero: u32, + pub ebx: u32, + pub edx: u32, + pub ecx: u32, + pub eax: u32, + pub status_flags: u16, + pub es: u16, + pub ds: u16, + pub fs: u16, + pub gs: u16, + ip_ignored: u16, + cs_ignored: u16, + pub sp: u16, + pub ss: u16 +} +impl DpmiRegs { + pub const fn zero() -> Self { + DpmiRegs { edi: 0, esi: 0, ebp: 0, reserved_zero: 0, ebx: 0, edx: 0, ecx: 0, eax: 0, status_flags: 0, es: 0, ds: 0, fs: 0, gs: 0, ip_ignored: 0, cs_ignored: 0, sp: 0, ss: 0 } + } +} + +#[inline] +pub fn set_video_mode(mode: u8) { + let mut regs = DpmiRegs::zero(); + regs.eax = mode as u32; + real_int(0x10, &mut regs); +} + +pub fn getchar() -> u16 { + let mut regs = DpmiRegs::zero(); + real_int(0x16, &mut regs); + regs.eax as u16 +} + +pub fn kb_status() -> Option { + let mut regs = DpmiRegs::zero(); + regs.eax = 0x100; + real_int(0x16, &mut regs); + match regs.status_flags & 0x40 { + 0 => match regs.eax as u16 { + 0 => None, + v => Some(v), + }, + _ => None + } +} + +pub fn get_psp() -> *const u8 { + let psp: u32; + unsafe { asm!( + "int 0x21", + in("ax") 0x5100_u16, + out("ebx") psp + ); } + (psp << 4) as *const u8 +} + +pub fn real_int(int: u8, regs: &mut DpmiRegs) { + unsafe { asm!( + "int 0x31", + in("bx") 0x0000_u16 | int as u16, + in("cx") 0x0000_u16, + in("edi") regs, + inout("ax") 0x0300_u16 => _ + );} +} + +pub fn dpmi_print(string: &str) { + // DOS DPMI function 21h, AH 9h + // Prints string pointed to + // by EDX, terminated with $ + unsafe { asm!( + "int 0x21", + in("ah") 0x9_u8, + in("edx") string.as_ptr() + ); } +} + +pub fn dpmi_exit() -> ! { + // DOS DPMI function Exit: 21h, AH 4Ch + unsafe { asm!( + "int 0x21", + in("ah") 0x4C_u8 + ); } + loop {} +} + +#[macro_export] +#[doc(hidden)] +macro_rules! _raw_print { + ($($arg:tt)*) => ($crate::dpmi::_print(format_args!($($arg)*))); +} +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::_raw_print!("{}$", format_args!($($arg)*))); +} +#[macro_export] +macro_rules! println { + () => (_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()); +} + diff --git a/src/dpmi_alloc.rs b/src/dpmi_alloc.rs new file mode 100644 index 0000000..0718b53 --- /dev/null +++ b/src/dpmi_alloc.rs @@ -0,0 +1,192 @@ +use core::{ptr::null_mut, arch::asm, alloc::Layout}; +use core::fmt::Write; +use heapless::String; + +const PTR_COUNT: usize = 1024; + +struct Locked { + inner: spin::Mutex, +} +impl Locked { + pub const fn new(inner: A) -> Self { + Locked { + inner: spin::Mutex::new(inner) + } + } + pub fn lock(&self) -> spin::MutexGuard { + self.inner.lock() + } +} + +pub fn init_allocator() { unsafe { + ALLOCATOR.inner.force_unlock(); + let mut t = ALLOCATOR.lock(); + // 3.53 - DOS function 0FF91h - DOS/32 Advanced Allocate High Memory Block + // In: AX = 0FF91h + // EBX = size of memory block in bytes + // Out: + // if successful: + // CF clear + // EBX = linear address of allocated memory block + // ESI = handle of allocated memory block + // if failed: + // CF set + let ptr: u32; + let handle: u32; + asm!( + "mov edx, esi", + "int 0x21", + "mov eax, esi", + "mov esi, edx", + inout("eax") 0x0FF91 => handle, + inout("ebx") PTR_COUNT * core::mem::size_of::() => ptr, + out("edx") _ + ); + t.ptrs = ptr; + t.global_handle = handle; + let mut ptrs = core::slice::from_raw_parts_mut::(t.ptrs as *mut DpmiPtr, PTR_COUNT); + for p in ptrs.iter_mut() { + *p = DpmiPtr { ptr: 0, handle: 0 }; + } +} } + +#[derive(Copy, Clone)] +struct DpmiPtr { + ptr: u32, + handle: u32 +} +pub struct DpmiAlloc { + ptrs: u32, + global_handle: u32 +} +impl DpmiAlloc { + const fn new() -> Self { DpmiAlloc { ptrs: 0, global_handle: 0 } } +} + +unsafe impl core::alloc::GlobalAlloc for Locked { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let mut dat = self.lock(); + let mut ptrs = unsafe { core::slice::from_raw_parts_mut::(dat.ptrs as *mut DpmiPtr, PTR_COUNT) }; + let free_idx: usize = { + let mut tmp = None; + for (i, p) in ptrs.iter().enumerate() { + if p.ptr == 0 { tmp = Some(i); break; } + } + match tmp { + None => { asm!("int 0x1", in("eax") ptrs.as_ptr()); return null_mut(); } + Some(i) => i + } + }; + // 3.53 - DOS function 0FF91h - DOS/32 Advanced Allocate High Memory Block + // In: AX = 0FF91h + // EBX = size of memory block in bytes + // Out: + // if successful: + // CF clear + // EBX = linear address of allocated memory block + // ESI = handle of allocated memory block + // if failed: + // CF set + let ptr: u32; + let handle: u32; + asm!( + "mov edx, esi", + "int 0x21", + "mov eax, esi", + "mov esi, edx", + inout("eax") 0x0FF91 => handle, + inout("ebx") layout.size() as u32 => ptr, + out("edx") _ + ); + ptrs[free_idx] = DpmiPtr { ptr, handle }; + ptr as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + if ptr as u32 == 0 { return; } + let mut dat = self.lock(); + let mut ptrs = unsafe { core::slice::from_raw_parts_mut::(dat.ptrs as *mut DpmiPtr, PTR_COUNT) }; + let mut found_idx: Option = None; + for i in 0..ptrs.len() { + if ptrs[i].ptr == ptr as u32 { found_idx = Some(i); break; } + } + let handle = match found_idx { + None => return, + Some(i) => ptrs[i].handle + }; + // 3.54 - DOS function 0FF92h - DOS/32 Advanced Free High Memory Block + // In: AX = 0FF92h + // ESI = handle of previously allocated memory block + // Out: + // if successful: + // CF clear + // if failed: + // CF set + asm!( + "mov edx, esi", + "mov esi, ebx", + "int 0x21", + "mov esi, edx", + in("eax") 0x0FF92, + in("ebx") handle, + out("edx") _ + ); + ptrs[found_idx.unwrap()] = DpmiPtr { ptr: 0, handle: 0 }; + } + + unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 { + if ptr.is_null() { return null_mut(); } + let mut dat = self.lock(); + let mut ptrs = core::slice::from_raw_parts_mut::(dat.ptrs as *mut DpmiPtr, PTR_COUNT); + let mut found_idx: Option = None; + for i in 0..ptrs.len() { + if ptrs[i].ptr == ptr as u32 { found_idx = Some(i); break; } + } + let old_handle = match found_idx { + None => return null_mut(), + Some(i) => ptrs[i].handle + }; + // 3.55 - DOS function 0FF93h - DOS/32 Advanced Resize High Memory Block + // In: AX = 0FF93h + // EBX = new size of memory block in bytes + // ESI = handle of previously allocated memory block + // Out: + + // if successful: + // CF clear + // EBX = new linear address of allocated memory block + // ESI = new handle of allocated memory block + + // if failed: + // CF set + let ptr: u32; + let handle: u32; + asm!( + "xchg edx, esi", + "int 0x21", + "xchg esi, edx", + inout("eax") 0x0FF93 => _, + inout("ebx") new_size as u32 => ptr, + inout("edx") old_handle => handle + ); + ptrs[found_idx.unwrap()] = DpmiPtr { ptr, handle }; + ptr as *mut u8 + } +} + +#[global_allocator] +static ALLOCATOR: Locked = Locked::new(DpmiAlloc::new()); + +#[doc(hidden)] +pub fn _print(args: core::fmt::Arguments) { + let mut s: String<256> = String::new(); + s.write_fmt(args).unwrap(); + crate::dpmi::dpmi_print(s.as_str()); +} + +#[alloc_error_handler] +fn alloc_error_handler(layout: Layout) -> ! { + _print(format_args!("allocation error: {:?}$", layout)); + crate::dpmi::dpmi_exit(); +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5f7f648 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,34 @@ +#![feature(alloc_error_handler)] +#![feature(abi_x86_interrupt)] +#![no_main] +#![no_std] + +use core::arch::asm; + +extern crate alloc; + +mod dpmi; +mod dpmi_alloc; +mod panic; + +fn main() { + println!("Hello, world!"); + debug_trap!(); + println!("Done."); +} + +#[allow(unused_macros)] +#[macro_export] +macro_rules! debug_trap { + () => { unsafe { asm!("int 0x1"); } }; +} + +#[no_mangle] +pub extern "C" fn start() { + unsafe { asm!( + "push ds", + "pop es", + ); } + main(); + dpmi::dpmi_exit(); +} diff --git a/src/panic.rs b/src/panic.rs new file mode 100644 index 0000000..f2d5a66 --- /dev/null +++ b/src/panic.rs @@ -0,0 +1,8 @@ +use crate::dpmi::dpmi_exit; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + //crate::println!("Panic! {}", info); + dpmi_exit(); +} +