rustubs/machine/
cgascr.rs1use crate::machine::device_io::*;
2use crate::{arch::x86_64::misc::*, P2V};
3use core::{fmt, ptr, slice, str};
4
5const MAX_COLS: usize = 80;
13const MAX_ROWS: usize = 25;
14const CGA_BUFFER_START: usize = P2V(0xb8000).unwrap() as usize;
15
16pub struct CGAScreen {
17 pub cga_mem: &'static mut [u8],
18 cursor_r: usize,
19 cursor_c: usize,
20 attr: u8,
21}
22
23#[inline(always)]
24fn cal_offset(row: usize, col: usize) -> usize { col + row * MAX_COLS }
25
26impl CGAScreen {
27 const IR_PORT: IOPort = IOPort::new(0x3d4);
28 const DR_PORT: IOPort = IOPort::new(0x3d5);
29 pub fn new() -> Self {
30 let cga = Self {
31 cga_mem: unsafe {
32 slice::from_raw_parts_mut(
33 CGA_BUFFER_START as *mut u8,
34 2 * MAX_COLS * MAX_ROWS,
35 )
36 },
37 cursor_r: 0,
38 cursor_c: 0,
39 attr: 0x0f,
40 };
41 cga.init_cursor();
42 return cga;
43 }
44
45 #[inline(always)]
46 pub fn show(&mut self, row: usize, col: usize, c: char, attr: u8) {
49 let index = cal_offset(row, col);
50 self.cga_mem[index * 2] = c as u8;
51 self.cga_mem[index * 2 + 1] = attr;
52 }
53
54 fn putchar(&mut self, ch: char) {
59 match ch {
62 '\n' => self.newline(),
63 _ => {
64 self.show(self.cursor_r, self.cursor_c, ch, self.attr);
65 self.advance();
66 }
67 }
68 }
69
70 #[inline(always)]
71 pub fn backspace(&mut self) {
72 if self.cursor_c == 0 && self.cursor_r == 0 {
73 return;
74 }
75 if self.cursor_c == 0 {
76 self.cursor_r -= 1;
77 self.cursor_c = MAX_COLS;
78 } else {
79 self.cursor_c -= 1;
80 }
81 self.sync_cursor();
82 self.show(self.cursor_r, self.cursor_c, 0 as char, self.attr);
83 }
84
85 fn move_line(from: usize, to: usize) {
89 if !(0..MAX_ROWS).contains(&from) {
90 return;
91 }
92 if !(0..MAX_ROWS).contains(&to) {
93 return;
94 }
95 if from == to {
96 return;
97 }
98 let src: usize = CGA_BUFFER_START + from * 160;
100 let dst: usize = CGA_BUFFER_START + to * 160;
101 unsafe {
102 ptr::copy_nonoverlapping(src as *mut u64, dst as *mut u64, 20);
103 }
104 }
105
106 fn clearline(line: usize, attr: u8) {
108 let mut base: u64 = (attr as u64) << 8;
109 base += base << 16;
110 base += base << 32;
111 if !(0..MAX_ROWS).contains(&line) {
112 return;
113 }
114 unsafe {
115 slice::from_raw_parts_mut(
116 (CGA_BUFFER_START + line * 160) as *mut u64,
117 20,
118 )
119 .fill(base);
120 }
121 }
122
123 fn advance(&mut self) {
126 if self.cursor_c >= (MAX_COLS - 1) {
127 self.newline();
128 } else {
129 self.cursor_c += 1;
130 }
131 }
132
133 fn newline(&mut self) {
136 if self.cursor_r >= (MAX_ROWS - 1) {
137 self.scroll(1);
138 } else {
139 self.cursor_r += 1;
140 }
141 self.cursor_c = 0;
142 }
143
144 pub fn scroll(&self, lines: usize) {
145 if lines >= MAX_ROWS {
146 self.clear();
147 }
148
149 if lines == 0 {
150 return;
151 }
152 for i in lines..MAX_ROWS {
154 Self::move_line(i, i - lines);
155 }
156 for i in (MAX_ROWS - lines)..MAX_ROWS {
158 Self::clearline(i, self.attr);
159 }
160 }
161
162 pub fn clear(&self) {
163 for i in 0..MAX_ROWS {
164 Self::clearline(i, self.attr);
165 }
166 }
167
168 pub fn reset(&mut self) {
169 self.clear();
170 self.setpos(0, 0);
171 self.sync_cursor();
172 }
173
174 pub fn setpos(&mut self, row: usize, col: usize) {
178 self.cursor_r = row;
179 self.cursor_c = col;
180 }
181
182 fn sync_cursor(&self) {
183 let offset = cal_offset(self.cursor_r, self.cursor_c);
185 Self::IR_PORT.outb(15_u8);
187 delay();
188 Self::DR_PORT.outb(offset as u8);
189 Self::IR_PORT.outb(14_u8);
191 delay();
192 Self::DR_PORT.outb((offset >> 8) as u8);
193 }
194
195 pub fn init_cursor(&self) {
197 Self::IR_PORT.outb(0x0a);
198 delay();
199 let mut d = Self::DR_PORT.inb();
200 delay();
201 d &= 0xc0;
202 d |= 0xe;
203 Self::DR_PORT.outb(d);
204 delay();
205 Self::IR_PORT.outb(0x0b);
206 delay();
207 let mut d = Self::DR_PORT.inb();
208 d &= 0xe0;
209 d |= 0xf;
210 Self::DR_PORT.outb(d);
211 }
212
213 pub fn print(&mut self, s: &str) {
214 for c in s.bytes() {
215 self.putchar(c as char);
216 }
217 self.sync_cursor();
218 }
219
220 pub fn print_at_bottom(&mut self, s: &str, attr: u8) {
224 Self::clearline(24, self.attr);
225 let s = if s.len() >= MAX_COLS { &s[0..MAX_COLS] } else { s };
226 let orig_r = self.cursor_r;
228 let orig_c = self.cursor_c;
229 let orig_a = self.attr;
230 self.cursor_r = 24;
231 self.cursor_c = 0;
232 self.setattr(attr);
233 self.print(s);
234
235 self.setattr(orig_a);
236 self.cursor_r = orig_r;
237 self.cursor_c = orig_c;
238 }
239
240 pub fn setattr(&mut self, attr: u8) { self.attr = attr; }
241}
242
243impl fmt::Write for CGAScreen {
244 fn write_str(&mut self, s: &str) -> fmt::Result {
245 self.print(s);
246 Ok(())
247 }
248}