Nothing to see here, move along
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}