Initial commit

This commit is contained in:
Lucia Ceionia 2023-02-17 01:53:13 -06:00
commit 9c17546cc6
11 changed files with 293 additions and 0 deletions

7
.cargo/config.toml Normal file
View File

@ -0,0 +1,7 @@
[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem", "panic_immediate_abort"]
[build]
target = "lucia.json"
rustflags = []

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
*.com

7
Cargo.lock generated Normal file
View File

@ -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"

17
Cargo.toml Normal file
View File

@ -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"

15
Makefile Normal file
View File

@ -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

3
README.md Normal file
View File

@ -0,0 +1,3 @@
A short example program written in Rust for LuciaOS
`make` to build.

30
lucia.json Normal file
View File

@ -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"
}

20
lucia.ld Normal file
View File

@ -0,0 +1,20 @@
OUTPUT_FORMAT(binary)
SECTIONS
{
. = 0x800000;
.entry : {
*(.entry)
}
.text : {
*(.text);
}
.rodata : {
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}

68
src/main.rs Normal file
View File

@ -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);
}

7
src/panic.rs Normal file
View File

@ -0,0 +1,7 @@
use crate::system::sys_exit;
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
sys_exit(0xFFFFFFFF);
}

117
src/system.rs Normal file
View File

@ -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, &regs);
}
// 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, &regs);
}
// 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, &regs);
}
// --- 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;
}