Compare commits
	
		
			No commits in common. "ebbd8da3332cc93a719f5af2379dfe9f58324314" and "07346f758c4fc15dd24e3f99e082f6d81049271f" have entirely different histories.
		
	
	
		
			ebbd8da333
			...
			07346f758c
		
	
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,4 @@ | |||
| target | ||||
| /kernel/Cargo.lock | ||||
| /kernel/target | ||||
| /build | ||||
| /binutils | ||||
| /gcc | ||||
|  |  | |||
							
								
								
									
										2
									
								
								kernel/std/Cargo.lock → kernel/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								kernel/std/Cargo.lock → kernel/Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -3,5 +3,5 @@ | |||
| version = 3 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "std" | ||||
| name = "kernel" | ||||
| version = "0.1.0" | ||||
|  | @ -3,8 +3,4 @@ name = "kernel" | |||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [workspace] | ||||
| members = ["std"] | ||||
| 
 | ||||
| [dependencies] | ||||
| std = { path = "std" } | ||||
|  |  | |||
|  | @ -1,16 +1,22 @@ | |||
| #![no_std] | ||||
| #![no_main] | ||||
| 
 | ||||
| use std::println; | ||||
| 
 | ||||
| #[no_mangle] | ||||
| pub extern "C" fn kernel_main() -> ! { | ||||
|     let a = 4; | ||||
|     println!("Hiii :3"); | ||||
|     println!("{}", a); | ||||
|     let vga_buffer = 0xb8000 as *mut u8; | ||||
| 
 | ||||
|     let string: &[u8] = b":3"; | ||||
|     for (i, &byte) in string.iter().enumerate() { | ||||
|         unsafe { | ||||
|             *vga_buffer.offset(i as isize * 2) = byte; | ||||
|             *vga_buffer.offset(i as isize * 2 + 1) = 0xf; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     loop {} | ||||
| } | ||||
| 
 | ||||
|  
 | ||||
| #[panic_handler] | ||||
| fn panic(_info: &core::panic::PanicInfo) -> ! { | ||||
|     loop {} | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| [package] | ||||
| name = "std" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [dependencies] | ||||
| lazy_static = { version = "1.4.0", features = ["spin_no_std"] } | ||||
| spin = "0.9.8" | ||||
|  | @ -1,3 +0,0 @@ | |||
| #![no_std] | ||||
| 
 | ||||
| pub mod vga_buffer; | ||||
|  | @ -1,132 +0,0 @@ | |||
| use core::fmt::{self, Write}; | ||||
| 
 | ||||
| use lazy_static::lazy_static; | ||||
| use spin::Mutex; | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(u8)] | ||||
| pub enum Color { | ||||
|     Black = 0, | ||||
|     Blue = 1, | ||||
|     Green = 2, | ||||
|     Cyan = 3, | ||||
|     Red = 4, | ||||
|     Magenta = 5, | ||||
|     Brown = 6, | ||||
|     LightGray = 7, | ||||
|     DarkGray = 8, | ||||
|     LightBlue = 9, | ||||
|     LightGreen = 10, | ||||
|     LightCyan = 11, | ||||
|     LightRed = 12, | ||||
|     Pink = 13, | ||||
|     Yellow = 14, | ||||
|     White = 15, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(transparent)] | ||||
| pub struct ColorCode(u8); | ||||
| 
 | ||||
| impl ColorCode { | ||||
|     pub fn new(foreground: Color, background: Color) -> ColorCode { | ||||
|         ColorCode((background as u8) << 4 | (foreground as u8)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(C)] | ||||
| struct ScreenChar { | ||||
|     ascii_character: u8, | ||||
|     color_code: ColorCode, | ||||
| } | ||||
| 
 | ||||
| const BUFFER_HEIGHT: usize = 25; | ||||
| const BUFFER_WIDTH: usize = 80; | ||||
| 
 | ||||
| #[repr(transparent)] | ||||
| struct Buffer { | ||||
|     chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT], | ||||
| } | ||||
| 
 | ||||
| pub struct Writer { | ||||
|     col: usize, | ||||
|     row: usize, | ||||
|     buffer: &'static mut Buffer, | ||||
| } | ||||
| 
 | ||||
| impl Writer { | ||||
|     pub fn write_byte(&mut self, byte: u8) { | ||||
|         self.write_byte_color(byte, ColorCode::new(Color::White, Color::Black)); | ||||
|     } | ||||
| 
 | ||||
|     pub fn write_byte_color(&mut self, byte: u8, color: ColorCode) { | ||||
|         match byte { | ||||
|             b'\n' => self.new_line(), | ||||
|             byte => { | ||||
|                 if self.col >= BUFFER_WIDTH { | ||||
|                     self.new_line(); | ||||
|                 } | ||||
| 
 | ||||
|                 let row = self.row; | ||||
|                 let col = self.col; | ||||
| 
 | ||||
|                 let color_code = color; | ||||
|                 self.buffer.chars[row][col] = ScreenChar { | ||||
|                     ascii_character: byte, | ||||
|                     color_code, | ||||
|                 }; | ||||
|                 self.col += 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn write_string(&mut self, s: &str) { | ||||
|         self.write_string_color(s, ColorCode::new(Color::White, Color::Black)); | ||||
|     } | ||||
| 
 | ||||
|     pub fn write_string_color(&mut self, s: &str, color: ColorCode) { | ||||
|         for char in s.bytes() { | ||||
|             match char { | ||||
|                 0x20..=0x7e | b'\n' => self.write_byte_color(char, color), | ||||
|                 _ => self.write_byte(0xfe), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn new_line(&mut self) { | ||||
|         self.row += 1; | ||||
|         self.col = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Write for Writer { | ||||
|     fn write_str(&mut self, s: &str) -> fmt::Result { | ||||
|         self.write_string(s); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| lazy_static! { | ||||
|     pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer { | ||||
|         col: 0, | ||||
|         row: 0, | ||||
|         buffer: unsafe { &mut *(0xB8000 as *mut Buffer) }, | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| #[doc(hidden)] | ||||
| pub fn _print(args: fmt::Arguments) { | ||||
|     WRITER.lock().write_fmt(args).unwrap() | ||||
| } | ||||
| 
 | ||||
| #[macro_export] | ||||
| macro_rules! print { | ||||
|     ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); | ||||
| } | ||||
| 
 | ||||
| #[macro_export] | ||||
| macro_rules! println { | ||||
|     () => ($crate::print!("\n")); | ||||
|     ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); | ||||
| } | ||||
|  | @ -350,15 +350,6 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { | |||
| # Building the kernel | ||||
| To build the kernel I just run `cargo build` in the `kernel` directory. This will produce a binary called `kernel/target/i686-bare-metal/debug/kernel.elf`. I now have a multiboot enabled kernel :) | ||||
| 
 | ||||
| # Grub | ||||
| Now to turn the kernel into a ISO file with a bootloader, I used grub. To configure grub I needed to create a directory for it. I made `build/isodir/boot/grub/grub.cfg` containing this: | ||||
| ``` | ||||
| menuentry "geos" { | ||||
| 	multiboot /boot/geos.bin | ||||
| } | ||||
| ``` | ||||
| Then I copied `kernel/target/i686-bare-metal/debug/kernel.elf` to `isodir/boot/geos.bin`. Then I ran `grub-mkrescue -o build/geos.iso build/isodir` to make an ISO containing my OS and grub. Yippee | ||||
| 
 | ||||
| # File structure | ||||
| ``` | ||||
| geos/ | ||||
|  |  | |||
|  | @ -1,189 +0,0 @@ | |||
| # VGA text | ||||
| Hello. Today I succesfully made a `println!` macro. | ||||
| 
 | ||||
| ## std | ||||
| To following conventions I decided to make a `std` library. To do this I made a workspace in `kernel/`. | ||||
| 
 | ||||
| `kernel/Cargo.toml` | ||||
| ```toml | ||||
| [package] | ||||
| name = "kernel" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [workspace] | ||||
| members = ["std"] | ||||
| 
 | ||||
| [dependencies] | ||||
| std = { path = "std" } | ||||
| ``` | ||||
| 
 | ||||
| ## lib.rs | ||||
| To not have one big spaghetti file I separated out all the individal modules into different files. | ||||
| `kernel/std/src/lib.rs` | ||||
| ```rust | ||||
| #![no_std] | ||||
| 
 | ||||
| pub mod vga_buffer; | ||||
| ``` | ||||
| 
 | ||||
| ## vga_buffer.rs | ||||
| First step for a VGA text driver is to be able to represent data. | ||||
| 
 | ||||
| `kernel/std/src/lib.rs` | ||||
| ```rust | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(u8)] | ||||
| pub enum Color { | ||||
|     Black = 0, | ||||
|     Blue = 1, | ||||
|     Green = 2, | ||||
|     Cyan = 3, | ||||
|     Red = 4, | ||||
|     Magenta = 5, | ||||
|     Brown = 6, | ||||
|     LightGray = 7, | ||||
|     DarkGray = 8, | ||||
|     LightBlue = 9, | ||||
|     LightGreen = 10, | ||||
|     LightCyan = 11, | ||||
|     LightRed = 12, | ||||
|     Pink = 13, | ||||
|     Yellow = 14, | ||||
|     White = 15, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(transparent)] // Store this as a u8 | ||||
| pub struct ColorCode(u8); | ||||
| 
 | ||||
| impl ColorCode { | ||||
|     pub fn new(foreground: Color, background: Color) -> ColorCode { | ||||
|         ColorCode((background as u8) << 4 | (foreground as u8)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(C)] // Tells rust to store this like a struct in C, i.e. store this as 2 bytes | ||||
| struct ScreenChar { | ||||
|     ascii_character: u8, | ||||
|     color_code: ColorCode, | ||||
| } | ||||
| 
 | ||||
| const BUFFER_HEIGHT: usize = 25; | ||||
| const BUFFER_WIDTH: usize = 80; | ||||
| 
 | ||||
| #[repr(transparent)] | ||||
| struct Buffer { | ||||
|     chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT], | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Now I can represent the VGA text buffer. To write to this I want to make a writer struct that implements `fmt::Write`. | ||||
| ```rust | ||||
| pub struct Writer { | ||||
|     col: usize, | ||||
|     row: usize, | ||||
|     buffer: &'static mut Buffer, | ||||
| } | ||||
| 
 | ||||
| impl Writer { | ||||
|     pub fn write_byte(&mut self, byte: u8) { | ||||
|         self.write_byte_color(byte, ColorCode::new(Color::White, Color::Black)); | ||||
|     } | ||||
| 
 | ||||
|     pub fn write_byte_color(&mut self, byte: u8, color: ColorCode) { | ||||
|         match byte { | ||||
|             b'\n' => self.new_line(), | ||||
|             byte => { | ||||
|                 if self.col >= BUFFER_WIDTH { | ||||
|                     self.new_line(); | ||||
|                 } | ||||
| 
 | ||||
|                 let row = self.row; | ||||
|                 let col = self.col; | ||||
| 
 | ||||
|                 let color_code = color; | ||||
|                 self.buffer.chars[row][col] = ScreenChar { | ||||
|                     ascii_character: byte, | ||||
|                     color_code, | ||||
|                 }; | ||||
|                 self.col += 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn write_string(&mut self, s: &str) { | ||||
|         self.write_string_color(s, ColorCode::new(Color::White, Color::Black)); | ||||
|     } | ||||
| 
 | ||||
|     pub fn write_string_color(&mut self, s: &str, color: ColorCode) { | ||||
|         for char in s.bytes() { | ||||
|             match char { | ||||
|                 0x20..=0x7e | b'\n' => self.write_byte_color(char, color), // Ascii Character | ||||
|                 _ => self.write_byte(0xfe), // Non ascii | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn new_line(&mut self) { | ||||
|         self.row += 1; | ||||
|         self.col = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Write for Writer { | ||||
|     fn write_str(&mut self, s: &str) -> fmt::Result { | ||||
|         self.write_string(s); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| The problem now becomes that I can create a `Writer` but I don't have a global position, so it will just overwrite what I wrote last time. To solve this I can use a static variable. But a static variable won't be mutable. This can be solved with `Mutex` in the spin crate and `lazy_static`. | ||||
| 
 | ||||
| `kernel/std/Cargo.toml` | ||||
| ```toml | ||||
| [package] | ||||
| name = "std" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [dependencies] | ||||
| lazy_static = { version = "1.4.0", features = ["spin_no_std"] } | ||||
| spin = "0.9.8" | ||||
| ``` | ||||
| 
 | ||||
| and to make a static variable I wrote (copied from [here](https://os.phil-opp.com/vga-text-mode/)) this | ||||
| ```rust | ||||
| lazy_static! { | ||||
|     pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer { | ||||
|         col: 0, | ||||
|         row: 0, | ||||
|         buffer: unsafe { &mut *(0xB8000 as *mut Buffer) }, | ||||
|     }); | ||||
| } | ||||
| ``` | ||||
| in `kernel/std/src/vga_buffer.rs`. | ||||
| 
 | ||||
| Last step is just to add a function and 2 macros | ||||
| ```rust | ||||
| #[doc(hidden)] | ||||
| pub fn _print(args: fmt::Arguments) { | ||||
|     WRITER.lock().write_fmt(args).unwrap() | ||||
| } | ||||
| 
 | ||||
| #[macro_export] | ||||
| macro_rules! print { | ||||
|     ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*))); | ||||
| } | ||||
| 
 | ||||
| #[macro_export] | ||||
| macro_rules! println { | ||||
|     () => ($crate::print!("\n")); | ||||
|     ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Huge thanks to @phil-opp for [os.phil-opp.com](https://os.phil-opp.com/) | ||||
| 
 | ||||
		Loading…
	
		Reference in a new issue