11//! # The Neotron Operating System
22//!
33//! This OS is intended to be loaded by a Neotron BIOS.
4+ //!
5+ //! Copyright (c) The Neotron Developers, 2020
6+ //!
7+ //! Licence: GPL v3 or higher (see ../LICENCE.md)
8+
49#![ no_std]
510#![ no_main]
611
12+ // Imports
713use core:: fmt:: Write ;
8- use neotron_common_bios as common ;
14+ use neotron_common_bios as bios ;
915
16+ // ===========================================================================
17+ // Global Variables
18+ // ===========================================================================
19+
20+ /// This tells the BIOS how to start the OS. This must be the first four bytes
21+ /// of our portion of Flash.
1022#[ link_section = ".entry_point" ]
11- #[ no_mangle]
1223#[ used]
13- pub static ENTRY_POINT : extern "C" fn ( & ' static common :: Api ) -> ! = entry_point ;
24+ pub static ENTRY_POINT_ADDR : extern "C" fn ( & ' static bios :: Api ) -> ! = main ;
1425
1526/// The OS version string
16- static OS_VERSION : & str = concat ! ( "Neotron OS, version " , env!( "CARGO_PKG_VERSION" ) , "\0 " ) ;
27+ const OS_VERSION : & str = concat ! ( "Neotron OS, version " , env!( "CARGO_PKG_VERSION" ) , "-2" ) ;
28+
29+ /// We store the API object supplied by the BIOS here
30+ static mut API : Option < & ' static bios:: Api > = None ;
1731
18- static mut API : Option < & ' static common:: Api > = None ;
32+ /// We store our VGA console here.
33+ static mut VGA_CONSOLE : Option < VgaConsole > = None ;
34+
35+ /// We store our VGA console here.
36+ static mut SERIAL_CONSOLE : Option < SerialConsole > = None ;
37+
38+ // ===========================================================================
39+ // Macros
40+ // ===========================================================================
41+
42+ /// Prints to the screen
43+ #[ macro_export]
44+ macro_rules! print {
45+ ( $( $arg: tt) * ) => {
46+ if let Some ( ref mut console) = unsafe { & mut VGA_CONSOLE } {
47+ write!( console, $( $arg) * ) . unwrap( ) ;
48+ }
49+ if let Some ( ref mut console) = unsafe { & mut SERIAL_CONSOLE } {
50+ write!( console, $( $arg) * ) . unwrap( ) ;
51+ }
52+ } ;
53+ }
54+
55+ /// Prints to the screen and puts a new-line on the end
56+ #[ macro_export]
57+ macro_rules! println {
58+ ( ) => ( print!( "\n " ) ) ;
59+ ( $( $arg: tt) * ) => {
60+ print!( $( $arg) * ) ;
61+ print!( "\n " ) ;
62+ } ;
63+ }
64+
65+ // ===========================================================================
66+ // Local types
67+ // ===========================================================================
1968
2069#[ derive( Debug ) ]
2170struct VgaConsole {
@@ -26,21 +75,6 @@ struct VgaConsole {
2675 col : u8 ,
2776}
2877
29- struct SerialConsole ;
30-
31- impl core:: fmt:: Write for SerialConsole {
32- fn write_str ( & mut self , data : & str ) -> core:: fmt:: Result {
33- if let Some ( api) = unsafe { API } {
34- let _res = ( api. serial_write ) (
35- 0 ,
36- common:: ApiByteSlice :: new ( data. as_bytes ( ) ) ,
37- common:: Option :: None ,
38- ) ;
39- }
40- Ok ( ( ) )
41- }
42- }
43-
4478impl VgaConsole {
4579 const DEFAULT_ATTR : u8 = ( 2 << 3 ) | ( 1 << 0 ) ;
4680
@@ -130,39 +164,145 @@ impl core::fmt::Write for VgaConsole {
130164 }
131165}
132166
133- #[ no_mangle]
134- extern "C" fn entry_point ( api : & ' static common:: Api ) -> ! {
167+ /// Represents the serial port we can use as a text input/output device.
168+ struct SerialConsole ( u8 ) ;
169+
170+ impl core:: fmt:: Write for SerialConsole {
171+ fn write_str ( & mut self , data : & str ) -> core:: fmt:: Result {
172+ if let Some ( api) = unsafe { API } {
173+ ( api. serial_write ) (
174+ // Which port
175+ self . 0 ,
176+ // Data
177+ bios:: ApiByteSlice :: new ( data. as_bytes ( ) ) ,
178+ // No timeout
179+ bios:: Option :: None ,
180+ )
181+ . unwrap ( ) ;
182+ }
183+ Ok ( ( ) )
184+ }
185+ }
186+
187+ // ===========================================================================
188+ // Private functions
189+ // ===========================================================================
190+
191+ /// Initialise our global variables - the BIOS will not have done this for us
192+ /// (as it doesn't know where they are).
193+ unsafe fn start_up_init ( ) {
194+ extern "C" {
195+
196+ // These symbols come from `link.x`
197+ static mut __sbss: u32 ;
198+ static mut __ebss: u32 ;
199+
200+ static mut __sdata: u32 ;
201+ static mut __edata: u32 ;
202+ static __sidata: u32 ;
203+ }
204+
205+ r0:: zero_bss ( & mut __sbss, & mut __ebss) ;
206+ r0:: init_data ( & mut __sdata, & mut __edata, & __sidata) ;
207+ }
208+
209+ struct Config {
210+ vga_console : bool ,
211+ serial_console : bool ,
212+ }
213+
214+ impl Config {
215+ /// Create a new default Config object
216+ ///
217+ /// TODO: We should load from EEPROM / RTC SRAM here.
218+ fn new ( ) -> Config {
219+ Config {
220+ vga_console : true ,
221+ serial_console : true ,
222+ }
223+ }
224+
225+ /// Should this system use the VGA console?
226+ fn has_vga_console ( & self ) -> bool {
227+ self . vga_console
228+ }
229+
230+ /// Should this system use the UART console?
231+ fn has_serial_console ( & self ) -> Option < ( u8 , bios:: serial:: Config ) > {
232+ if self . serial_console {
233+ Some ( (
234+ 0 ,
235+ bios:: serial:: Config {
236+ data_rate_bps : 115200 ,
237+ data_bits : bios:: serial:: DataBits :: Eight ,
238+ stop_bits : bios:: serial:: StopBits :: One ,
239+ parity : bios:: serial:: Parity :: None ,
240+ handshaking : bios:: serial:: Handshaking :: None ,
241+ } ,
242+ ) )
243+ } else {
244+ None
245+ }
246+ }
247+ }
248+
249+ // ===========================================================================
250+ // Public functions / impl for public types
251+ // ===========================================================================
252+
253+ /// This is the function the BIOS calls. This is because we store the address
254+ /// of this function in the ENTRY_POINT_ADDR variable.
255+ extern "C" fn main ( api : & ' static bios:: Api ) -> ! {
135256 unsafe {
257+ start_up_init ( ) ;
136258 API = Some ( api) ;
137259 }
138- let mut addr: * mut u8 = core:: ptr:: null_mut ( ) ;
139- let mut width = 0 ;
140- let mut height = 0 ;
141- ( api. video_memory_info_get ) ( & mut addr, & mut width, & mut height) ;
142- let mut vga = VgaConsole {
143- addr,
144- width,
145- height,
146- row : 0 ,
147- col : 0 ,
148- } ;
149- vga. find_start_row ( ) ;
150- writeln ! ( vga, "{}" , OS_VERSION ) . unwrap ( ) ;
151- writeln ! ( vga, "BIOS Version: {}" , ( api. bios_version_get) ( ) ) . unwrap ( ) ;
152- writeln ! ( vga, "BIOS API Version: {}" , ( api. api_version_get) ( ) ) . unwrap ( ) ;
153- loop {
154- for _ in 0 ..1_000_000 {
155- let _ = ( api. api_version_get ) ( ) ;
260+
261+ let config = Config :: new ( ) ;
262+
263+ if config. has_vga_console ( ) {
264+ let mut addr: * mut u8 = core:: ptr:: null_mut ( ) ;
265+ let mut width = 0 ;
266+ let mut height = 0 ;
267+ ( api. video_memory_info_get ) ( & mut addr, & mut width, & mut height) ;
268+ if addr != core:: ptr:: null_mut ( ) {
269+ let mut vga = VgaConsole {
270+ addr,
271+ width,
272+ height,
273+ row : 0 ,
274+ col : 0 ,
275+ } ;
276+ vga. find_start_row ( ) ;
277+ unsafe {
278+ VGA_CONSOLE = Some ( vga) ;
279+ }
280+ println ! ( "Configured VGA console" ) ;
156281 }
157- writeln ! ( vga, "tick..." ) . unwrap ( ) ;
158282 }
283+
284+ if let Some ( ( idx, serial_config) ) = config. has_serial_console ( ) {
285+ let _ignored = ( api. serial_configure ) ( idx, serial_config) ;
286+ unsafe { SERIAL_CONSOLE = Some ( SerialConsole ( idx) ) } ;
287+ println ! ( "Configured Serial console on Serial {}" , idx) ;
288+ }
289+
290+ // Now we can call println!
291+ println ! ( "Welcome to {}!" , OS_VERSION ) ;
292+ panic ! ( "Testing a panic..." ) ;
159293}
160294
295+ /// Called when we have a panic.
161296#[ inline( never) ]
162297#[ panic_handler]
163- fn panic ( _info : & core:: panic:: PanicInfo ) -> ! {
298+ fn panic ( info : & core:: panic:: PanicInfo ) -> ! {
299+ println ! ( "PANIC!\n {:#?}" , info) ;
164300 use core:: sync:: atomic:: { self , Ordering } ;
165301 loop {
166302 atomic:: compiler_fence ( Ordering :: SeqCst ) ;
167303 }
168304}
305+
306+ // ===========================================================================
307+ // End of file
308+ // ===========================================================================
0 commit comments