Skip to content

Commit dda9a5b

Browse files
authored
Merge pull request #71 from Neotron-Compute/mode-support
Can list and change video modes.
2 parents 2439089 + e5b1fea commit dda9a5b

File tree

12 files changed

+184
-64
lines changed

12 files changed

+184
-64
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
## Unreleased changes
44

5-
* None
5+
* Can set/set video mode
6+
* Stores video mode as part of config
7+
* Removed demo commands (they should be applications)
8+
* Added raw PCM sound playback
9+
* Added mixer command
10+
* Switch to neotron-common-bios 0.11.1
611

712
## v0.5.0
813

Cargo.lock

Lines changed: 2 additions & 2 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
@@ -41,7 +41,7 @@ panic = "abort"
4141
panic = "abort"
4242

4343
[dependencies]
44-
neotron-common-bios = "0.11"
44+
neotron-common-bios = "0.11.1"
4545
pc-keyboard = "0.7"
4646
r0 = "1.0"
4747
heapless = "0.7"

src/commands/config.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Configuration related commands for Neotron OS
22
3-
use crate::{config, osprintln, Ctx};
3+
use crate::{bios, config, osprintln, Ctx};
44

55
pub static COMMAND_ITEM: menu::Item<Ctx> = menu::Item {
66
item_type: menu::ItemType::Callback {
@@ -42,16 +42,25 @@ fn command(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx:
4242
}
4343
},
4444
"vga" => match args.get(1).cloned() {
45-
Some("on") => {
46-
ctx.config.set_vga_console(true);
47-
osprintln!("VGA now on");
48-
}
4945
Some("off") => {
50-
ctx.config.set_vga_console(false);
46+
ctx.config.set_vga_console(None);
5147
osprintln!("VGA now off");
5248
}
49+
Some(mode_str) => {
50+
let Some(mode) = mode_str
51+
.parse::<u8>()
52+
.ok()
53+
.and_then(bios::video::Mode::try_from_u8)
54+
.filter(|m| m.is_text_mode())
55+
else {
56+
osprintln!("Not a valid text mode");
57+
return;
58+
};
59+
ctx.config.set_vga_console(Some(mode));
60+
osprintln!("VGA set to mode {}", mode.as_u8());
61+
}
5362
_ => {
54-
osprintln!("Give on or off as argument");
63+
osprintln!("Give integer or off as argument");
5564
}
5665
},
5766
"serial" => match (args.get(1).cloned(), args.get(1).map(|s| s.parse::<u32>())) {
@@ -68,7 +77,14 @@ fn command(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx:
6877
}
6978
},
7079
"print" => {
71-
osprintln!("VGA : {}", ctx.config.get_vga_console());
80+
match ctx.config.get_vga_console() {
81+
Some(m) => {
82+
osprintln!("VGA : Mode {}", m.as_u8());
83+
}
84+
None => {
85+
osprintln!("VGA : off");
86+
}
87+
};
7288
match ctx.config.get_serial_console() {
7389
None => {
7490
osprintln!("Serial: off");
@@ -83,7 +99,7 @@ fn command(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx:
8399
osprintln!("config help - print this help text");
84100
osprintln!("config reset - load config from BIOS store");
85101
osprintln!("config save - save config to BIOS store");
86-
osprintln!("config vga on - turn VGA on");
102+
osprintln!("config vga <n> - enable VGA in Mode <n>");
87103
osprintln!("config vga off - turn VGA off");
88104
osprintln!("config serial off - turn serial console off");
89105
osprintln!("config serial <baud> - turn serial console on with given baud rate");

src/commands/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub static OS_MENU: menu::Menu<Ctx> = menu::Menu {
2828
&ram::LOAD_ITEM,
2929
&fs::LOAD_ITEM,
3030
&screen::CLS_ITEM,
31+
&screen::MODE_ITEM,
3132
&input::KBTEST_ITEM,
3233
&hardware::SHUTDOWN_ITEM,
3334
&sound::MIXER_ITEM,

src/commands/screen.rs

Lines changed: 104 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,118 @@
11
//! Screen-related commands for Neotron OS
22
3-
use crate::{osprint, Ctx};
3+
use crate::{
4+
bios::{
5+
video::{Format, Mode},
6+
ApiResult,
7+
},
8+
osprint, osprintln, Ctx,
9+
};
410

511
pub static CLS_ITEM: menu::Item<Ctx> = menu::Item {
612
item_type: menu::ItemType::Callback {
7-
function: cls,
13+
function: cls_cmd,
814
parameters: &[],
915
},
1016
command: "cls",
1117
help: Some("Clear the screen"),
1218
};
1319

20+
pub static MODE_ITEM: menu::Item<Ctx> = menu::Item {
21+
item_type: menu::ItemType::Callback {
22+
function: mode_cmd,
23+
parameters: &[menu::Parameter::Optional {
24+
parameter_name: "new_mode",
25+
help: Some("The new text mode to change to"),
26+
}],
27+
},
28+
command: "mode",
29+
help: Some("List possible video modes"),
30+
};
31+
1432
/// Called when the "cls" command is executed.
15-
fn cls(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
33+
fn cls_cmd(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
1634
// Reset SGR, go home, clear screen,
17-
let _ = osprint!("\u{001b}[0m\u{001b}[1;1H\u{001b}[2J");
35+
osprint!("\u{001b}[0m\u{001b}[1;1H\u{001b}[2J");
1836
}
37+
38+
/// Called when the "mode" command is executed
39+
fn mode_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) {
40+
if let Some(new_mode) = menu::argument_finder(item, args, "new_mode").unwrap() {
41+
let Ok(mode_num) = new_mode.parse::<u8>() else {
42+
osprintln!("Invalid integer {:?}", new_mode);
43+
return;
44+
};
45+
let Some(mode) = Mode::try_from_u8(mode_num) else {
46+
osprintln!("Invalid mode {:?}", new_mode);
47+
return;
48+
};
49+
let has_vga = {
50+
let mut guard = crate::VGA_CONSOLE.lock();
51+
guard.as_mut().is_some()
52+
};
53+
if !has_vga {
54+
osprintln!("No VGA console.");
55+
return;
56+
}
57+
let api = crate::API.get();
58+
match mode.format() {
59+
Format::Text8x16 => {}
60+
Format::Text8x8 => {}
61+
_ => {
62+
osprintln!("Not a text mode?");
63+
return;
64+
}
65+
}
66+
match (api.video_set_mode)(mode) {
67+
ApiResult::Ok(_) => {
68+
let mut guard = crate::VGA_CONSOLE.lock();
69+
if let Some(console) = guard.as_mut() {
70+
console.change_mode(mode);
71+
}
72+
osprintln!("Now in mode {}", mode.as_u8());
73+
}
74+
ApiResult::Err(e) => {
75+
osprintln!("Failed to change mode: {:?}", e);
76+
}
77+
}
78+
} else {
79+
print_modes();
80+
}
81+
}
82+
83+
/// Print out all supported video modes
84+
fn print_modes() {
85+
let api = crate::API.get();
86+
let current_mode = (api.video_get_mode)();
87+
let mut any_mode = false;
88+
for mode_no in 0..255 {
89+
// Note (unsafe): we'll test if it's right before we try and use it
90+
let Some(m) = Mode::try_from_u8(mode_no) else {
91+
continue;
92+
};
93+
let is_supported = (api.video_is_valid_mode)(m);
94+
if is_supported {
95+
any_mode = true;
96+
let is_current = if current_mode == m { "*" } else { " " };
97+
let text_rows = m.text_height();
98+
let text_cols = m.text_width();
99+
let f = m.format();
100+
let width = m.horizontal_pixels();
101+
let height = m.vertical_lines();
102+
let hz = m.frame_rate_hz();
103+
if let (Some(text_rows), Some(text_cols)) = (text_rows, text_cols) {
104+
// It's a text mode
105+
osprintln!("{mode_no:3}{is_current}: {width} x {height} @ {hz} Hz {f} ({text_cols} x {text_rows})");
106+
} else {
107+
// It's a framebuffer mode
108+
let f = m.format();
109+
osprintln!("{mode_no:3}{is_current}: {width} x {height} @ {hz} Hz {f}");
110+
}
111+
}
112+
}
113+
if !any_mode {
114+
osprintln!("No valid modes found");
115+
}
116+
}
117+
118+
// End of file

src/commands/sound.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Sound related commands for Neotron OS
22
3-
use crate::{osprint, osprintln, Ctx, API};
3+
use crate::{bios, osprint, osprintln, Ctx, API};
44

55
pub static MIXER_ITEM: menu::Item<Ctx> = menu::Item {
66
item_type: menu::ItemType::Callback {
@@ -53,7 +53,7 @@ fn mixer(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx: &
5353
let mut found = false;
5454
for mixer_id in 0u8..=255u8 {
5555
match (api.audio_mixer_channel_get_info)(mixer_id) {
56-
neotron_common_bios::FfiOption::Some(mixer_info) => {
56+
bios::FfiOption::Some(mixer_info) => {
5757
if mixer_info.name.as_str() == selected_mixer {
5858
if let Err(e) =
5959
(api.audio_mixer_channel_set_level)(mixer_id, level_int).into()
@@ -70,7 +70,7 @@ fn mixer(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx: &
7070
break;
7171
}
7272
}
73-
neotron_common_bios::FfiOption::None => {
73+
bios::FfiOption::None => {
7474
break;
7575
}
7676
}
@@ -84,11 +84,11 @@ fn mixer(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx: &
8484
osprintln!("Mixers:");
8585
for mixer_id in 0u8..=255u8 {
8686
match (api.audio_mixer_channel_get_info)(mixer_id) {
87-
neotron_common_bios::FfiOption::Some(mixer_info) => {
87+
bios::FfiOption::Some(mixer_info) => {
8888
let dir_str = match mixer_info.direction {
89-
neotron_common_bios::audio::Direction::Input => "In",
90-
neotron_common_bios::audio::Direction::Loopback => "Loop",
91-
neotron_common_bios::audio::Direction::Output => "Out",
89+
bios::audio::Direction::Input => "In",
90+
bios::audio::Direction::Loopback => "Loop",
91+
bios::audio::Direction::Output => "Out",
9292
};
9393
if selected_mixer
9494
.and_then(|s| Some(s == mixer_info.name.as_str()))
@@ -104,7 +104,7 @@ fn mixer(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx: &
104104
);
105105
}
106106
}
107-
neotron_common_bios::FfiOption::None => {
107+
bios::FfiOption::None => {
108108
// Run out of mixers
109109
break;
110110
}
@@ -117,7 +117,7 @@ fn play(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx: &m
117117
fn play_inner(
118118
file_name: &str,
119119
scratch: &mut [u8],
120-
) -> Result<(), embedded_sdmmc::Error<neotron_common_bios::Error>> {
120+
) -> Result<(), embedded_sdmmc::Error<bios::Error>> {
121121
osprintln!("Loading /{} from Block Device 0", file_name);
122122
let bios_block = crate::fs::BiosBlock();
123123
let time = crate::fs::BiosTime();
@@ -142,7 +142,7 @@ fn play(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx: &m
142142
let bytes_read = mgr.read(&mut volume, &mut file, &mut buffer)?;
143143
let mut buffer = &buffer[0..bytes_read];
144144
while !buffer.is_empty() {
145-
let slice = neotron_common_bios::FfiByteSlice::new(buffer);
145+
let slice = bios::FfiByteSlice::new(buffer);
146146
let played = unsafe { (api.audio_output_data)(slice).unwrap() };
147147
buffer = &buffer[played..];
148148
delta += played;

src/config.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
88
/// Represents our configuration information that we ask the BIOS to serialise
99
#[derive(Debug, Serialize, Deserialize)]
1010
pub struct Config {
11-
vga_console: bool,
11+
vga_console: Option<u8>,
1212
serial_console: bool,
1313
serial_baud: u32,
1414
}
@@ -39,13 +39,13 @@ impl Config {
3939
}
4040

4141
/// Should this system use the VGA console?
42-
pub fn get_vga_console(&self) -> bool {
43-
self.vga_console
42+
pub fn get_vga_console(&self) -> Option<bios::video::Mode> {
43+
self.vga_console.and_then(bios::video::Mode::try_from_u8)
4444
}
4545

4646
// Set whether this system should use the VGA console.
47-
pub fn set_vga_console(&mut self, new_value: bool) {
48-
self.vga_console = new_value;
47+
pub fn set_vga_console(&mut self, new_value: Option<bios::video::Mode>) {
48+
self.vga_console = new_value.map(|m| m.as_u8());
4949
}
5050

5151
/// Should this system use the UART console?
@@ -82,7 +82,7 @@ impl Config {
8282
impl core::default::Default for Config {
8383
fn default() -> Config {
8484
Config {
85-
vga_console: true,
85+
vga_console: Some(0),
8686
serial_console: false,
8787
serial_baud: 115200,
8888
}

src/fs.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
//! Filesystem related types
22
33
use chrono::{Datelike, Timelike};
4-
use neotron_common_bios as bios;
54

6-
use crate::API;
5+
use crate::{bios, API};
76

87
pub struct BiosBlock();
98

src/lib.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -376,20 +376,19 @@ pub extern "C" fn os_main(api: &bios::Api) -> ! {
376376
}
377377

378378
let api = API.get();
379-
if (api.api_version_get)() != neotron_common_bios::API_VERSION {
379+
if (api.api_version_get)() != bios::API_VERSION {
380380
panic!("API mismatch!");
381381
}
382382

383383
let config = config::Config::load().unwrap_or_default();
384384

385-
if config.get_vga_console() {
386-
// Try and set 80x30 mode for maximum compatibility
387-
(api.video_set_mode)(bios::video::Mode::new(
388-
bios::video::Timing::T640x480,
389-
bios::video::Format::Text8x16,
390-
));
385+
if let Some(mut mode) = config.get_vga_console() {
386+
// Set the configured mode
387+
if let bios::FfiResult::Err(_e) = (api.video_set_mode)(mode) {
388+
// Failed to change mode - check what mode we're in
389+
mode = (api.video_get_mode)();
390+
};
391391
// Work with whatever we get
392-
let mode = (api.video_get_mode)();
393392
let (width, height) = (mode.text_width(), mode.text_height());
394393

395394
if let (Some(width), Some(height)) = (width, height) {

0 commit comments

Comments
 (0)