Nothing to see here, move along
at main 462 lines 14 kB view raw
1#![no_std] 2#![no_main] 3 4mod ansi; 5mod font; 6 7use ansi::{Action, Parser}; 8use lancer_user::show; 9use lancer_user::syscall; 10 11const FB_CAP_SLOT: u64 = 2; 12const CONSOLE_RING_BASE_SLOT: u64 = 64; 13const CONSOLE_RING_MAX_FRAMES: u64 = 16; 14const FB_MAP_VADDR: u64 = 0x4000_0000_0000; 15const CONSOLE_RING_VADDR: u64 = 0x5000_0000_0000; 16 17const DEFAULT_FG: u32 = 0x00CC_CCCC; 18const DEFAULT_BG: u32 = 0x0000_0000; 19 20struct Console { 21 fb_base: *mut u8, 22 fb_pitch: u32, 23 fb_byte_size: usize, 24 col: usize, 25 row: usize, 26 max_cols: usize, 27 max_rows: usize, 28 fg: u32, 29 bg: u32, 30 bold: bool, 31 saved_col: usize, 32 saved_row: usize, 33 scroll_top: usize, 34 scroll_bottom: usize, 35 parser: Parser, 36} 37 38impl Console { 39 fn new(base: *mut u8, pitch: u32, byte_size: usize, max_cols: usize, max_rows: usize) -> Self { 40 Self { 41 fb_base: base, 42 fb_pitch: pitch, 43 fb_byte_size: byte_size, 44 col: 0, 45 row: 0, 46 max_cols, 47 max_rows, 48 fg: DEFAULT_FG, 49 bg: DEFAULT_BG, 50 bold: false, 51 saved_col: 0, 52 saved_row: 0, 53 scroll_top: 0, 54 scroll_bottom: max_rows.saturating_sub(1), 55 parser: Parser::new(), 56 } 57 } 58 59 fn put_glyph(&self, col: usize, row: usize, ch: u8, fg: u32, bg: u32) { 60 let glyph_offset = (ch as usize) * 16; 61 let pixel_x = col * 8; 62 let pixel_y = row * 16; 63 64 let last_byte = 65 ((pixel_y + 15) as u64) * (self.fb_pitch as u64) + ((pixel_x + 7) as u64) * 4 + 4; 66 if last_byte as usize > self.fb_byte_size { 67 return; 68 } 69 70 (0u32..16).fold((), |(), glyph_row| { 71 let bits = font::FONT_8X16[glyph_offset + glyph_row as usize]; 72 let row_offset = ((pixel_y as u32 + glyph_row) as u64) * (self.fb_pitch as u64); 73 let col_base = (pixel_x as u64) * 4; 74 75 (0u32..8).fold((), |(), bit| { 76 let color = match bits & (0x80 >> bit) != 0 { 77 true => fg, 78 false => bg, 79 }; 80 let offset = row_offset + col_base + (bit as u64) * 4; 81 unsafe { 82 core::ptr::write_volatile(self.fb_base.add(offset as usize) as *mut u32, color); 83 } 84 }); 85 }); 86 } 87 88 fn effective_fg(&self) -> u32 { 89 match self.bold { 90 true => brighten(self.fg), 91 false => self.fg, 92 } 93 } 94 95 fn scroll_region_up(&mut self, top: usize, bottom: usize, count: usize) { 96 let row_bytes = (self.fb_pitch as usize) * 16; 97 let region_rows = bottom.saturating_sub(top) + 1; 98 if count >= region_rows { 99 self.clear_rows(top, bottom); 100 return; 101 } 102 103 let src_start = (top + count) * row_bytes; 104 let dst_start = top * row_bytes; 105 let copy_rows = region_rows - count; 106 let copy_size = copy_rows * row_bytes; 107 108 if src_start + copy_size > self.fb_byte_size { 109 return; 110 } 111 112 unsafe { 113 core::ptr::copy( 114 self.fb_base.add(src_start), 115 self.fb_base.add(dst_start), 116 copy_size, 117 ); 118 } 119 120 let clear_start = (bottom + 1 - count) * row_bytes; 121 let clear_size = count * row_bytes; 122 if clear_start + clear_size <= self.fb_byte_size { 123 unsafe { 124 core::ptr::write_bytes(self.fb_base.add(clear_start), 0u8, clear_size); 125 } 126 } 127 } 128 129 fn scroll_region_down(&mut self, top: usize, bottom: usize, count: usize) { 130 let row_bytes = (self.fb_pitch as usize) * 16; 131 let region_rows = bottom.saturating_sub(top) + 1; 132 if count >= region_rows { 133 self.clear_rows(top, bottom); 134 return; 135 } 136 137 let src_start = top * row_bytes; 138 let dst_start = (top + count) * row_bytes; 139 let copy_rows = region_rows - count; 140 let copy_size = copy_rows * row_bytes; 141 142 if dst_start + copy_size > self.fb_byte_size { 143 return; 144 } 145 146 unsafe { 147 core::ptr::copy( 148 self.fb_base.add(src_start), 149 self.fb_base.add(dst_start), 150 copy_size, 151 ); 152 } 153 154 let clear_size = count * row_bytes; 155 if src_start + clear_size <= self.fb_byte_size { 156 unsafe { 157 core::ptr::write_bytes(self.fb_base.add(src_start), 0u8, clear_size); 158 } 159 } 160 } 161 162 fn clear_rows(&mut self, top: usize, bottom: usize) { 163 let row_bytes = (self.fb_pitch as usize) * 16; 164 let start = top * row_bytes; 165 let end = (bottom + 1) * row_bytes; 166 if end <= self.fb_byte_size { 167 unsafe { 168 core::ptr::write_bytes(self.fb_base.add(start), 0u8, end - start); 169 } 170 } 171 } 172 173 fn clear_row_cols(&self, row: usize, from_col: usize, to_col: usize) { 174 (from_col..to_col).fold((), |(), col| { 175 self.put_glyph(col, row, b' ', DEFAULT_FG, DEFAULT_BG); 176 }); 177 } 178 179 fn newline(&mut self) { 180 self.col = 0; 181 self.row += 1; 182 if self.row > self.scroll_bottom { 183 self.scroll_region_up(self.scroll_top, self.scroll_bottom, 1); 184 self.row = self.scroll_bottom; 185 } 186 } 187 188 fn advance_cursor(&mut self) { 189 self.col += 1; 190 if self.col >= self.max_cols { 191 self.newline(); 192 } 193 } 194 195 fn handle_action(&mut self, action: Action) { 196 match action { 197 Action::Print(ch) => { 198 let fg = self.effective_fg(); 199 let bg = self.bg; 200 self.put_glyph(self.col, self.row, ch, fg, bg); 201 self.advance_cursor(); 202 } 203 Action::Newline => self.newline(), 204 Action::CarriageReturn => { 205 self.col = 0; 206 } 207 Action::Backspace => { 208 if self.col > 0 { 209 self.col -= 1; 210 self.put_glyph(self.col, self.row, b' ', DEFAULT_FG, DEFAULT_BG); 211 } 212 } 213 Action::Tab => { 214 let target = (self.col + 8) & !7; 215 let target = target.min(self.max_cols.saturating_sub(1)); 216 self.col = target; 217 } 218 Action::SetFg(color) => { 219 self.fg = color; 220 } 221 Action::SetBg(color) => { 222 self.bg = color; 223 } 224 Action::SetBold => { 225 self.bold = true; 226 } 227 Action::SetDim => { 228 self.bold = false; 229 } 230 Action::ResetStyle => { 231 self.fg = DEFAULT_FG; 232 self.bg = DEFAULT_BG; 233 self.bold = false; 234 } 235 Action::DefaultFg => { 236 self.fg = DEFAULT_FG; 237 } 238 Action::DefaultBg => { 239 self.bg = DEFAULT_BG; 240 } 241 Action::CursorTo { row, col } => { 242 self.row = (row as usize) 243 .saturating_sub(1) 244 .min(self.max_rows.saturating_sub(1)); 245 self.col = (col as usize) 246 .saturating_sub(1) 247 .min(self.max_cols.saturating_sub(1)); 248 } 249 Action::CursorUp(n) => { 250 self.row = self.row.saturating_sub(n as usize); 251 } 252 Action::CursorDown(n) => { 253 self.row = (self.row + n as usize).min(self.max_rows.saturating_sub(1)); 254 } 255 Action::CursorForward(n) => { 256 self.col = (self.col + n as usize).min(self.max_cols.saturating_sub(1)); 257 } 258 Action::CursorBack(n) => { 259 self.col = self.col.saturating_sub(n as usize); 260 } 261 Action::CursorColumn(n) => { 262 self.col = (n as usize) 263 .saturating_sub(1) 264 .min(self.max_cols.saturating_sub(1)); 265 } 266 Action::SaveCursor => { 267 self.saved_col = self.col; 268 self.saved_row = self.row; 269 } 270 Action::RestoreCursor => { 271 self.col = self.saved_col; 272 self.row = self.saved_row; 273 } 274 Action::HideCursor | Action::ShowCursor => {} 275 Action::ClearToEnd => { 276 self.clear_row_cols(self.row, self.col, self.max_cols); 277 if self.row + 1 < self.max_rows { 278 self.clear_rows(self.row + 1, self.max_rows - 1); 279 } 280 } 281 Action::ClearFromStart => { 282 if self.row > 0 { 283 self.clear_rows(0, self.row - 1); 284 } 285 self.clear_row_cols(self.row, 0, self.col + 1); 286 } 287 Action::ClearScreen => { 288 self.clear_rows(0, self.max_rows.saturating_sub(1)); 289 self.col = 0; 290 self.row = 0; 291 } 292 Action::ClearLineToEnd => { 293 self.clear_row_cols(self.row, self.col, self.max_cols); 294 } 295 Action::ClearLineFromStart => { 296 self.clear_row_cols(self.row, 0, self.col + 1); 297 } 298 Action::ClearLine => { 299 self.clear_row_cols(self.row, 0, self.max_cols); 300 } 301 Action::SetScrollRegion { top, bottom } => { 302 let t = (top as usize) 303 .saturating_sub(1) 304 .min(self.max_rows.saturating_sub(1)); 305 let b = (bottom as usize) 306 .saturating_sub(1) 307 .min(self.max_rows.saturating_sub(1)); 308 if t < b { 309 self.scroll_top = t; 310 self.scroll_bottom = b; 311 self.col = 0; 312 self.row = 0; 313 } 314 } 315 Action::ResetScrollRegion => { 316 self.scroll_top = 0; 317 self.scroll_bottom = self.max_rows.saturating_sub(1); 318 } 319 Action::ScrollUp(n) => { 320 self.scroll_region_up(self.scroll_top, self.scroll_bottom, n as usize); 321 } 322 Action::ScrollDown(n) => { 323 self.scroll_region_down(self.scroll_top, self.scroll_bottom, n as usize); 324 } 325 } 326 } 327 328 fn process_byte(&mut self, byte: u8) { 329 let mut action_buf = [None; 4]; 330 let mut action_count = 0; 331 332 self.parser.feed(byte, |action| { 333 if action_count < action_buf.len() { 334 action_buf[action_count] = Some(action); 335 action_count += 1; 336 } 337 }); 338 339 (0..action_count).fold((), |(), i| { 340 if let Some(action) = action_buf[i] { 341 self.handle_action(action); 342 } 343 }); 344 } 345} 346 347fn brighten(color: u32) -> u32 { 348 let r = ((color >> 16) & 0xFF).min(204) + 51; 349 let g = ((color >> 8) & 0xFF).min(204) + 51; 350 let b = (color & 0xFF).min(204) + 51; 351 (r << 16) | (g << 8) | b 352} 353 354struct ConsoleRing { 355 base: *const u8, 356 capacity: u32, 357 last_tail: u32, 358} 359 360impl ConsoleRing { 361 fn attach(vaddr: u64) -> Self { 362 let base = vaddr as *const u8; 363 let capacity = unsafe { (base.add(8) as *const u32).read_volatile() }; 364 let write_head = unsafe { (base as *const u32).read_volatile() }; 365 Self { 366 base, 367 capacity, 368 last_tail: write_head, 369 } 370 } 371 372 fn drain(&mut self, mut handler: impl FnMut(u8)) { 373 let write_head = unsafe { (self.base as *const u32).read_volatile() }; 374 let available = write_head.wrapping_sub(self.last_tail); 375 376 let effective_tail = match available > self.capacity { 377 true => write_head.wrapping_sub(self.capacity), 378 false => self.last_tail, 379 }; 380 381 let data_base = unsafe { self.base.add(16) }; 382 let count = write_head.wrapping_sub(effective_tail); 383 384 (0..count).fold(effective_tail, |tail, _| { 385 let idx = tail % self.capacity; 386 let byte = unsafe { data_base.add(idx as usize).read_volatile() }; 387 handler(byte); 388 tail.wrapping_add(1) 389 }); 390 391 self.last_tail = write_head; 392 unsafe { 393 (self.base as *mut u32).add(1).write_volatile(write_head); 394 } 395 } 396} 397 398#[unsafe(no_mangle)] 399pub extern "C" fn lancer_main() -> ! { 400 let info = match syscall::fb_info(FB_CAP_SLOT) { 401 Some(i) => i, 402 None => syscall::exit(), 403 }; 404 405 let mapped_pages = syscall::fb_map(FB_CAP_SLOT, FB_MAP_VADDR); 406 if mapped_pages < 0 { 407 syscall::exit(); 408 } 409 let mapped_bytes = (mapped_pages as usize) * 4096; 410 411 let byte_size = (info.byte_size as usize).min(mapped_bytes); 412 let pitch = info.pitch as u32; 413 let row_pixel_bytes = (pitch as usize) * 16; 414 415 unsafe { core::ptr::write_bytes(FB_MAP_VADDR as *mut u8, 0u8, byte_size) }; 416 417 show!( 418 fbcon, 419 "{}x{} pitch {} byte_size {}", 420 info.width, 421 info.height, 422 info.pitch, 423 info.byte_size 424 ); 425 426 let max_cols = (info.width as usize) / 8; 427 let max_rows = match row_pixel_bytes { 428 0 => 0, 429 rpb => byte_size / rpb, 430 }; 431 432 let mut console = Console::new( 433 FB_MAP_VADDR as *mut u8, 434 pitch, 435 byte_size, 436 max_cols, 437 max_rows, 438 ); 439 440 let ring_frames = (0..CONSOLE_RING_MAX_FRAMES) 441 .take_while(|&i| { 442 syscall::frame_map( 443 CONSOLE_RING_BASE_SLOT + i, 444 CONSOLE_RING_VADDR + i * 4096, 445 1, 446 ) >= 0 447 }) 448 .count(); 449 if ring_frames == 0 { 450 show!(fbcon, error, "console ring map failed, exiting"); 451 syscall::exit(); 452 } 453 454 let mut ring = ConsoleRing::attach(CONSOLE_RING_VADDR); 455 456 ring.drain(|b| console.process_byte(b)); 457 458 loop { 459 ring.drain(|b| console.process_byte(b)); 460 syscall::sched_yield(); 461 } 462}