Skip to content

Commit 90ce11b

Browse files
committed
Add support for 320x200 256 color VGA video mode
1 parent ddc89c2 commit 90ce11b

11 files changed

+300
-41
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ x86_64 = "0.2.7"
1515
usize_conversions = "0.2.0"
1616
fixedvec = "0.2.3"
1717

18+
[dependencies.font8x8]
19+
version = "0.2.4"
20+
default-features = false
21+
features = ["unicode"]
22+
23+
[features]
24+
default = []
25+
vga_320x200 = []
26+
1827
[profile.dev]
1928
panic = "abort"
2029

src/main.rs

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern crate x86_64;
1414
extern crate xmas_elf;
1515
#[macro_use]
1616
extern crate fixedvec;
17+
extern crate font8x8;
1718

1819
use bootloader::bootinfo::BootInfo;
1920
use core::panic::PanicInfo;
@@ -32,6 +33,11 @@ global_asm!(include_str!("stage_3.s"));
3233
global_asm!(include_str!("stage_4.s"));
3334
global_asm!(include_str!("context_switch.s"));
3435

36+
#[cfg(feature = "vga_320x200")]
37+
global_asm!(include_str!("video_mode/vga_320x200.s"));
38+
#[cfg(not(feature = "vga_320x200"))]
39+
global_asm!(include_str!("video_mode/vga_text_80x25.s"));
40+
3541
extern "C" {
3642
fn context_switch(boot_info: VirtAddr, entry_point: VirtAddr, stack_pointer: VirtAddr) -> !;
3743
}

src/printer/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#[cfg(not(feature = "vga_320x200"))]
2+
pub use self::vga_text_80x25::*;
3+
4+
#[cfg(feature = "vga_320x200")]
5+
pub use self::vga_320x200::*;
6+
7+
mod vga_text_80x25;
8+
mod vga_320x200;

src/printer/vga_320x200.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use core::fmt::{Result, Write};
2+
use core::slice;
3+
use core::sync::atomic::{AtomicUsize, Ordering};
4+
5+
const VGA_BUFFER: *mut u8 = 0xa0000 as *mut _;
6+
const SCREEN_WIDTH: usize = 320;
7+
const SCREEN_HEIGHT: usize = 200;
8+
9+
pub static X_POS: AtomicUsize = AtomicUsize::new(1); // must not be 0 so that we don't have a .bss section
10+
pub static Y_POS: AtomicUsize = AtomicUsize::new(1); // must not be 0 so that we don't have a .bss section
11+
12+
pub struct Printer;
13+
14+
impl Printer {
15+
pub fn clear_screen(&mut self) {
16+
let vga_buffer = Self::vga_buffer();
17+
for byte in vga_buffer {
18+
*byte = 0x00;
19+
}
20+
X_POS.store(0, Ordering::SeqCst);
21+
Y_POS.store(0, Ordering::SeqCst);
22+
}
23+
24+
fn vga_buffer() -> &'static mut [u8] {
25+
unsafe { slice::from_raw_parts_mut(VGA_BUFFER, SCREEN_WIDTH * SCREEN_HEIGHT) }
26+
}
27+
28+
fn newline(&mut self) {
29+
let y_pos = Y_POS.fetch_add(8, Ordering::SeqCst);
30+
X_POS.store(0, Ordering::SeqCst);
31+
if y_pos >= SCREEN_HEIGHT {
32+
self.clear_screen();
33+
}
34+
}
35+
36+
fn write_char(&mut self, c: char) {
37+
use font8x8::{self, UnicodeFonts};
38+
39+
if c == '\n' {
40+
self.newline();
41+
return;
42+
}
43+
44+
let vga_buffer = Self::vga_buffer();
45+
46+
let x_pos = X_POS.fetch_add(8, Ordering::SeqCst);
47+
let y_pos = Y_POS.load(Ordering::SeqCst);
48+
49+
match c {
50+
' '..='~' => {
51+
let rendered = font8x8::BASIC_FONTS
52+
.get(c)
53+
.expect("character not found in basic font");
54+
for (y, byte) in rendered.iter().enumerate() {
55+
for (x, bit) in (0..8).enumerate() {
56+
if *byte & (1 << bit) == 0 {
57+
continue
58+
}
59+
let color = 0xf;
60+
vga_buffer[(y_pos + y) * SCREEN_WIDTH + x_pos + x] = color;
61+
}
62+
}
63+
}
64+
_ => panic!("unprintable character"),
65+
}
66+
67+
if x_pos + 8 >= SCREEN_WIDTH {
68+
self.newline();
69+
}
70+
}
71+
}
72+
73+
impl Write for Printer {
74+
fn write_str(&mut self, s: &str) -> Result {
75+
for c in s.chars() {
76+
self.write_char(c);
77+
}
78+
79+
Ok(())
80+
}
81+
}
File renamed without changes.

src/stage_1.s

-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ rest_of_bootloader_load_failed:
182182

183183
boot_start_str: .asciz "Booting (first stage)..."
184184
error_str: .asciz "Error: "
185-
no_cpuid_str: .asciz "No CPUID support"
186185
no_int13h_extensions_str: .asciz "No support for int13h extensions"
187186
rest_of_bootloader_load_failed_str: .asciz "Failed to load rest of bootloader"
188187

src/stage_2.s

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ create_memory_map:
8686
lea di, es:[_memory_map]
8787
call do_e820
8888

89+
video_mode_config:
90+
call config_video_mode
91+
8992
enter_protected_mode_again:
9093
cli
9194
lgdt [gdt32info]

src/stage_3.s

+12-40
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,8 @@ stage_3:
1313
mov es, bx # set extra segment
1414
mov ss, bx # set stack segment
1515

16-
# print "3rd stage" to the top right
17-
mov eax, 0x0f720f33 # "3r"
18-
mov [0xb808c], eax
19-
mov eax, 0x0f200f64 # "d "
20-
mov [0xb808c + 4], eax
21-
mov eax, 0x0f740f73 # "st"
22-
mov [0xb808c + 8], eax
23-
mov eax, 0x0f670f61 # "ag"
24-
mov [0xb808c + 12], eax
25-
mov eax, 0x0f200f65 # "e "
26-
mov [0xb808c + 16], eax
16+
lea si, boot_third_stage_str
17+
call vga_println
2718

2819
check_cpu:
2920
call check_cpuid
@@ -86,13 +77,8 @@ set_up_page_tables:
8677
add ecx, 1
8778
cmp ecx, edx
8879
jb map_p1_table
89-
#
90-
map_vga_buffer:
91-
mov eax, 0xb8000
92-
or eax, (1 | 2)
93-
mov ecx, 0xb8000
94-
shr ecx, 12
95-
mov [_p1 + ecx * 8], eax
80+
map_framebuffer:
81+
call vga_map_frame_buffer
9682

9783
enable_paging:
9884
# Write back cache and add a memory fence. I'm not sure if this is
@@ -165,15 +151,8 @@ check_cpuid:
165151
je no_cpuid
166152
ret
167153
no_cpuid:
168-
# print "no cpuid" to the top right
169-
mov eax, 0x4f6f4f6e # "no"
170-
mov [0xb8130], eax
171-
mov eax, 0x4f634f20 # " c"
172-
mov [0xb8130 + 4], eax
173-
mov eax, 0x4f754f70 # "pu"
174-
mov [0xb8130 + 8], eax
175-
mov eax, 0x4f644f69 # "id"
176-
mov [0xb8130 + 12], eax
154+
lea esi, no_cpuid_str
155+
call vga_println
177156
no_cpuid_spin:
178157
hlt
179158
jmp no_cpuid_spin
@@ -192,19 +171,8 @@ check_long_mode:
192171
jz no_long_mode # If it's not set, there is no long mode
193172
ret
194173
no_long_mode:
195-
# print "no long mode" to the top right
196-
mov eax, 0x4f6f4f6e # "no"
197-
mov [0xb8128], eax
198-
mov eax, 0x4f6c4f20 # " l"
199-
mov [0xb8128 + 4], eax
200-
mov eax, 0x4f6e4f6f # "on"
201-
mov [0xb8128 + 8], eax
202-
mov eax, 0x4f204f67 # "g "
203-
mov [0xb8128 + 12], eax
204-
mov eax, 0x4f6f4f6d # "mo"
205-
mov [0xb8128 + 16], eax
206-
mov eax, 0x4f654f64 # "de"
207-
mov [0xb8128 + 20], eax
174+
lea esi, no_long_mode_str
175+
call vga_println
208176
no_long_mode_spin:
209177
hlt
210178
jmp no_long_mode_spin
@@ -226,3 +194,7 @@ gdt_64:
226194
gdt_64_pointer:
227195
.word gdt_64_pointer - gdt_64 - 1 # 16-bit Size (Limit) of GDT.
228196
.long gdt_64 # 32-bit Base Address of GDT. (CPU will zero extend to 64-bit)
197+
198+
boot_third_stage_str: .asciz "Booting (third stage)..."
199+
no_cpuid_str: .asciz "Error: CPU does not support CPUID"
200+
no_long_mode_str: .asciz "Error: CPU does not support long mode"

src/video_mode/vga_320x200.s

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
.section .boot, "awx"
2+
.intel_syntax noprefix
3+
.code16
4+
5+
config_video_mode:
6+
mov ah, 0
7+
mov al, 0x13 # 320x200 256 color graphics
8+
int 0x10
9+
ret
10+
11+
.code32
12+
13+
vga_map_frame_buffer:
14+
mov eax, 0xa0000
15+
or eax, (1 | 2)
16+
vga_map_frame_buffer_loop:
17+
mov ecx, eax
18+
shr ecx, 12
19+
mov [_p1 + ecx * 8], eax
20+
21+
add eax, 4096
22+
cmp eax, 0xa0000 + 320 * 200
23+
jl vga_map_frame_buffer_loop
24+
25+
ret
26+
27+
# print a string and a newline
28+
# IN
29+
# esi: points at zero-terminated String
30+
vga_println:
31+
push eax
32+
push ebx
33+
push ecx
34+
push edx
35+
36+
call vga_print
37+
38+
# newline
39+
mov edx, 0
40+
mov eax, vga_position
41+
mov ecx, 80 * 2
42+
div ecx
43+
add eax, 1
44+
mul ecx
45+
mov vga_position, eax
46+
47+
pop edx
48+
pop ebx
49+
pop ecx
50+
pop eax
51+
52+
ret
53+
54+
# print a string
55+
# IN
56+
# esi: points at zero-terminated String
57+
# CLOBBER
58+
# ah, ebx
59+
vga_print:
60+
cld
61+
vga_print_loop:
62+
# note: if direction flag is set (via std)
63+
# this will DECREMENT the ptr, effectively
64+
# reading/printing in reverse.
65+
lodsb al, BYTE PTR [esi]
66+
test al, al
67+
jz vga_print_done
68+
call vga_print_char
69+
jmp vga_print_loop
70+
vga_print_done:
71+
ret
72+
73+
74+
# print a character
75+
# IN
76+
# al: character to print
77+
# CLOBBER
78+
# ah, ebx
79+
vga_print_char:
80+
mov ebx, vga_position
81+
mov ah, 0x0f
82+
mov [ebx + 0xa0000], ax
83+
84+
add ebx, 2
85+
mov [vga_position], ebx
86+
87+
ret
88+
89+
vga_position:
90+
.double 0

0 commit comments

Comments
 (0)