summaryrefslogtreecommitdiffstats
path: root/kernel/src/terminal.rs
blob: 1045b09e076d00dd3a5335a494238a741dfa5bb6 (plain) (blame)
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);
        }
    }
}
Go back to lisible.xyz