1212// Modules and Imports
1313// ===========================================================================
1414
15- use core:: sync:: atomic:: { AtomicBool , Ordering } ;
15+ use core:: sync:: atomic:: { AtomicBool , AtomicPtr , Ordering } ;
1616
1717use 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.
4646static 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
5460static 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
56213struct 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.
307325pub struct Ctx {
308326 config : config:: Config ,
309327 tpa : program:: TransientProgramArea ,
0 commit comments