Skip to content

Commit 83e1a4e

Browse files
committed
Working dual vga/serial console.
1 parent cca92dd commit 83e1a4e

File tree

5 files changed

+200
-50
lines changed

5 files changed

+200
-50
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ opt-level = "s"
2121

2222
[dependencies.neotron-common-bios]
2323
path = "./common"
24+
25+
[dependencies.r0]
26+
version = "1.0"

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ This is the Neotron OS. It will run on any system which can execute ARM Thumb v7
77
This OS is a work in progress. We intend to support:
88

99
* [x] Calling BIOS APIs
10-
* [ ] Text mode console (on both text and bitmap displays)
10+
* [x] Text mode VGA console
11+
* [x] Serial console
1112
* [ ] Starting a command-line shell application
1213
* [ ] Executing applications from RAM
1314
* [ ] MBR/FAT32 formatted block devices with standard open/close/read/write file semantics
@@ -21,7 +22,11 @@ This OS is a work in progress. We intend to support:
2122
Your board will need an appropriate Neotron BIOS installed, and you need to have OpenOCD running for your particular board. You also need to set the linker
2223
arguments so you link the binary to suit the memory available on your system.
2324

24-
### Build Instructions for the Neotron 340ST
25+
### Build Instructions for 256K RAM systems
26+
27+
Systems which reserve the second 512 KiB of Flash and first 256 KiB of SRAM
28+
for the OS can use this linker script. These systems include the Neotron
29+
340ST.
2530

2631
```
2732
$ git clone https://github.com/neotron-compute/Neotron-OS.git
@@ -30,7 +35,10 @@ $ git submodule update --init
3035
$ RUSTFLAGS="-C link-arg=-Tneotron-os-256k.ld" cargo run --release
3136
```
3237

33-
### Build Instructions for the Neotron 32
38+
### Build Instructions for 32K RAM systems
39+
40+
Systems which reserve the second 128 KiB of Flash and first 26 KiB of SRAM for
41+
the OS can use this linker script. These systems include the Neotron 32.
3442

3543
```
3644
$ git clone https://github.com/neotron-compute/Neotron-OS.git
@@ -45,7 +53,7 @@ TODO: Think of a better way of setting the memory limits for a particular OS bui
4553

4654
### Unreleased Changes ([Source](https://github.com/neotron-compute/Neotron-OS/tree/master))
4755

48-
* Basic UART hello on start-up on the Neotron 32 and Neotron 340ST
56+
* Basic `println!` to the text buffer.
4957

5058
## Licence
5159

common

Submodule common updated 1 file

gdb.cfg

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ monitor arm semihosting enable
1818
# monitor itm port 0 on
1919

2020
load
21-
step
22-
continue
23-
21+
monitor reset halt
22+
stepi
2423

src/main.rs

Lines changed: 182 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,70 @@
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
713
use 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)]
2170
struct 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-
4478
impl 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

Comments
 (0)