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