Files
Elyz/kernel/docs/tty.md
2026-07-07 16:40:41 +03:00

151 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Framebuffer Console: `tty.rs`
## Назначение
Драйвер графического вывода текста на framebuffer. Использует
PSF2 (PC Screen Font 2) шрифты для отрисовки символов.
Является основным визуальным каналом вывода ядра (через info! макрос).
## Структура Console
```rust
pub struct Console<'a> {
framebuffer: &'a Framebuffer<'a>, // ссылка на Limine framebuffer
font: &'static [u8], // PSF2 шрифт
pub x: usize, // текущая позиция X (колонка)
pub y: usize, // текущая позиция Y (строка)
pub fg_color: u32, // цвет текста (RGB)
pub bg_color: u32, // цвет фона (RGB)
}
```
## PSF2 Header
```rust
#[repr(C, packed)]
struct Psf2Header {
magic: u32, // 0x864AB572 (PSF2 magic)
version: u32,
header_size: u32, // размер заголовка
flags: u32,
num_glyphs: u32, // количество глифов
bytes_per_glyph: u32, // байт на глиф
height: u32, // высота глифа в пикселях
width: u32, // ширина глифа в пикселях
}
```
Шрифт — `font.psf` (8×16, встроен через `include_bytes!`).
## Операции
### clear()
Заполняет весь framebuffer нулями. Сбрасывает позицию курсора в (0, 0).
```rust
pub fn clear(&mut self) {
unsafe {
core::ptr::write_bytes(fb.addr(), 0, (fb.pitch() * fb.height()) as usize);
}
self.x = 0;
self.y = 0;
}
```
### scroll()
Сдвигает содержимое framebuffer вверх на высоту шрифта.
Новые строки заполняются нулями.
```rust
fn scroll(&mut self) {
let shift = font_height * pitch; // смещение на 1 строку
let size = pitch * (height - font_height); // сколько копировать
unsafe {
ptr::copy(addr.add(shift), addr, size);
ptr::write_bytes(addr.add(size), 0, shift);
}
self.y -= font_height;
}
```
### draw_glyph(glyph_index, x, y)
Рисует глиф (символ) в заданной позиции.
```rust
fn draw_glyph(&mut self, glyph_index: u32, x: usize, y: usize) {
let bytes_per_line = (width + 7) / 8; // пиксели → байты
let glyph_offset = header_size + (glyph_index * bytes_per_glyph);
for cy in 0..height {
let glyph_row = self.font[glyph_offset + cy * bytes_per_line];
for cx in 0..width {
if (glyph_row & (0x80 >> cx)) != 0 {
// Пиксель включён — рисуем fg_color
let offset = ((y + cy) * fb_pitch) + ((x + cx) * 4);
unsafe {
fb_addr.add(offset).cast::<u32>().write_volatile(self.fg_color);
}
}
// Если пиксель выключен — фон остаётся (нет очистки)
}
}
}
```
- `width=8`, `bytes_per_line=1`: каждый ряд глифа = 1 байт.
- `(0x80 >> cx)`: проверка бита слева направо (MSB first).
- `write_volatile`: запрещает компилятору оптимизировать запись в MMIO.
### write_char(c)
Печатает символ:
- `\n` → переход на новую строку.
- Другие символы → draw_glyph. Если символ > num_glyphs → глиф 0.
- Перенос строки по ширине экрана.
- Скроллинг при достижении низа.
## fmt::Write реализация
```rust
impl fmt::Write for Console<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
self.write_char(c);
}
Ok(())
}
}
```
## Использование в kmain()
```rust
let fb = fb_res.framebuffers().next().expect("No framebuffer");
let mut console = tty::Console::new(&fb, KERNEL_FONT);
console.clear();
info!(console, "BOOT", "LIS4 Kernel Starting...");
// ... остальные логи
let _ = writeln!(console, "{}", logo); // ASCII art при загрузке
```
## Детали реализации
- **Цвета**: write_volatile для 32-битного пикселя. Формат — RGBA?
(зависит от режима framebuffer).
- **Фон**: не перерисовывается. Только пиксели текста.
Для полной очистки — clear().
- **PSF2 magic**: `0x864AB572` — проверяется в заголовке.
- **Производительность**: draw_glyph простая, без буферизации.
Для скроллинга — memcpy всего framebuffer.
## Зависимости
- `KERNEL_FONT: &[u8]` — встроен в main.rs через `include_bytes!("font.psf")`.
- `limine::framebuffer::Framebuffer` — через HHDM отображённый framebuffer.