Skip to content

Commit

Permalink
Move gfx command to be vidtest utility.
Browse files Browse the repository at this point in the history
Requires new neotron-sdk.

Running an application now also caches and restores the current video mode (incase the app leaves it in a mess), which is done through the VGACONSOLE object so you can't write text to a framebuffer and ruin it.
  • Loading branch information
thejpster committed Dec 27, 2024
1 parent 3ce842e commit 7800d1b
Show file tree
Hide file tree
Showing 12 changed files with 348 additions and 245 deletions.
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
members = [
"neotron-os",
"utilities/flames",
"utilities/vidtest",
]
resolver = "2"
exclude = [
"nbuild"
]

[workspace.dependencies]
neotron-sdk = "0.2.0"
neotron-sdk = { git = "https://github.com/neotron-compute/neotron-sdk.git", branch = "add-gfx-ioctl-defines" }

[profile.release]
lto = true
Expand Down
7 changes: 7 additions & 0 deletions nbuild/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ fn packages() -> Vec<nbuild::Package> {
kind: nbuild::PackageKind::Utility,
testable: false,
},
nbuild::Package {
name: "vidtest",
path: std::path::Path::new("./utilities/vidtest/Cargo.toml"),
output_template: Some("./target/{target}/{profile}/vidtest"),
kind: nbuild::PackageKind::Utility,
testable: false,
},
nbuild::Package {
name: "Neotron OS",
path: std::path::Path::new("./neotron-os/Cargo.toml"),
Expand Down
1 change: 0 additions & 1 deletion neotron-os/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ pub static OS_MENU: menu::Menu<Ctx> = menu::Menu {
&fs::ROM_ITEM,
&screen::CLS_ITEM,
&screen::MODE_ITEM,
&screen::GFX_ITEM,
&input::KBTEST_ITEM,
&hardware::SHUTDOWN_ITEM,
&sound::MIXER_ITEM,
Expand Down
251 changes: 12 additions & 239 deletions neotron-os/src/commands/screen.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
//! Screen-related commands for Neotron OS
use crate::{
bios::{
video::{Format, Mode},
ApiResult,
},
osprint, osprintln, Ctx,
};
use crate::{bios::video::Mode, osprint, osprintln, Ctx};

pub static CLS_ITEM: menu::Item<Ctx> = menu::Item {
item_type: menu::ItemType::Callback {
Expand All @@ -29,18 +23,6 @@ pub static MODE_ITEM: menu::Item<Ctx> = menu::Item {
help: Some("List/change video mode"),
};

pub static GFX_ITEM: menu::Item<Ctx> = menu::Item {
item_type: menu::ItemType::Callback {
function: gfx_cmd,
parameters: &[menu::Parameter::Mandatory {
parameter_name: "new_mode",
help: Some("The new gfx mode to try"),
}],
},
command: "gfx",
help: Some("Test a graphics mode"),
};

/// Called when the "cls" command is executed.
fn cls_cmd(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
// Reset SGR, go home, clear screen,
Expand All @@ -49,63 +31,8 @@ fn cls_cmd(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ct

/// Called when the "mode" command is executed
fn mode_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) {
if let Some(new_mode) = menu::argument_finder(item, args, "new_mode").unwrap() {
let Ok(mode_num) = new_mode.parse::<u8>() else {
osprintln!("Invalid integer {:?}", new_mode);
return;
};
let Some(mode) = Mode::try_from_u8(mode_num) else {
osprintln!("Invalid mode {:?}", new_mode);
return;
};
let has_vga = {
let mut guard = crate::VGA_CONSOLE.lock();
guard.as_mut().is_some()
};
if !has_vga {
osprintln!("No VGA console.");
return;
}
let api = crate::API.get();
match mode.format() {
Format::Text8x16 => {}
Format::Text8x8 => {}
_ => {
osprintln!("Not a text mode?");
return;
}
}
if (api.video_mode_needs_vram)(mode) {
// The OS currently has no VRAM for text modes
osprintln!("That mode requires more VRAM than the BIOS has.");
return;
}
// # Safety
//
// It's always OK to pass NULl to this API.
match unsafe { (api.video_set_mode)(mode, core::ptr::null_mut()) } {
ApiResult::Ok(_) => {
let mut guard = crate::VGA_CONSOLE.lock();
if let Some(console) = guard.as_mut() {
console.change_mode(mode);
}
osprintln!("Now in mode {}", mode.as_u8());
}
ApiResult::Err(e) => {
osprintln!("Failed to change mode: {:?}", e);
}
}
} else {
print_modes();
}
}

/// Called when the "gfx" command is executed
///
/// Performs a selection of graphical tests
fn gfx_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], ctx: &mut Ctx) {
let Some(new_mode) = menu::argument_finder(item, args, "new_mode").unwrap() else {
osprintln!("Missing arg");
print_modes();
return;
};
let Ok(mode_num) = new_mode.parse::<u8>() else {
Expand All @@ -116,174 +43,20 @@ fn gfx_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], ctx:
osprintln!("Invalid mode {:?}", new_mode);
return;
};
let api = crate::API.get();
let old_mode = (api.video_get_mode)();
let old_ptr = (api.video_get_framebuffer)();

let buffer = ctx.tpa.as_slice_u8();
if mode.frame_size_bytes() > buffer.len() {
osprintln!("Not enough space in TPA");
let mut lock = crate::VGA_CONSOLE.lock();
let Some(vga_console) = lock.as_mut() else {
osprintln!("No VGA console.");
return;
}
let buffer_ptr = buffer.as_mut_ptr() as *mut u32;

if let neotron_common_bios::FfiResult::Err(e) =
unsafe { (api.video_set_mode)(mode, buffer_ptr) }
{
osprintln!("Couldn't set mode {}: {:?}", mode_num, e);
}

let gen_value = |x: u16, y: u16, colour: u32| -> u64 {
(x as u64) << 48 | (y as u64) << 32 | (colour & 0xFFFFFF) as u64
};

// plots a vertical colour stripe pattern.
let vertical = |handle| -> Result<(), neotron_api::Error> {
for y in 0..mode.vertical_lines() {
let mut colour = 0u32;
for x in 0..mode.horizontal_pixels() {
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
handle,
crate::program::GFX_COMMAND_CHUNKY_PLOT,
gen_value(x, y, colour),
)
.into();
result?;
colour = colour.wrapping_add(1) & 0xFFFFFF;
}
// Passing a null pointer is OK here because the BIOS will either allocate
// space, or give us a blank screen.
match unsafe { vga_console.change_mode(mode, core::ptr::null_mut()) } {
Ok(_) => {
osprintln!("Changed to mode {}", mode_num);
}

// Now wait for user input
while crate::STD_INPUT.lock().get_raw().is_none() {
// spin
Err(e) => {
osprintln!("Failed to set mode {}: BIOS said {:?}", mode_num, e);
}

Ok(())
};

// plots a horizontal colour stripe pattern.
let horizontal = |handle| -> Result<(), neotron_api::Error> {
let mut colour = 0u32;
for y in 0..mode.vertical_lines() {
for x in 0..mode.horizontal_pixels() {
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
handle,
crate::program::GFX_COMMAND_CHUNKY_PLOT,
gen_value(x, y, colour),
)
.into();
result?;
}
colour = colour.wrapping_add(1) & 0xFFFFFF;
}

// Now wait for user input
while crate::STD_INPUT.lock().get_raw().is_none() {
// spin
}

Ok(())
};

// plots a rolling stripe pattern.
let rolling = |handle| -> Result<(), neotron_api::Error> {
for y in 0..mode.vertical_lines() {
let mut colour = y as u32;
for x in 0..mode.horizontal_pixels() {
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
handle,
crate::program::GFX_COMMAND_CHUNKY_PLOT,
gen_value(x, y, colour),
)
.into();
result?;
colour = colour.wrapping_add(1) & 0xFFFFFF;
}
}

// Now wait for user input
while crate::STD_INPUT.lock().get_raw().is_none() {
// spin
}

Ok(())
};

// plots a grid
let grid = |handle| -> Result<(), neotron_api::Error> {
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
handle,
crate::program::GFX_COMMAND_CLEAR_SCREEN,
0,
)
.into();
result?;

let width = mode.horizontal_pixels() / 10;
let height = mode.vertical_lines() / 10;

for y in 0..mode.vertical_lines() {
if (y % height) == 0 || (y == mode.vertical_lines() - 1) {
// solid line
for x in 0..mode.horizontal_pixels() {
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
handle,
crate::program::GFX_COMMAND_CHUNKY_PLOT,
gen_value(x, y, 15),
)
.into();
result?;
}
} else {
// stripes
for x in 0..mode.horizontal_pixels() {
let colour = if (x % width) == 0 || (x == mode.horizontal_pixels() - 1) {
15
} else {
0
};
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
handle,
crate::program::GFX_COMMAND_CHUNKY_PLOT,
gen_value(x, y, colour),
)
.into();
result?;
}
}
}

// Now wait for user input
while crate::STD_INPUT.lock().get_raw().is_none() {
// spin
}

Ok(())
};

let handle: Result<_, _> =
(crate::program::CALLBACK_TABLE.open)("GFX:".into(), neotron_api::file::Flags::WRITE)
.into();
if let Ok(handle) = handle {
if let Err(e) = vertical(handle) {
osprintln!("Draw failure on vertical: {:?}", e);
}
if let Err(e) = horizontal(handle) {
osprintln!("Draw failure on horizontal: {:?}", e);
}
if let Err(e) = rolling(handle) {
osprintln!("Draw failure on rolling: {:?}", e);
}
if let Err(e) = grid(handle) {
osprintln!("Draw failure on grid: {:?}", e);
}
// close the handle
let _ = (crate::program::CALLBACK_TABLE.close)(handle);
}

// Put it back as it was
unsafe {
(api.video_set_mode)(old_mode, old_ptr);
}
}

Expand Down
Loading

0 comments on commit 7800d1b

Please sign in to comment.