1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
use crate::arch::x86::io;
use compiler_builtins::mem::memcpy;
const VGA_BUFFER_POINTER: *mut u8 = 0xB8000 as *mut u8;
const VGA_BUFFER_SIZE: u16 = 4000;
const ROW_COUNT: u8 = 25;
const COL_COUNT: u8 = 80;
const VALUE_SIZE: u8 = 2;
pub(crate) struct Terminal {
current_row: u8,
current_col: u8,
}
impl Terminal {
pub fn new() -> Self {
Self {
current_row: 0,
current_col: 0,
}
}
pub fn put_string(&mut self, string: &[u8]) {
for &character in string {
self.put_char(character);
}
self.set_cursor_position(self.current_row, self.current_col);
}
pub fn put_char(&mut self, c: u8) {
if c == b'\n' {
if self.current_row < ROW_COUNT - 1 {
self.current_row += 1;
} else {
self.scroll_y();
}
self.current_col = 0;
return;
}
// Safety: This is safe because VGA_BUFFER_POINTER points toward the VGA character buffer
unsafe {
*VGA_BUFFER_POINTER.offset(
((self.current_col + self.current_row * COL_COUNT) * VALUE_SIZE) as isize,
) = c;
}
self.compute_current_position();
}
fn compute_current_position(&mut self) {
if self.current_col == COL_COUNT - 1 {
if self.current_row != ROW_COUNT - 1 {
self.current_row = (self.current_row + 1) % ROW_COUNT;
} else {
self.scroll_y();
}
self.current_col = 0;
} else {
self.current_col = (self.current_col + 1) % COL_COUNT;
}
}
fn scroll_y(&mut self) {
// Safety:
// The starting pointer is in bound because it's the VGA character buffer pointer
// The end pointer isn't past the VGA character buffer pointer
// The offset doesn't overflow isize
// The offset doesn't rely on wrapping around the address space
let src = unsafe { VGA_BUFFER_POINTER.offset(COL_COUNT as isize * VALUE_SIZE as isize) };
let dest = VGA_BUFFER_POINTER;
let size = (VGA_BUFFER_SIZE - COL_COUNT as u16) * VALUE_SIZE as u16;
// Safety:
// dest and src are valid pointer to the same object being the VGA character buffer
// This copy will not go past the VGA character buffer
unsafe { memcpy(dest, src, size as usize) };
self.clear_line(ROW_COUNT - 1);
}
pub fn put_char_at(&mut self, c: u8, row: u8, col: u8) {
self.current_row = row;
self.current_col = col;
unsafe {
*VGA_BUFFER_POINTER.offset(
(self.current_col as isize + self.current_row as isize * COL_COUNT as isize)
* VALUE_SIZE as isize,
) = c;
}
}
pub fn clear(&mut self) {
for row in 0..ROW_COUNT {
self.clear_line(row)
}
self.current_col = 0;
self.current_row = 0;
}
fn clear_line(&mut self, row: u8) {
for col in 0..COL_COUNT {
self.put_char_at(b' ', row, col);
}
self.current_row = row;
self.current_col = 0;
}
pub fn set_cursor_position(&mut self, row: u8, col: u8) {
let cursor_position = row as u16 * COL_COUNT as u16 + col as u16;
let address_register = 0x3D4;
let data_register = 0x3D5;
unsafe {
io::out8(address_register, 0x0F);
io::out8(data_register, (cursor_position & 0xFF) as u8);
io::out8(address_register, 0x0E);
io::out8(data_register, ((cursor_position >> 8) & 0xFF) as u8);
}
}
}
|