Skip to content

Commit 6ccfb83

Browse files
authored
Merge pull request #63 from Neotron-Compute/fixup-console
Clean up console handling.
2 parents be8d85a + 440219b commit 6ccfb83

File tree

5 files changed

+168
-160
lines changed

5 files changed

+168
-160
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ chrono = { version = "0.4", default-features = false }
5252
embedded-sdmmc = { version = "0.5", default-features = false }
5353
neotron-api = "0.1"
5454
neotron-loader = "0.1"
55-
vte = { git = "https://github.com/alacritty/vte", commit="94e74f3a64f42d5dad4e3d42dbe8c23291038214" }
55+
vte = { git = "https://github.com/alacritty/vte", rev = "94e74f3a64f42d5dad4e3d42dbe8c23291038214" }
5656

5757
[features]
5858
lib-mode = []

src/lib.rs

Lines changed: 162 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// Modules and Imports
1313
// ===========================================================================
1414

15-
use core::sync::atomic::{AtomicBool, Ordering};
15+
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
1616

1717
use neotron_common_bios as bios;
1818

@@ -45,6 +45,12 @@ static VGA_CONSOLE: CsRefCell<Option<vgaconsole::VgaConsole>> = CsRefCell::new(N
4545
/// We store our serial console here.
4646
static SERIAL_CONSOLE: CsRefCell<Option<SerialConsole>> = CsRefCell::new(None);
4747

48+
/// Our overall text output console.
49+
///
50+
/// Writes to the VGA console and/or the serial console (depending on which is
51+
/// configured).
52+
static CONSOLE: Console = Console;
53+
4854
/// Note if we are panicking right now.
4955
///
5056
/// If so, don't panic if a serial write fails.
@@ -53,6 +59,157 @@ static IS_PANIC: AtomicBool = AtomicBool::new(false);
5359
/// Our keyboard controller
5460
static STD_INPUT: CsRefCell<StdInput> = CsRefCell::new(StdInput::new());
5561

62+
// ===========================================================================
63+
// Macros
64+
// ===========================================================================
65+
66+
/// Prints to the screen
67+
#[macro_export]
68+
macro_rules! osprint {
69+
($($arg:tt)*) => {
70+
#[allow(unused)]
71+
use core::fmt::Write as _;
72+
let _ = write!(&$crate::CONSOLE, $($arg)*);
73+
}
74+
}
75+
76+
/// Prints to the screen and puts a new-line on the end
77+
#[macro_export]
78+
macro_rules! osprintln {
79+
() => ($crate::osprint!("\n"));
80+
($($arg:tt)*) => {
81+
$crate::osprint!($($arg)*);
82+
$crate::osprint!("\n");
83+
};
84+
}
85+
86+
// ===========================================================================
87+
// Local types
88+
// ===========================================================================
89+
90+
/// Represents the API supplied by the BIOS
91+
struct Api {
92+
bios: AtomicPtr<bios::Api>,
93+
}
94+
95+
impl Api {
96+
/// Create a new object with a null pointer for the BIOS API.
97+
const fn new() -> Api {
98+
Api {
99+
bios: AtomicPtr::new(core::ptr::null_mut()),
100+
}
101+
}
102+
103+
/// Change the stored BIOS API pointer.
104+
///
105+
/// The pointed-at object must have static lifetime.
106+
unsafe fn store(&self, api: *const bios::Api) {
107+
self.bios.store(api as *mut bios::Api, Ordering::SeqCst)
108+
}
109+
110+
/// Get the BIOS API as a reference.
111+
///
112+
/// Will panic if the stored pointer is null.
113+
fn get(&self) -> &'static bios::Api {
114+
let ptr = self.bios.load(Ordering::SeqCst) as *const bios::Api;
115+
let api_ref = unsafe { ptr.as_ref() }.expect("BIOS API should be non-null");
116+
api_ref
117+
}
118+
119+
/// Get the current time
120+
fn get_time(&self) -> chrono::NaiveDateTime {
121+
let api = self.get();
122+
let bios_time = (api.time_clock_get)();
123+
let secs = i64::from(bios_time.secs) + SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH;
124+
let nsecs = bios_time.nsecs;
125+
chrono::NaiveDateTime::from_timestamp_opt(secs, nsecs).unwrap()
126+
}
127+
128+
/// Set the current time
129+
fn set_time(&self, timestamp: chrono::NaiveDateTime) {
130+
let api = self.get();
131+
let nanos = timestamp.timestamp_nanos();
132+
let bios_time = bios::Time {
133+
secs: ((nanos / 1_000_000_000) - SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH) as u32,
134+
nsecs: (nanos % 1_000_000_000) as u32,
135+
};
136+
(api.time_clock_set)(bios_time);
137+
}
138+
}
139+
140+
/// Represents the serial port we can use as a text input/output device.
141+
struct SerialConsole(u8);
142+
143+
impl SerialConsole {
144+
/// Write some bytes to the serial console
145+
fn write_bstr(&mut self, mut data: &[u8]) -> Result<(), bios::Error> {
146+
let api = API.get();
147+
while !data.is_empty() {
148+
let res: Result<usize, bios::Error> = (api.serial_write)(
149+
// Which port
150+
self.0,
151+
// Data
152+
bios::FfiByteSlice::new(data),
153+
// No timeout
154+
bios::FfiOption::None,
155+
)
156+
.into();
157+
let count = match res {
158+
Ok(n) => n,
159+
Err(_e) => {
160+
// If we can't write to the serial port, let's not break any
161+
// other consoles we might have configured. Instead, just
162+
// quit now and pretend we wrote it all.
163+
return Ok(());
164+
}
165+
};
166+
data = &data[count..];
167+
}
168+
Ok(())
169+
}
170+
171+
/// Try and get as many bytes as we can from the serial console.
172+
fn read_data(&mut self, buffer: &mut [u8]) -> Result<usize, bios::Error> {
173+
let api = API.get();
174+
let ffi_buffer = bios::FfiBuffer::new(buffer);
175+
let res = (api.serial_read)(
176+
self.0,
177+
ffi_buffer,
178+
bios::FfiOption::Some(bios::Timeout::new_ms(0)),
179+
);
180+
res.into()
181+
}
182+
}
183+
184+
impl core::fmt::Write for SerialConsole {
185+
fn write_str(&mut self, data: &str) -> core::fmt::Result {
186+
self.write_bstr(data.as_bytes())
187+
.map_err(|_e| core::fmt::Error)
188+
}
189+
}
190+
191+
/// Represents either or both of the VGA console and the serial console.
192+
struct Console;
193+
194+
impl core::fmt::Write for &Console {
195+
fn write_str(&mut self, s: &str) -> core::fmt::Result {
196+
if let Ok(mut guard) = VGA_CONSOLE.try_lock() {
197+
if let Some(vga_console) = guard.as_mut() {
198+
vga_console.write_str(s)?;
199+
}
200+
}
201+
202+
if let Ok(mut guard) = SERIAL_CONSOLE.try_lock() {
203+
if let Some(serial_console) = guard.as_mut() {
204+
serial_console.write_str(s)?;
205+
}
206+
}
207+
208+
Ok(())
209+
}
210+
}
211+
212+
/// Represents the standard input of our console
56213
struct StdInput {
57214
keyboard: pc_keyboard::EventDecoder<pc_keyboard::layouts::AnyLayout>,
58215
buffer: heapless::spsc::Queue<u8, 16>,
@@ -161,149 +318,10 @@ impl StdInput {
161318
}
162319
}
163320

164-
// ===========================================================================
165-
// Macros
166-
// ===========================================================================
167-
168-
/// Prints to the screen
169-
#[macro_export]
170-
macro_rules! osprint {
171-
($($arg:tt)*) => {
172-
$crate::VGA_CONSOLE.with(|guard| {
173-
if let Some(console) = guard.as_mut() {
174-
#[allow(unused)]
175-
use core::fmt::Write as _;
176-
write!(console, $($arg)*).unwrap();
177-
}
178-
}).unwrap();
179-
$crate::SERIAL_CONSOLE.with(|guard| {
180-
if let Some(console) = guard.as_mut() {
181-
#[allow(unused)]
182-
use core::fmt::Write as _;
183-
write!(console, $($arg)*).unwrap();
184-
}
185-
}).unwrap();
186-
};
187-
}
188-
189-
/// Prints to the screen and puts a new-line on the end
190-
#[macro_export]
191-
macro_rules! osprintln {
192-
() => ($crate::osprint!("\n"));
193-
($($arg:tt)*) => {
194-
$crate::osprint!($($arg)*);
195-
$crate::osprint!("\n");
196-
};
197-
}
198-
199-
// ===========================================================================
200-
// Local types
201-
// ===========================================================================
202-
203-
/// Represents the API supplied by the BIOS
204-
struct Api {
205-
bios: core::sync::atomic::AtomicPtr<bios::Api>,
206-
}
207-
208-
impl Api {
209-
/// Create a new object with a null pointer for the BIOS API.
210-
const fn new() -> Api {
211-
Api {
212-
bios: core::sync::atomic::AtomicPtr::new(core::ptr::null_mut()),
213-
}
214-
}
215-
216-
/// Change the stored BIOS API pointer.
217-
///
218-
/// The pointed-at object must have static lifetime.
219-
unsafe fn store(&self, api: *const bios::Api) {
220-
self.bios
221-
.store(api as *mut bios::Api, core::sync::atomic::Ordering::SeqCst)
222-
}
223-
224-
/// Get the BIOS API as a reference.
225-
///
226-
/// Will panic if the stored pointer is null.
227-
fn get(&self) -> &'static bios::Api {
228-
let ptr = self.bios.load(core::sync::atomic::Ordering::SeqCst) as *const bios::Api;
229-
let api_ref = unsafe { ptr.as_ref() }.expect("BIOS API should be non-null");
230-
api_ref
231-
}
232-
233-
/// Get the current time
234-
fn get_time(&self) -> chrono::NaiveDateTime {
235-
let api = self.get();
236-
let bios_time = (api.time_clock_get)();
237-
let secs = i64::from(bios_time.secs) + SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH;
238-
let nsecs = bios_time.nsecs;
239-
chrono::NaiveDateTime::from_timestamp_opt(secs, nsecs).unwrap()
240-
}
241-
242-
/// Set the current time
243-
fn set_time(&self, timestamp: chrono::NaiveDateTime) {
244-
let api = self.get();
245-
let nanos = timestamp.timestamp_nanos();
246-
let bios_time = bios::Time {
247-
secs: ((nanos / 1_000_000_000) - SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH) as u32,
248-
nsecs: (nanos % 1_000_000_000) as u32,
249-
};
250-
(api.time_clock_set)(bios_time);
251-
}
252-
}
253-
254-
/// Represents the serial port we can use as a text input/output device.
255-
struct SerialConsole(u8);
256-
257-
impl SerialConsole {
258-
/// Write some bytes to the serial console
259-
fn write_bstr(&mut self, data: &[u8]) {
260-
let api = API.get();
261-
let is_panic = IS_PANIC.load(Ordering::Relaxed);
262-
let res = (api.serial_write)(
263-
// Which port
264-
self.0,
265-
// Data
266-
bios::FfiByteSlice::new(data),
267-
// No timeout
268-
bios::FfiOption::None,
269-
);
270-
if !is_panic {
271-
res.unwrap();
272-
}
273-
}
274-
275-
/// Try and get as many bytes as we can from the serial console.
276-
fn read_data(&mut self, buffer: &mut [u8]) -> Result<usize, bios::Error> {
277-
let api = API.get();
278-
let ffi_buffer = bios::FfiBuffer::new(buffer);
279-
let res = (api.serial_read)(
280-
self.0,
281-
ffi_buffer,
282-
bios::FfiOption::Some(bios::Timeout::new_ms(0)),
283-
);
284-
res.into()
285-
}
286-
}
287-
288-
impl core::fmt::Write for SerialConsole {
289-
fn write_str(&mut self, data: &str) -> core::fmt::Result {
290-
let api = API.get();
291-
let is_panic = IS_PANIC.load(Ordering::Relaxed);
292-
let res = (api.serial_write)(
293-
// Which port
294-
self.0,
295-
// Data
296-
bios::FfiByteSlice::new(data.as_bytes()),
297-
// No timeout
298-
bios::FfiOption::None,
299-
);
300-
if !is_panic {
301-
res.unwrap();
302-
}
303-
Ok(())
304-
}
305-
}
306-
321+
/// Local context used by the main menu.
322+
///
323+
/// Stuff goes here in preference, but we take it out of here and make it a
324+
/// global if we have to.
307325
pub struct Ctx {
308326
config: config::Config,
309327
tpa: program::TransientProgramArea,

src/program.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ extern "C" fn api_write(
325325
}
326326
let mut guard = crate::SERIAL_CONSOLE.lock();
327327
if let Some(console) = guard.as_mut() {
328-
console.write_bstr(buffer.as_slice());
328+
// Ignore serial errors on stdout
329+
let _ = console.write_bstr(buffer.as_slice());
329330
}
330331
neotron_api::Result::Ok(())
331332
} else {

src/refcell.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,6 @@ impl<T> CsRefCell<T> {
5151
}
5252
}
5353

54-
/// Try and do something with the lock.
55-
pub fn with<F, U>(&self, f: F) -> Result<U, LockError>
56-
where
57-
F: FnOnce(&mut CsRefCellGuard<T>) -> U,
58-
{
59-
let mut guard = self.try_lock()?;
60-
let result = f(&mut guard);
61-
drop(guard);
62-
Ok(result)
63-
}
64-
6554
/// Lock the cell.
6655
///
6756
/// If you can't lock it (because it is already locked), this function will panic.

0 commit comments

Comments
 (0)