summaryrefslogtreecommitdiffstats
path: root/rsnix/src/gop.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/gop.rs')
-rw-r--r--rsnix/src/gop.rs152
1 files changed, 60 insertions, 92 deletions
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs
index 9645bb0..81b8945 100644
--- a/rsnix/src/gop.rs
+++ b/rsnix/src/gop.rs
@@ -1,4 +1,8 @@
1/// GOP framebuffer driver — renders text via a bitmap font. 1/// GOP framebuffer driver — renders text via a bitmap font.
2///
3/// Text surface (cursor, scrolling, colours) lives in `crate::vt`; this
4/// module is the low-level rasteriser: glyph blits and pixel shifts on the
5/// linear framebuffer.
2use crate::font; 6use crate::font;
3use crate::uefi::{GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB}; 7use crate::uefi::{GraphicsOutputProtocol, PIXEL_BGR, PIXEL_RGB};
4use core::fmt; 8use core::fmt;
@@ -13,11 +17,7 @@ pub struct Framebuffer {
13 pitch: usize, // bytes per scan line 17 pitch: usize, // bytes per scan line
14 bytes_per_pixel: usize, 18 bytes_per_pixel: usize,
15 bg: u32, // packed BGRA or RGBA background colour 19 bg: u32, // packed BGRA or RGBA background colour
16 fg: u32, // packed foreground colour 20 bgr: bool,
17 cursor_x: usize,
18 cursor_y: usize,
19 rows: usize,
20 cols: usize,
21} 21}
22 22
23static mut FB: Option<Framebuffer> = None; 23static mut FB: Option<Framebuffer> = None;
@@ -38,9 +38,8 @@ pub fn init(gop: &mut GraphicsOutputProtocol) {
38 PIXEL_RGB => (0x00_FF_FF_FFu32, 0x00_00_00_00u32), 38 PIXEL_RGB => (0x00_FF_FF_FFu32, 0x00_00_00_00u32),
39 _ => (0x00_FF_FF_FFu32, 0x00_00_00_00u32), 39 _ => (0x00_FF_FF_FFu32, 0x00_00_00_00u32),
40 }; 40 };
41 41 let bgr = info.pixel_format == PIXEL_BGR;
42 let rows = height / 16; 42 let _ = fg;
43 let cols = width / 8;
44 43
45 let fb = Framebuffer { 44 let fb = Framebuffer {
46 base, 45 base,
@@ -49,11 +48,7 @@ pub fn init(gop: &mut GraphicsOutputProtocol) {
49 pitch, 48 pitch,
50 bytes_per_pixel: bpp, 49 bytes_per_pixel: bpp,
51 bg, 50 bg,
52 fg, 51 bgr,
53 cursor_x: 0,
54 cursor_y: 0,
55 rows,
56 cols,
57 }; 52 };
58 53
59 fb.clear(); 54 fb.clear();
@@ -63,79 +58,45 @@ pub fn init(gop: &mut GraphicsOutputProtocol) {
63 } 58 }
64} 59}
65 60
66/// Draw a character at the current cursor position and advance. 61// ---------------------------------------------------------------------------
67pub fn put_char(c: char) { 62// Public text API — the text surface now lives in `crate::vt`; these wrappers
68 let fb = unsafe { 63// keep callers (shell, macros) unchanged.
69 match FB.as_mut() { 64// ---------------------------------------------------------------------------
70 Some(f) => f,
71 None => return,
72 }
73 };
74 65
75 if c == '\x08' { 66/// Draw a character at the current terminal cursor position and advance.
76 // Backspace - move cursor back and erase 67pub fn put_char(c: char) {
77 if fb.cursor_x > 0 { 68 let b = if (c as u32) < 0x100 { c as u8 } else { b'?' };
78 fb.cursor_x -= 1; 69 crate::vt::put_char(b);
79 } else if fb.cursor_y > 0 { 70}
80 fb.cursor_y -= 1;
81 fb.cursor_x = fb.cols - 1;
82 } else {
83 return;
84 }
85 // Draw glyph 32 (space) to erase — glyph 0 is NUL, not blank
86 fb.draw_glyph(32, fb.cursor_x, fb.cursor_y);
87 return;
88 }
89 if c == '\n' {
90 fb.cursor_x = 0;
91 fb.cursor_y += 1;
92 if fb.cursor_y >= fb.rows {
93 fb.scroll();
94 }
95 return;
96 }
97 if c == '\r' {
98 fb.cursor_x = 0;
99 return;
100 }
101 71
102 let idx = c as usize; 72/// Write a string to the terminal.
103 if idx >= 256 { 73pub fn write_str(s: &str) {
104 // Unmapped character: draw a space 74 crate::vt::write_str(s);
105 fb.draw_glyph(0, fb.cursor_x, fb.cursor_y); 75}
106 } else {
107 fb.draw_glyph(idx, fb.cursor_x, fb.cursor_y);
108 }
109 76
110 fb.cursor_x += 1; 77/// Clear the terminal and home the cursor.
111 if fb.cursor_x >= fb.cols { 78pub fn clear_screen() {
112 fb.cursor_x = 0; 79 crate::vt::clear_all();
113 fb.cursor_y += 1;
114 if fb.cursor_y >= fb.rows {
115 fb.scroll();
116 }
117 }
118} 80}
119 81
120/// Write a string to the framebuffer. 82/// (cols, rows) of the text surface, from the current framebuffer mode.
121pub fn write_str(s: &str) { 83pub fn dims() -> (usize, usize) {
122 for c in s.chars() { 84 unsafe {
123 put_char(c); 85 match FB.as_ref() {
86 Some(f) => (f.width / 8, f.height / 16),
87 None => (0, 0),
88 }
124 } 89 }
125} 90}
126 91
127/// Clear the entire screen to background colour and reset cursor to (0, 0). 92/// Pack an (r, g, b) colour for the framebuffer's pixel format.
128pub fn clear_screen() { 93pub fn rgb(r: u8, g: u8, b: u8) -> u32 {
129 let fb = unsafe { 94 unsafe {
130 match FB.as_mut() { 95 match FB.as_ref() {
131 Some(f) => f, 96 Some(f) if f.bgr => ((r as u32) << 16) | ((g as u32) << 8) | b as u32,
132 None => return, 97 _ => ((b as u32) << 16) | ((g as u32) << 8) | r as u32,
133 } 98 }
134 }; 99 }
135 fb.clear();
136 // Reset cursor back to top-left so the next prompt appears at (0, 0)
137 fb.cursor_x = 0;
138 fb.cursor_y = 0;
139} 100}
140 101
141// --------------------------------------------------------------------------- 102// ---------------------------------------------------------------------------
@@ -155,7 +116,10 @@ impl Framebuffer {
155 } 116 }
156 } 117 }
157 118
158 fn scroll(&mut self) { 119 /// Shift the whole framebuffer up one row (16 pixels) and clear the
120 /// exposed bottom row. Called by `vt::scroll_up`; the vt then re-blits
121 /// only the new bottom row.
122 pub(crate) fn shift_up(&self) {
159 // Move everything up one row (16 pixels) 123 // Move everything up one row (16 pixels)
160 let row_bytes = self.pitch * 16; 124 let row_bytes = self.pitch * 16;
161 let total = self.pitch * self.height; 125 let total = self.pitch * self.height;
@@ -167,40 +131,44 @@ impl Framebuffer {
167 ptr::write_volatile(last_row.add(i * 4) as *mut u32, self.bg); 131 ptr::write_volatile(last_row.add(i * 4) as *mut u32, self.bg);
168 } 132 }
169 } 133 }
170 self.cursor_y -= 1;
171 } 134 }
135}
172 136
173 fn draw_glyph(&self, glyph_idx: usize, col: usize, row: usize) { 137/// Blit one glyph cell at (col, row) with explicit fg/bg colours.
138pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) {
139 if let Some(fb) = unsafe { FB.as_ref() } {
140 let glyph_idx = if ch < 256 { ch as usize } else { 32 };
174 let glyph = font::GLYPH_8X16; 141 let glyph = font::GLYPH_8X16;
175 let data = match glyph.get(glyph_idx) { 142 let data = match glyph.get(glyph_idx) {
176 Some(d) => *d, 143 Some(d) => *d,
177 None => return, 144 None => return,
178 }; 145 };
179 146
147 let fg = rgb(fg.0, fg.1, fg.2);
148 let bg = rgb(bg.0, bg.1, bg.2);
180 let x = col * 8; 149 let x = col * 8;
181 let y = row * 16; 150 let y = row * 16;
182 151
183 for cy in 0..16 { 152 for cy in 0..16 {
184 let row_bits = data[cy]; 153 let row_bits = data[cy];
185 for cx in 0..8 { 154 for cx in 0..8 {
186 if row_bits & (0x80 >> cx) != 0 { 155 let offset = (y + cy) * fb.pitch + (x + cx) * fb.bytes_per_pixel;
187 // Set foreground pixel 156 unsafe {
188 let offset = (y + cy) * self.pitch + (x + cx) * self.bytes_per_pixel; 157 let px = if row_bits & (0x80 >> cx) != 0 { fg } else { bg };
189 unsafe { 158 ptr::write_volatile(fb.base.add(offset) as *mut u32, px);
190 ptr::write_volatile(self.base.add(offset) as *mut u32, self.fg);
191 }
192 } else {
193 // Set background pixel
194 let offset = (y + cy) * self.pitch + (x + cx) * self.bytes_per_pixel;
195 unsafe {
196 ptr::write_volatile(self.base.add(offset) as *mut u32, self.bg);
197 }
198 } 159 }
199 } 160 }
200 } 161 }
201 } 162 }
202} 163}
203 164
165/// Shift the framebuffer up one row and clear the bottom row.
166pub(crate) fn shift_up() {
167 if let Some(fb) = unsafe { FB.as_ref() } {
168 fb.shift_up();
169 }
170}
171
204// --------------------------------------------------------------------------- 172// ---------------------------------------------------------------------------
205// fmt::Write wrapper 173// fmt::Write wrapper
206// --------------------------------------------------------------------------- 174// ---------------------------------------------------------------------------