commit 9c17546cc64c1a3ce14d490173796f3ba12de55f Author: Lucia Ceionia Date: Fri Feb 17 01:53:13 2023 -0600 Initial commit 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; +}