From 9c17546cc64c1a3ce14d490173796f3ba12de55f Mon Sep 17 00:00:00 2001 From: Lucia Ceionia Date: Fri, 17 Feb 2023 01:53:13 -0600 Subject: [PATCH] Initial commit --- .cargo/config.toml | 7 +++ .gitignore | 2 + Cargo.lock | 7 +++ Cargo.toml | 17 +++++++ Makefile | 15 ++++++ README.md | 3 ++ lucia.json | 30 ++++++++++++ lucia.ld | 20 ++++++++ src/main.rs | 68 ++++++++++++++++++++++++++ src/panic.rs | 7 +++ src/system.rs | 117 +++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 293 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 README.md create mode 100644 lucia.json create mode 100644 lucia.ld create mode 100644 src/main.rs create mode 100644 src/panic.rs create mode 100644 src/system.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..212eaf2 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,7 @@ +[unstable] +build-std = ["core", "compiler_builtins"] +build-std-features = ["compiler-builtins-mem", "panic_immediate_abort"] + +[build] +target = "lucia.json" +rustflags = [] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f4bf88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +*.com diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fd7b3b8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust-luciaos" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..725937f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rust-luciaos" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = "z" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..55a85d0 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +EXECNAME = rust-luciaos + +all: target/lucia/release/$(EXECNAME).o + clang -m32 -nostartfiles -nostdlib -Tlucia.ld -ffreestanding -o $(EXECNAME).com $< + +debug: target/lucia/debug/$(EXECNAME).o + clang -m32 -nostartfiles -nostdlib -Tlucia.ld -ffreestanding -o $(EXECNAME).com $< + +.PHONY: target/lucia/release/$(EXECNAME).o +target/lucia/release/$(EXECNAME).o: + cargo build --release + +.PHONY: target/lucia/debug/$(EXECNAME).o +target/lucia/debug/$(EXECNAME).o: + cargo build diff --git a/README.md b/README.md new file mode 100644 index 0000000..c24d57f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +A short example program written in Rust for LuciaOS + +`make` to build. diff --git a/lucia.json b/lucia.json new file mode 100644 index 0000000..1ca9656 --- /dev/null +++ b/lucia.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", + "exe-suffix": ".o", + "dynamic-linking": false, + "executables": true, + "linker-flavor": "gcc", + "linker-is-gnu": true, + "llvm-target": "i386-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", + "--entry=start" + ] + }, + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "32", + "os": "none", + "vendor": "unknown", + "panic-strategy": "abort" +} diff --git a/lucia.ld b/lucia.ld new file mode 100644 index 0000000..734e586 --- /dev/null +++ b/lucia.ld @@ -0,0 +1,20 @@ +OUTPUT_FORMAT(binary) +SECTIONS +{ + . = 0x800000; + .entry : { + *(.entry) + } + .text : { + *(.text); + } + .rodata : { + *(.rodata) + } + .data : { + *(.data) + } + .bss : { + *(.bss) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ae9a66b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,68 @@ +#![no_main] +#![no_std] + +use system::*; + +mod system; +mod panic; + +#[no_mangle] +unsafe extern "C" +fn __atomic_load_8(ptr: *const u8, _val: u8, _ordering: isize) -> u8 { + ptr.read() +} +#[no_mangle] +unsafe extern "C" +fn __atomic_store_8(ptr: *mut u8, val: u8, _ordering: isize) { + ptr.write(val); +} + +fn main() { + // Get text mode VGA memory as 80x25 u16 array + let vga = unsafe { &mut *(0xB8000 as *mut [[u16; 80]; 25]) }; + // Fill VGA memory with empty chars, BG=2 FG=f + *vga = [[0x2f00; 80]; 25]; + // Copy a string to VGA memory, one byte at a time + for (i, c) in "Hello, world! (from Rust!) Press q to exit :3".as_bytes().iter().enumerate() { + // OR to not overwrite the character attributes + vga[0][i] |= *c as u16; + } + // Wait for key 'q' + while sys_getkey() != b'q' {} + + // --- Draw Trans Pride Flag --- + // Set VGA 320x200 256 indexed color mode + bios_set_video_mode(0x13); + // Set colors to display with + // VGA DAC uses 18-bit color, RGB + bios_set_dac_color_reg(0, 0x16, 0x33, 0x3e); + bios_set_dac_color_reg(1, 0x3d, 0x2a, 0x2e); + bios_set_dac_color_reg(2, 0x3f, 0x3f, 0x3f); + bios_set_dac_color_reg(3, 0x3d, 0x2a, 0x2e); + bios_set_dac_color_reg(4, 0x16, 0x33, 0x3e); + // Get graphics mode VGA memory as 320x200 u8 array + let vga = unsafe { &mut *(0xA0000 as *mut [[u8; 320]; 200]) }; + // Split the screen into 5 equal chunks, then + // fill all rows of the chunk i with color i. + // This gives the pattern we want, since we + // set the colors + for (i, c) in vga.chunks_mut(200/5).enumerate() { + c.fill([i as u8; 320]); + } + // Wait for key 'q' + while sys_getkey() != b'q' {} + + // We can't copy text to memory in graphics mode, + // so write a string with BIOS. Color 1 to show up over chunk 0. + bios_print_str("Writing w/ BIOS in graphics mode :3", 1, 2, 1); + // Wait for key 'q' + while sys_getkey() != b'q' {} +} + +#[no_mangle] +#[link_section = ".entry"] +// This is where the program starts +pub extern "C" fn start() { + main(); + sys_exit(0x0CA7F00D); +} diff --git a/src/panic.rs b/src/panic.rs new file mode 100644 index 0000000..7c55079 --- /dev/null +++ b/src/panic.rs @@ -0,0 +1,7 @@ +use crate::system::sys_exit; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + sys_exit(0xFFFFFFFF); +} + diff --git a/src/system.rs b/src/system.rs new file mode 100644 index 0000000..eb05d1b --- /dev/null +++ b/src/system.rs @@ -0,0 +1,117 @@ +use core::arch::asm; + +// Used for Virtual 8086 mode system calls +#[repr(C, packed)] +pub struct Regs { + pub ebp: u32, + pub edi: u32, + pub esi: u32, + pub ebx: u32, + pub edx: u32, + pub ecx: u32, + pub eax: u32, +} + +// --- BIOS Function Definitions --- + +// Sets VGA video mode +pub fn bios_set_video_mode(mode: u8) { + let regs = Regs { + ebp: 0, + edi: 0, + esi: 0, + ebx: 0, + edx: 0, + ecx: 0, + eax: mode as u32, // set video mode, INT 10 AH=0h AL=mode + }; + sys_v86_int(0x10, ®s); +} + +// Prints a string to display at provided location in given color +pub fn bios_print_str(string: &str, row: u8, column: u8, color: u8) { + let ptr = sys_v86_data_pointer(); + ptr[..string.as_bytes().len()].copy_from_slice(string.as_bytes()); + let regs = Regs { + ebp: ptr.as_ptr() as u32 & 0xffff, // ES:BP=pointer to string + edi: 0, + esi: 0, + ebx: color as u32, // BL=pixel color (gfx mode), attribute (text mode) + edx: ((row as u32) << 8) | (column as u32), // DH=row DL=column + ecx: string.as_bytes().len() as u32, // CX=length + eax: 0x1300 // write string, INT 10 AH=13h + }; + sys_v86_int(0x10, ®s); +} + +// Sets a VGA DAC color index, takes 6-bit values for each color component +pub fn bios_set_dac_color_reg(index: u16, red: u8, green: u8, blue: u8) { + let regs = Regs { + ebp: 0, + edi: 0, + esi: 0, + ebx: index as u32, + edx: (red as u32) << 8, // DH=red + ecx: blue as u32 | (green as u32) << 8, // CL=blue CH=green + eax: 0x1010, // set DAC register, INT 10 AH=10h AL=10h + }; + sys_v86_int(0x10, ®s); +} + +// --- System Call Definitions --- + +pub fn sys_v86_data_pointer() -> &'static mut [u8; 0x10000] { + // System call: Get Virtual 8086 mode data pointer: int 21, AX=86D8 + // Returns in EAX a 32-bit flat pointer to the memory which will be loaded + // in DS & ES in Virtual 8086 calls. E.g. When this function returns 0x30000, + // accessing 0x30100 in Virtual 8086 mode would be DS:0100h. + // Since 16-bit code can only access 64KiB at a time without changing segments, + // this pointer refers to a 64KiB chunk of memory addressable by Virtual 8086 tasks. + let ptr: u32; + unsafe { + asm!("int 0x21", + inout("eax") 0x86D8 => ptr, + clobber_abi("C")); + return &mut *(ptr as *mut [u8; 0x10000]) + } +} + +// Calls a BIOS (or other real mode) interrupt with CPU state defined in regs +pub fn sys_v86_int(interrupt: u8, regs: &Regs) { + // System call: Virtual 8086 mode interrupt: int 21, AL=86 + // Stack contains interrupt value and pointer to Regs struct. + // Values from Regs struct are loaded for the Virtual 8086 task. + unsafe { asm!( + "push ecx", + "push ebx", + "int 0x21", + "add esp, 8", + inout("eax") 0x86 => _, + inout("ebx") interrupt as u32 => _, + inout("ecx") regs as *const Regs => _, + clobber_abi("C") + ); } +} + +// Quit it. +pub fn sys_exit(exit_code: u32) -> ! { + // System call Exit: int 30 + // Exit code in EAX. + unsafe { asm!( + "int 0x30", + in("eax") exit_code + ); } + loop {} +} + +// Get key press in ASCII +pub fn sys_getkey() -> u8 { + // System call Get Key: int 21, AL=0 + // Returns ASCII key pressed in AL. + let key: u32; + unsafe { asm!( + "int 0x21", + inout("eax") 0 => key, + ); } + return key as u8; +}