strat9_kernel/arch/x86_64/vga/
debug_overlay.rs1use super::panic_screen::{
6 panic_pack_rgb, PANIC_FB_ADDR, PANIC_FB_FORMAT, PANIC_FB_PITCH, PANIC_FONT_8X16,
7};
8
9static VGA_DEBUG_COL: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
14static VGA_DEBUG_ROW: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
16
17static VGA_DEBUG_MAX_COL: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
19static VGA_DEBUG_MAX_ROW: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
21
22pub fn vga_debug_init(fb_width: usize, fb_height: usize) {
25 let cols = fb_width / 8; let rows = fb_height / 16; let status_rows = 1; VGA_DEBUG_MAX_COL.store(cols, core::sync::atomic::Ordering::Relaxed);
29 VGA_DEBUG_MAX_ROW.store(
30 rows.saturating_sub(status_rows),
31 core::sync::atomic::Ordering::Relaxed,
32 );
33 VGA_DEBUG_COL.store(0, core::sync::atomic::Ordering::Relaxed);
34 VGA_DEBUG_ROW.store(0, core::sync::atomic::Ordering::Relaxed);
35}
36
37fn vga_debug_scroll() {
42 let fb_addr = PANIC_FB_ADDR.load(core::sync::atomic::Ordering::Acquire);
43 if fb_addr == 0 {
44 return;
45 }
46 let pitch = PANIC_FB_PITCH.load(core::sync::atomic::Ordering::Relaxed) as usize;
47 let max_row = VGA_DEBUG_MAX_ROW.load(core::sync::atomic::Ordering::Relaxed);
48 let fmt = PANIC_FB_FORMAT.load(core::sync::atomic::Ordering::Relaxed);
49 let bpp = (fmt >> 32) as u16;
50 let bpp_bytes = (bpp / 8) as usize;
51 if bpp_bytes < 3 || pitch == 0 || max_row <= 1 {
52 return;
53 }
54
55 let fb = fb_addr as *mut u8;
56 let max_col = VGA_DEBUG_MAX_COL.load(core::sync::atomic::Ordering::Relaxed);
57 let line_bytes = 16 * pitch; let total = (max_row - 1) * line_bytes; let bg = panic_pack_rgb(fmt, 0x12, 0x16, 0x1E);
60
61 unsafe {
62 core::ptr::copy(fb.add(line_bytes), fb, total);
64 let clear = fb.add((max_row - 1) * line_bytes);
66 vga_debug_fill_line(clear, pitch, max_col, bpp_bytes, bg);
67 }
68}
69
70unsafe fn vga_debug_fill_line(
73 base: *mut u8,
74 pitch: usize,
75 max_col: usize,
76 bpp_bytes: usize,
77 color: u32,
78) {
79 let fill_pixels = max_col * 8; if bpp_bytes == 4 {
81 for row in 0..16 {
82 let r = base.add(row * pitch) as *mut u32;
83 for p in 0..fill_pixels {
84 core::ptr::write_volatile(r.add(p), color);
85 }
86 }
87 } else {
88 let fill_bytes = fill_pixels * 3;
89 for row in 0..16 {
90 let r = base.add(row * pitch);
91 for x in (0..fill_bytes).step_by(3) {
92 core::ptr::write_volatile(r.add(x), color as u8);
93 core::ptr::write_volatile(r.add(x + 1), (color >> 8) as u8);
94 core::ptr::write_volatile(r.add(x + 2), (color >> 16) as u8);
95 }
96 }
97 }
98}
99
100unsafe fn vga_debug_clear_cell(
102 fb: *mut u8,
103 pitch: usize,
104 x: usize,
105 y: usize,
106 bpp_bytes: usize,
107 bg: u32,
108) {
109 if bpp_bytes == 4 {
110 let x_off = x * 4;
111 for row in 0..16 {
112 let r = fb.add((y + row) * pitch + x_off) as *mut u32;
113 for c in 0..8 {
114 core::ptr::write_volatile(r.add(c), bg);
115 }
116 }
117 } else {
118 for row in 0..16 {
119 let r = fb.add((y + row) * pitch + x * 3);
120 for c in 0..8 {
121 let p = r.add(c * 3);
122 core::ptr::write_volatile(p, bg as u8);
123 core::ptr::write_volatile(p.add(1), (bg >> 8) as u8);
124 core::ptr::write_volatile(p.add(2), (bg >> 16) as u8);
125 }
126 }
127 }
128}
129
130unsafe fn vga_debug_draw_glyph(
135 fb: *mut u8,
136 pitch: usize,
137 x: usize,
138 y: usize,
139 ch: u8,
140 fg: u32,
141 bg: u32,
142 bpp_bytes: usize,
143) {
144 vga_debug_clear_cell(fb, pitch, x, y, bpp_bytes, bg);
146
147 let idx = if ch < 0x20 || ch > 0x7E {
148 0usize
149 } else {
150 (ch - 0x20) as usize
151 };
152 let gs = idx * 16;
153
154 if bpp_bytes == 4 {
155 for row in 0..16 {
156 let mask = PANIC_FONT_8X16[gs + row];
157 if mask == 0 {
158 continue;
159 }
160 let r = fb.add((y + row) * pitch + x * 4) as *mut u32;
161 for col in 0..8 {
162 if (mask >> (7 - col)) & 1 != 0 {
163 core::ptr::write_volatile(r.add(col), fg);
164 }
165 }
166 }
167 } else {
168 for row in 0..16 {
169 let mask = PANIC_FONT_8X16[gs + row];
170 if mask == 0 {
171 continue;
172 }
173 for col in 0..8 {
174 if (mask >> (7 - col)) & 1 != 0 {
175 let p = fb.add((y + row) * pitch + (x + col) * 3);
176 core::ptr::write_volatile(p, fg as u8);
177 core::ptr::write_volatile(p.add(1), (fg >> 8) as u8);
178 core::ptr::write_volatile(p.add(2), (fg >> 16) as u8);
179 }
180 }
181 }
182 }
183}
184
185pub fn vga_debug_write(s: &str) {
194 let fb_addr = PANIC_FB_ADDR.load(core::sync::atomic::Ordering::Acquire);
195 if fb_addr == 0 {
196 return;
197 }
198
199 let pitch = PANIC_FB_PITCH.load(core::sync::atomic::Ordering::Relaxed) as usize;
200 let max_col = VGA_DEBUG_MAX_COL.load(core::sync::atomic::Ordering::Relaxed);
201 let max_row = VGA_DEBUG_MAX_ROW.load(core::sync::atomic::Ordering::Relaxed);
202 let fmt = PANIC_FB_FORMAT.load(core::sync::atomic::Ordering::Relaxed);
203 let bpp = (fmt >> 32) as u16;
204 let bpp_bytes = (bpp / 8) as usize;
205 if max_col == 0 || max_row == 0 || pitch == 0 || bpp_bytes < 3 {
206 return;
207 }
208
209 let fb = fb_addr as *mut u8;
210
211 let fg = panic_pack_rgb(fmt, 0xE2, 0xE8, 0xF0);
213 let bg = panic_pack_rgb(fmt, 0x12, 0x16, 0x1E);
214
215 let mut col = VGA_DEBUG_COL.load(core::sync::atomic::Ordering::Relaxed);
216 let mut row = VGA_DEBUG_ROW.load(core::sync::atomic::Ordering::Relaxed);
217
218 for &byte in s.as_bytes() {
219 match byte {
220 b'\n' => {
221 col = 0;
222 row += 1;
223 if row >= max_row {
224 vga_debug_scroll();
225 row = max_row - 1;
226 }
227 }
228 b'\r' => {
229 col = 0;
230 }
231 b'\t' => {
232 col = (col + 8) & !7;
233 if col >= max_col {
234 col = 0;
235 row += 1;
236 }
237 if row >= max_row {
238 vga_debug_scroll();
239 row = max_row - 1;
240 }
241 }
242 _ => {
243 if col >= max_col {
244 col = 0;
245 row += 1;
246 if row >= max_row {
247 vga_debug_scroll();
248 row = max_row - 1;
249 }
250 }
251 unsafe {
253 vga_debug_draw_glyph(fb, pitch, col * 8, row * 16, byte, fg, bg, bpp_bytes);
254 }
255 col += 1;
256 }
257 }
258 }
259
260 VGA_DEBUG_COL.store(col, core::sync::atomic::Ordering::Relaxed);
261 VGA_DEBUG_ROW.store(row, core::sync::atomic::Ordering::Relaxed);
262}
263
264pub fn vga_debug_writeln(s: &str) {
266 let mut buf: [u8; 512] = [0u8; 512];
267 let max = buf.len() - 1;
268 let len = if s.len() > max {
269 let mut truncated = max;
271 let bytes = s.as_bytes();
272 while truncated > 0 && (bytes[truncated] & 0xC0) == 0x80 {
273 truncated -= 1;
274 }
275 truncated
276 } else {
277 s.len()
278 };
279 buf[..len].copy_from_slice(&s.as_bytes()[..len]);
280 buf[len] = b'\n';
281 if let Ok(valid) = core::str::from_utf8(&buf[..len + 1]) {
282 vga_debug_write(valid);
283 }
284}