Skip to content

Commit 10f873e

Browse files
authored
Merge pull request #71 from Neotron-Compute/fix-video-new-irq
Reworked the video render system.
2 parents 01f9f46 + 9e2d084 commit 10f873e

File tree

9 files changed

+1880
-1640
lines changed

9 files changed

+1880
-1640
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ See [CHANGELOG.md](./CHANGELOG.md)
116116

117117
## Licence
118118

119-
Neotron-Pico-BIOS Copyright (c) Jonathan 'theJPster' Pallant and the Neotron Developers, 2021
119+
Neotron-Pico-BIOS Copyright (c) Jonathan 'theJPster' Pallant and the Neotron Developers, 2023
120120

121121
This program is free software: you can redistribute it and/or modify
122122
it under the terms of the GNU General Public License as published by

src/console.rs

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
//! Code for handling a text-buffer.
2+
3+
// -----------------------------------------------------------------------------
4+
// Licence Statement
5+
// -----------------------------------------------------------------------------
6+
// Copyright (c) Jonathan 'theJPster' Pallant and the Neotron Developers, 2023
7+
//
8+
// This program is free software: you can redistribute it and/or modify it under
9+
// the terms of the GNU General Public License as published by the Free Software
10+
// Foundation, either version 3 of the License, or (at your option) any later
11+
// version.
12+
//
13+
// This program is distributed in the hope that it will be useful, but WITHOUT
14+
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15+
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16+
// details.
17+
//
18+
// You should have received a copy of the GNU General Public License along with
19+
// this program. If not, see <https://www.gnu.org/licenses/>.
20+
// -----------------------------------------------------------------------------
21+
22+
// -----------------------------------------------------------------------------
23+
// Sub-modules
24+
// -----------------------------------------------------------------------------
25+
26+
// -----------------------------------------------------------------------------
27+
// Imports
28+
// -----------------------------------------------------------------------------
29+
30+
use core::sync::atomic::{AtomicPtr, AtomicU16, AtomicU8, Ordering};
31+
32+
use neotron_common_bios::video::{
33+
Attr, Glyph, GlyphAttr, TextBackgroundColour, TextForegroundColour,
34+
};
35+
36+
// -----------------------------------------------------------------------------
37+
// Types
38+
// -----------------------------------------------------------------------------
39+
40+
/// Holds some data necessary to present a text console.
41+
///
42+
/// Used by Core 0 to control writes to a shared text-buffer.
43+
pub struct TextConsole {
44+
current_col: AtomicU16,
45+
current_row: AtomicU16,
46+
text_buffer: AtomicPtr<GlyphAttr>,
47+
current_attr: AtomicU8,
48+
}
49+
50+
impl TextConsole {
51+
/// Create a TextConsole.
52+
///
53+
/// Has no buffer associated with it
54+
pub const fn new() -> TextConsole {
55+
TextConsole {
56+
current_row: AtomicU16::new(0),
57+
current_col: AtomicU16::new(0),
58+
text_buffer: AtomicPtr::new(core::ptr::null_mut()),
59+
// White on Black, with the default palette
60+
current_attr: AtomicU8::new(
61+
Attr::new(
62+
TextForegroundColour::WHITE,
63+
TextBackgroundColour::BLACK,
64+
false,
65+
)
66+
.0,
67+
),
68+
}
69+
}
70+
71+
/// Update the text buffer we are using.
72+
///
73+
/// Will reset the cursor. The screen is not cleared.
74+
pub fn set_text_buffer(
75+
&self,
76+
text_buffer: &'static mut [GlyphAttr;
77+
crate::vga::MAX_TEXT_ROWS * crate::vga::MAX_TEXT_COLS],
78+
) {
79+
self.text_buffer
80+
.store(text_buffer.as_mut_ptr(), Ordering::Relaxed)
81+
}
82+
83+
/// Place a single Code Page 850 encoded 8-bit character on the screen.
84+
///
85+
/// Adjusts the current row and column automatically. Also understands
86+
/// Carriage Return and New Line bytes.
87+
pub fn write_font_glyph(&self, glyph: Glyph) {
88+
// Load from global state
89+
let mut row = self.current_row.load(Ordering::Relaxed);
90+
let mut col = self.current_col.load(Ordering::Relaxed);
91+
let buffer = self.text_buffer.load(Ordering::Relaxed);
92+
93+
if !buffer.is_null() {
94+
self.write_at(glyph, buffer, &mut row, &mut col);
95+
// Push back to global state
96+
self.current_row.store(row, Ordering::Relaxed);
97+
self.current_col.store(col, Ordering::Relaxed);
98+
}
99+
}
100+
101+
/// Moves the text cursor to the specified row and column.
102+
///
103+
/// If a value is out of bounds, the cursor is not moved in that axis.
104+
pub fn move_to(&self, row: u16, col: u16) {
105+
let mode = crate::vga::VIDEO_MODE.get_mode();
106+
let num_rows = mode.text_height().unwrap_or(0);
107+
let num_cols = mode.text_width().unwrap_or(0);
108+
if row < num_rows {
109+
self.current_row.store(row, Ordering::Relaxed);
110+
}
111+
if col < num_cols {
112+
self.current_col.store(col, Ordering::Relaxed);
113+
}
114+
}
115+
116+
/// Convert a Unicode Scalar Value to a font glyph.
117+
///
118+
/// Zero-width and modifier Unicode Scalar Values (e.g. `U+0301 COMBINING,
119+
/// ACCENT`) are not supported. Normalise your Unicode before calling
120+
/// this function.
121+
fn map_char_to_glyph(input: char) -> Glyph {
122+
// This fixed table only works for the default font. When we support
123+
// changing font, we will need to plug-in a different table for each font.
124+
let index = match input {
125+
'\u{0000}'..='\u{007F}' => input as u8,
126+
'\u{00A0}' => 255, // NBSP
127+
'\u{00A1}' => 173, // ¡
128+
'\u{00A2}' => 189, // ¢
129+
'\u{00A3}' => 156, // £
130+
'\u{00A4}' => 207, // ¤
131+
'\u{00A5}' => 190, // ¥
132+
'\u{00A6}' => 221, // ¦
133+
'\u{00A7}' => 245, // §
134+
'\u{00A8}' => 249, // ¨
135+
'\u{00A9}' => 184, // ©
136+
'\u{00AA}' => 166, // ª
137+
'\u{00AB}' => 174, // «
138+
'\u{00AC}' => 170, // ¬
139+
'\u{00AD}' => 240, // SHY
140+
'\u{00AE}' => 169, // ®
141+
'\u{00AF}' => 238, // ¯
142+
'\u{00B0}' => 248, // °
143+
'\u{00B1}' => 241, // ±
144+
'\u{00B2}' => 253, // ²
145+
'\u{00B3}' => 252, // ³
146+
'\u{00B4}' => 239, // ´
147+
'\u{00B5}' => 230, // µ
148+
'\u{00B6}' => 244, // ¶
149+
'\u{00B7}' => 250, // ·
150+
'\u{00B8}' => 247, // ¸
151+
'\u{00B9}' => 251, // ¹
152+
'\u{00BA}' => 167, // º
153+
'\u{00BB}' => 175, // »
154+
'\u{00BC}' => 172, // ¼
155+
'\u{00BD}' => 171, // ½
156+
'\u{00BE}' => 243, // ¾
157+
'\u{00BF}' => 168, // ¿
158+
'\u{00C0}' => 183, // À
159+
'\u{00C1}' => 181, // Á
160+
'\u{00C2}' => 182, // Â
161+
'\u{00C3}' => 199, // Ã
162+
'\u{00C4}' => 142, // Ä
163+
'\u{00C5}' => 143, // Å
164+
'\u{00C6}' => 146, // Æ
165+
'\u{00C7}' => 128, // Ç
166+
'\u{00C8}' => 212, // È
167+
'\u{00C9}' => 144, // É
168+
'\u{00CA}' => 210, // Ê
169+
'\u{00CB}' => 211, // Ë
170+
'\u{00CC}' => 222, // Ì
171+
'\u{00CD}' => 214, // Í
172+
'\u{00CE}' => 215, // Î
173+
'\u{00CF}' => 216, // Ï
174+
'\u{00D0}' => 209, // Ð
175+
'\u{00D1}' => 165, // Ñ
176+
'\u{00D2}' => 227, // Ò
177+
'\u{00D3}' => 224, // Ó
178+
'\u{00D4}' => 226, // Ô
179+
'\u{00D5}' => 229, // Õ
180+
'\u{00D6}' => 153, // Ö
181+
'\u{00D7}' => 158, // ×
182+
'\u{00D8}' => 157, // Ø
183+
'\u{00D9}' => 235, // Ù
184+
'\u{00DA}' => 233, // Ú
185+
'\u{00DB}' => 234, // Û
186+
'\u{00DC}' => 154, // Ü
187+
'\u{00DD}' => 237, // Ý
188+
'\u{00DE}' => 232, // Þ
189+
'\u{00DF}' => 225, // ß
190+
'\u{00E0}' => 133, // à
191+
'\u{00E1}' => 160, // á
192+
'\u{00E2}' => 131, // â
193+
'\u{00E3}' => 198, // ã
194+
'\u{00E4}' => 132, // ä
195+
'\u{00E5}' => 134, // å
196+
'\u{00E6}' => 145, // æ
197+
'\u{00E7}' => 135, // ç
198+
'\u{00E8}' => 138, // è
199+
'\u{00E9}' => 130, // é
200+
'\u{00EA}' => 136, // ê
201+
'\u{00EB}' => 137, // ë
202+
'\u{00EC}' => 141, // ì
203+
'\u{00ED}' => 161, // í
204+
'\u{00EE}' => 140, // î
205+
'\u{00EF}' => 139, // ï
206+
'\u{00F0}' => 208, // ð
207+
'\u{00F1}' => 164, // ñ
208+
'\u{00F2}' => 149, // ò
209+
'\u{00F3}' => 162, // ó
210+
'\u{00F4}' => 147, // ô
211+
'\u{00F5}' => 228, // õ
212+
'\u{00F6}' => 148, // ö
213+
'\u{00F7}' => 246, // ÷
214+
'\u{00F8}' => 155, // ø
215+
'\u{00F9}' => 151, // ù
216+
'\u{00FA}' => 163, // ú
217+
'\u{00FB}' => 150, // û
218+
'\u{00FC}' => 129, // ü
219+
'\u{00FD}' => 236, // ý
220+
'\u{00FE}' => 231, // þ
221+
'\u{00FF}' => 152, // ÿ
222+
'\u{0131}' => 213, // ı
223+
'\u{0192}' => 159, // ƒ
224+
'\u{2017}' => 242, // ‗
225+
'\u{2500}' => 196, // ─
226+
'\u{2502}' => 179, // │
227+
'\u{250C}' => 218, // ┌
228+
'\u{2510}' => 191, // ┐
229+
'\u{2514}' => 192, // └
230+
'\u{2518}' => 217, // ┘
231+
'\u{251C}' => 195, // ├
232+
'\u{2524}' => 180, // ┤
233+
'\u{252C}' => 194, // ┬
234+
'\u{2534}' => 193, // ┴
235+
'\u{253C}' => 197, // ┼
236+
'\u{2550}' => 205, // ═
237+
'\u{2551}' => 186, // ║
238+
'\u{2554}' => 201, // ╔
239+
'\u{2557}' => 187, // ╗
240+
'\u{255A}' => 200, // ╚
241+
'\u{255D}' => 188, // ╝
242+
'\u{2560}' => 204, // ╠
243+
'\u{2563}' => 185, // ╣
244+
'\u{2566}' => 203, // ╦
245+
'\u{2569}' => 202, // ╩
246+
'\u{256C}' => 206, // ╬
247+
'\u{2580}' => 223, // ▀
248+
'\u{2584}' => 220, // ▄
249+
'\u{2588}' => 219, // █
250+
'\u{2591}' => 176, // ░
251+
'\u{2592}' => 177, // ▒
252+
'\u{2593}' => 178, // ▓
253+
'\u{25A0}' => 254, // ■
254+
_ => b'?',
255+
};
256+
Glyph(index)
257+
}
258+
259+
/// Put a single character at a specified point on screen.
260+
///
261+
/// The character is relative to the current font, but newline and carriage
262+
/// return will be interpreted appropriately. The given `row` and `col` are
263+
/// updated.
264+
fn write_at(&self, glyph: Glyph, buffer: *mut GlyphAttr, row: &mut u16, col: &mut u16) {
265+
let mode = crate::vga::VIDEO_MODE.get_mode();
266+
let num_rows = mode.text_height().unwrap_or(0) as usize;
267+
let num_cols = mode.text_width().unwrap_or(0) as usize;
268+
let attr = Attr(self.current_attr.load(Ordering::Relaxed));
269+
270+
if glyph.0 == b'\r' {
271+
*col = 0;
272+
} else if glyph.0 == b'\n' {
273+
*col = 0;
274+
*row += 1;
275+
} else {
276+
let offset = (*col as usize) + (num_cols * (*row as usize));
277+
// Note (safety): This is safe as we bound `col` and `row`
278+
unsafe {
279+
buffer
280+
.add(offset)
281+
.write_volatile(GlyphAttr::new(glyph, attr))
282+
};
283+
*col += 1;
284+
}
285+
if *col == (num_cols as u16) {
286+
*col = 0;
287+
*row += 1;
288+
}
289+
290+
if *row == (num_rows as u16) {
291+
// Stay on last line
292+
*row = (num_rows - 1) as u16;
293+
294+
unsafe { core::ptr::copy(buffer.add(num_cols), buffer, num_cols * (num_rows - 1)) };
295+
296+
for blank_col in 0..num_cols {
297+
let offset = blank_col + (num_cols * (*row as usize));
298+
unsafe {
299+
buffer
300+
.add(offset)
301+
.write_volatile(GlyphAttr::new(Glyph(b' '), attr))
302+
};
303+
}
304+
}
305+
}
306+
307+
/// Store a new attribute to be used for subsequent characters.
308+
pub fn change_attr(&self, attr: Attr) {
309+
let value = attr.0;
310+
self.current_attr.store(value, Ordering::Relaxed);
311+
}
312+
}
313+
314+
unsafe impl Sync for TextConsole {}
315+
316+
impl core::fmt::Write for &TextConsole {
317+
/// Allows us to call `writeln!(some_text_console, "hello")`
318+
fn write_str(&mut self, s: &str) -> core::fmt::Result {
319+
// Load from global state
320+
let mut row = self.current_row.load(Ordering::Relaxed);
321+
let mut col = self.current_col.load(Ordering::Relaxed);
322+
let buffer = self.text_buffer.load(Ordering::Relaxed);
323+
324+
if !buffer.is_null() {
325+
for ch in s.chars() {
326+
let b = TextConsole::map_char_to_glyph(ch);
327+
self.write_at(b, buffer, &mut row, &mut col);
328+
}
329+
330+
// Push back to global state
331+
self.current_row.store(row, Ordering::Relaxed);
332+
self.current_col.store(col, Ordering::Relaxed);
333+
}
334+
335+
Ok(())
336+
}
337+
}
338+
339+
// -----------------------------------------------------------------------------
340+
// Static and Const Data
341+
// -----------------------------------------------------------------------------
342+
343+
// -----------------------------------------------------------------------------
344+
// Functions
345+
// -----------------------------------------------------------------------------
346+
347+
// -----------------------------------------------------------------------------
348+
// End of file
349+
// -----------------------------------------------------------------------------

0 commit comments

Comments
 (0)