Skip to content

Commit 3ce842e

Browse files
committed
Add GFX: device and some basic ioctls.
1 parent f5ad60d commit 3ce842e

File tree

2 files changed

+532
-111
lines changed

2 files changed

+532
-111
lines changed

neotron-os/src/commands/screen.rs

Lines changed: 156 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,10 @@ pub static MODE_ITEM: menu::Item<Ctx> = menu::Item {
3232
pub static GFX_ITEM: menu::Item<Ctx> = menu::Item {
3333
item_type: menu::ItemType::Callback {
3434
function: gfx_cmd,
35-
parameters: &[
36-
menu::Parameter::Mandatory {
37-
parameter_name: "new_mode",
38-
help: Some("The new gfx mode to try"),
39-
},
40-
menu::Parameter::Optional {
41-
parameter_name: "filename",
42-
help: Some("a file to display"),
43-
},
44-
],
35+
parameters: &[menu::Parameter::Mandatory {
36+
parameter_name: "new_mode",
37+
help: Some("The new gfx mode to try"),
38+
}],
4539
},
4640
command: "gfx",
4741
help: Some("Test a graphics mode"),
@@ -107,12 +101,13 @@ fn mode_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], _ctx
107101
}
108102

109103
/// Called when the "gfx" command is executed
104+
///
105+
/// Performs a selection of graphical tests
110106
fn gfx_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], ctx: &mut Ctx) {
111107
let Some(new_mode) = menu::argument_finder(item, args, "new_mode").unwrap() else {
112108
osprintln!("Missing arg");
113109
return;
114110
};
115-
let file_name = menu::argument_finder(item, args, "filename").unwrap();
116111
let Ok(mode_num) = new_mode.parse::<u8>() else {
117112
osprintln!("Invalid integer {:?}", new_mode);
118113
return;
@@ -126,58 +121,164 @@ fn gfx_cmd(_menu: &menu::Menu<Ctx>, item: &menu::Item<Ctx>, args: &[&str], ctx:
126121
let old_ptr = (api.video_get_framebuffer)();
127122

128123
let buffer = ctx.tpa.as_slice_u8();
129-
let buffer_ptr = buffer.as_mut_ptr() as *mut u32;
130-
if let Some(file_name) = file_name {
131-
let Ok(file) = crate::FILESYSTEM.open_file(file_name, embedded_sdmmc::Mode::ReadOnly)
132-
else {
133-
osprintln!("No such file.");
134-
return;
135-
};
136-
let _ = file.read(buffer);
137-
} else {
138-
let (odd_pattern, even_pattern) = match mode.format() {
139-
// This is alternating hearts and diamonds
140-
Format::Text8x16 | Format::Text8x8 => (
141-
u32::from_le_bytes(*b"\x03\x0F\x04\x70"),
142-
u32::from_le_bytes(*b"\x04\x70\x03\x0F"),
143-
),
144-
// Can't do a checkerboard here - so stripes will do
145-
Format::Chunky32 => (0x0000_0000, 0x0000_0001),
146-
// These should produce black/white checkerboard, in the default
147-
// palette
148-
Format::Chunky16 => (0x0000_FFFF, 0xFFFF_0000),
149-
Format::Chunky8 => (0x000F_000F, 0x0F00_0F00),
150-
Format::Chunky4 => (0x0F0F_0F0F, 0xF0F0_F0F0),
151-
Format::Chunky2 => (0x3333_3333, 0xCCCC_CCCC),
152-
Format::Chunky1 => (0x5555_5555, 0xAAAA_AAAA),
153-
_ => todo!(),
154-
};
155-
// draw a dummy non-zero data. In Chunky1 this is a checkerboard.
156-
let line_size_words = mode.line_size_bytes() / 4;
157-
for row in 0..mode.vertical_lines() as usize {
158-
let word = if (row % 2) == 0 {
159-
even_pattern
160-
} else {
161-
odd_pattern
162-
};
163-
for col in 0..line_size_words {
164-
let idx = (row * line_size_words) + col;
165-
unsafe {
166-
buffer_ptr.add(idx).write_volatile(word);
167-
}
168-
}
169-
}
124+
if mode.frame_size_bytes() > buffer.len() {
125+
osprintln!("Not enough space in TPA");
126+
return;
170127
}
128+
let buffer_ptr = buffer.as_mut_ptr() as *mut u32;
171129

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

178-
// Now wait for user input
179-
while crate::STD_INPUT.lock().get_raw().is_none() {
180-
// spin
136+
let gen_value = |x: u16, y: u16, colour: u32| -> u64 {
137+
(x as u64) << 48 | (y as u64) << 32 | (colour & 0xFFFFFF) as u64
138+
};
139+
140+
// plots a vertical colour stripe pattern.
141+
let vertical = |handle| -> Result<(), neotron_api::Error> {
142+
for y in 0..mode.vertical_lines() {
143+
let mut colour = 0u32;
144+
for x in 0..mode.horizontal_pixels() {
145+
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
146+
handle,
147+
crate::program::GFX_COMMAND_CHUNKY_PLOT,
148+
gen_value(x, y, colour),
149+
)
150+
.into();
151+
result?;
152+
colour = colour.wrapping_add(1) & 0xFFFFFF;
153+
}
154+
}
155+
156+
// Now wait for user input
157+
while crate::STD_INPUT.lock().get_raw().is_none() {
158+
// spin
159+
}
160+
161+
Ok(())
162+
};
163+
164+
// plots a horizontal colour stripe pattern.
165+
let horizontal = |handle| -> Result<(), neotron_api::Error> {
166+
let mut colour = 0u32;
167+
for y in 0..mode.vertical_lines() {
168+
for x in 0..mode.horizontal_pixels() {
169+
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
170+
handle,
171+
crate::program::GFX_COMMAND_CHUNKY_PLOT,
172+
gen_value(x, y, colour),
173+
)
174+
.into();
175+
result?;
176+
}
177+
colour = colour.wrapping_add(1) & 0xFFFFFF;
178+
}
179+
180+
// Now wait for user input
181+
while crate::STD_INPUT.lock().get_raw().is_none() {
182+
// spin
183+
}
184+
185+
Ok(())
186+
};
187+
188+
// plots a rolling stripe pattern.
189+
let rolling = |handle| -> Result<(), neotron_api::Error> {
190+
for y in 0..mode.vertical_lines() {
191+
let mut colour = y as u32;
192+
for x in 0..mode.horizontal_pixels() {
193+
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
194+
handle,
195+
crate::program::GFX_COMMAND_CHUNKY_PLOT,
196+
gen_value(x, y, colour),
197+
)
198+
.into();
199+
result?;
200+
colour = colour.wrapping_add(1) & 0xFFFFFF;
201+
}
202+
}
203+
204+
// Now wait for user input
205+
while crate::STD_INPUT.lock().get_raw().is_none() {
206+
// spin
207+
}
208+
209+
Ok(())
210+
};
211+
212+
// plots a grid
213+
let grid = |handle| -> Result<(), neotron_api::Error> {
214+
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
215+
handle,
216+
crate::program::GFX_COMMAND_CLEAR_SCREEN,
217+
0,
218+
)
219+
.into();
220+
result?;
221+
222+
let width = mode.horizontal_pixels() / 10;
223+
let height = mode.vertical_lines() / 10;
224+
225+
for y in 0..mode.vertical_lines() {
226+
if (y % height) == 0 || (y == mode.vertical_lines() - 1) {
227+
// solid line
228+
for x in 0..mode.horizontal_pixels() {
229+
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
230+
handle,
231+
crate::program::GFX_COMMAND_CHUNKY_PLOT,
232+
gen_value(x, y, 15),
233+
)
234+
.into();
235+
result?;
236+
}
237+
} else {
238+
// stripes
239+
for x in 0..mode.horizontal_pixels() {
240+
let colour = if (x % width) == 0 || (x == mode.horizontal_pixels() - 1) {
241+
15
242+
} else {
243+
0
244+
};
245+
let result: Result<_, _> = (crate::program::CALLBACK_TABLE.ioctl)(
246+
handle,
247+
crate::program::GFX_COMMAND_CHUNKY_PLOT,
248+
gen_value(x, y, colour),
249+
)
250+
.into();
251+
result?;
252+
}
253+
}
254+
}
255+
256+
// Now wait for user input
257+
while crate::STD_INPUT.lock().get_raw().is_none() {
258+
// spin
259+
}
260+
261+
Ok(())
262+
};
263+
264+
let handle: Result<_, _> =
265+
(crate::program::CALLBACK_TABLE.open)("GFX:".into(), neotron_api::file::Flags::WRITE)
266+
.into();
267+
if let Ok(handle) = handle {
268+
if let Err(e) = vertical(handle) {
269+
osprintln!("Draw failure on vertical: {:?}", e);
270+
}
271+
if let Err(e) = horizontal(handle) {
272+
osprintln!("Draw failure on horizontal: {:?}", e);
273+
}
274+
if let Err(e) = rolling(handle) {
275+
osprintln!("Draw failure on rolling: {:?}", e);
276+
}
277+
if let Err(e) = grid(handle) {
278+
osprintln!("Draw failure on grid: {:?}", e);
279+
}
280+
// close the handle
281+
let _ = (crate::program::CALLBACK_TABLE.close)(handle);
181282
}
182283

183284
// Put it back as it was

0 commit comments

Comments
 (0)