Initial commit
This commit is contained in:
commit
9c17546cc6
7
.cargo/config.toml
Normal file
7
.cargo/config.toml
Normal 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
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
*.com
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
17
Cargo.toml
Normal 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
15
Makefile
Normal 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
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
A short example program written in Rust for LuciaOS
|
||||
|
||||
`make` to build.
|
30
lucia.json
Normal file
30
lucia.json
Normal 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
20
lucia.ld
Normal 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
68
src/main.rs
Normal 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
7
src/panic.rs
Normal 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
117
src/system.rs
Normal 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, ®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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user