From 88b28f0c0cd14c85be56070ec52861b152e24bef Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 27 Dec 2024 17:34:59 +0000 Subject: [PATCH 1/5] Fix some whitespace issues in the docs. --- samples/chello/neotron.h | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/chello/neotron.h b/samples/chello/neotron.h index 8f57c16..c4d0f86 100644 --- a/samples/chello/neotron.h +++ b/samples/chello/neotron.h @@ -381,7 +381,7 @@ typedef struct NeotronApi * * * You cannot rename a file if it is currently open. * * You cannot rename a file where the `old_path` and the `new_path` are - * not on the same drive. + * not on the same drive. * * Paths must confirm to the rules for the filesystem for the given drive. */ struct FfiResult_void (*rename)(struct FfiString old_path, struct FfiString new_path); diff --git a/src/lib.rs b/src/lib.rs index 23df9f3..5aa1943 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,7 +189,7 @@ impl File { /// /// * You cannot rename a file if it is currently open. /// * You cannot rename a file where the `old_path` and the `new_path` are - /// not on the same drive. + /// not on the same drive. /// * Paths must confirm to the rules for the filesystem for the given drive. pub fn rename(old_path: path::Path, new_path: path::Path) -> Result<()> { let api = get_api(); From 49509fce305a4d7cf956dcfcaba06a06a307fd7e Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 27 Dec 2024 17:39:50 +0000 Subject: [PATCH 2/5] Make ioctls unsafe, and add some defines. --- Cargo.toml | 1 + src/ioctls/gfx.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/ioctls/mod.rs | 3 +++ src/lib.rs | 14 ++++++++++++-- 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/ioctls/gfx.rs create mode 100644 src/ioctls/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 75555ed..7e7c319 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ authors = ["Jonathan 'theJPster' Pallant "] [dependencies] neotron-ffi = "0.1" neotron-api = "0.2" +neotron-common-bios = "0.12" [target.'cfg(unix)'.dependencies] crossterm = "0.26" diff --git a/src/ioctls/gfx.rs b/src/ioctls/gfx.rs new file mode 100644 index 0000000..5210764 --- /dev/null +++ b/src/ioctls/gfx.rs @@ -0,0 +1,44 @@ +//! Graphics ioctl constants + +/// Clear the screen +/// +/// The corresponding value is the fill colour, which is taken modulo the number of on-screen colours. +pub const COMMAND_CLEAR_SCREEN: u64 = 0; + +/// Plot a chunky pixel +/// +/// The command contains [ x | y | mode | colour ]. +/// +/// * `x` is 16 bits and marks the horizontal position (0 is left) +/// * `y` is 16 bits and marks the vertical position (0 is top) +/// * `mode` is 8 bits and is currently ignored +/// * `colour` is 24 bits, and is taken modulo the number of on-screen colours +/// +/// Use [`chunky_plot_value`] to create a suitable value for this ioctl command. +pub const COMMAND_CHUNKY_PLOT: u64 = 1; + +/// Change graphics mode +/// +/// The command contains the video mode in the upper 32 bits and a pointer to a +/// framebuffer in the lower 32 bits. +/// +/// The framebuffer pointer must point to a 32-bit aligned region of memory +/// that is large enough for the selected mode. If you pass `null`, then the OS +/// will attempt to allocate a framebuffer for you. +/// +/// Use [`change_mode_value`] to construct a value. +pub const COMMAND_CHANGE_MODE: u64 = 2; + +/// Calculate a 64-bit value argument for a gfx ioctl +pub fn chunky_plot_value(x: u16, y: u16, colour: u32) -> u64 { + (x as u64) << 48 | (y as u64) << 32 | (colour & 0xFFFFFF) as u64 +} + +/// Calculate a 64-bit value argument for a gfx ioctl +pub fn change_mode_value(mode: crate::VideoMode, fb_ptr: *mut u32) -> u64 { + let fb_ptr = fb_ptr as usize as u64; + let mode = mode.as_u8() as u64; + mode << 32 | fb_ptr +} + +// End of file diff --git a/src/ioctls/mod.rs b/src/ioctls/mod.rs new file mode 100644 index 0000000..9379d18 --- /dev/null +++ b/src/ioctls/mod.rs @@ -0,0 +1,3 @@ +//! A collection of ioctl constants + +pub mod gfx; diff --git a/src/lib.rs b/src/lib.rs index 5aa1943..b85e30f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,10 +27,14 @@ pub use neotron_ffi::{FfiBuffer, FfiByteSlice, FfiString}; pub use neotron_api::{file::Flags, path, Api, Error}; +pub use neotron_common_bios::video::Mode as VideoMode; + use neotron_api as api; pub mod console; +pub mod ioctls; + #[cfg(not(target_os = "none"))] mod fake_os_api; @@ -204,8 +208,14 @@ impl File { /// Perform a special I/O control operation. /// - /// The allowed values of `command` and `value` are TBD. - pub fn ioctl(&self, command: u64, value: u64) -> Result { + /// The allowed values of `command` and `value` are defined in the + /// [`ioctls`] module. + /// + /// # Safety + /// + /// Refer to the documentation for the ioctl you are using. Raw pointers may + /// be involved. + pub unsafe fn ioctl(&self, command: u64, value: u64) -> Result { let api = get_api(); match (api.ioctl)(self.0, command, value) { neotron_ffi::FfiResult::Ok(output) => Ok(output), From f4e2c70c811da7452857c51ba61c3f9db02404c8 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 28 Dec 2024 18:27:13 +0000 Subject: [PATCH 3/5] Add line drawing ioctls. --- src/ioctls/gfx.rs | 42 +++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 2 +- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/ioctls/gfx.rs b/src/ioctls/gfx.rs index 5210764..bb8e021 100644 --- a/src/ioctls/gfx.rs +++ b/src/ioctls/gfx.rs @@ -5,7 +5,7 @@ /// The corresponding value is the fill colour, which is taken modulo the number of on-screen colours. pub const COMMAND_CLEAR_SCREEN: u64 = 0; -/// Plot a chunky pixel +/// Plot a chunky pixel (and move the cursor). /// /// The command contains [ x | y | mode | colour ]. /// @@ -29,16 +29,52 @@ pub const COMMAND_CHUNKY_PLOT: u64 = 1; /// Use [`change_mode_value`] to construct a value. pub const COMMAND_CHANGE_MODE: u64 = 2; -/// Calculate a 64-bit value argument for a gfx ioctl +/// Move the cursor +/// +/// The command contains [ x | y | ]. +/// +/// The graphics handle maintains a virtual cursor position. This is used for +/// plotting lines for example - you move the cursor to one end of the line, and +/// then issue a LINE_DRAW ioctl containing the other end of the line. This saves +/// having to try and pass two sets of co-ordinates within one ioctl. +/// +/// Use [`move_cursor_value`] to construct a value. +pub const COMMAND_MOVE_CURSOR: u64 = 3; + +/// Draw a line +/// +/// The command contains [ x | y | mode | colour ]. +/// +/// * `x` is 16 bits and marks the final horizontal position (0 is left) +/// * `y` is 16 bits and marks the final vertical position (0 is top) +/// * `mode` is 8 bits and is currently ignored +/// * `colour` is 24 bits, and is taken modulo the number of on-screen colours +/// +/// The start position is the cursor position. The cursor is updated to the final position. +/// +/// Use [`draw_line_value`] to construct a value. +pub const COMMAND_DRAW_LINE: u64 = 4; + +/// Calculate a 64-bit value argument for the [`COMMAND_CHUNKY_PLOT`] gfx ioctl pub fn chunky_plot_value(x: u16, y: u16, colour: u32) -> u64 { (x as u64) << 48 | (y as u64) << 32 | (colour & 0xFFFFFF) as u64 } -/// Calculate a 64-bit value argument for a gfx ioctl +/// Calculate a 64-bit value argument for the [`COMMAND_CHANGE_MODE`] gfx ioctl pub fn change_mode_value(mode: crate::VideoMode, fb_ptr: *mut u32) -> u64 { let fb_ptr = fb_ptr as usize as u64; let mode = mode.as_u8() as u64; mode << 32 | fb_ptr } +/// Calculate a 64-bit value argument for the [`COMMAND_MOVE_CURSOR`] gfx ioctl +pub fn move_cursor_value(x: u16, y: u16) -> u64 { + (x as u64) << 48 | (y as u64) << 32 +} + +/// Calculate a 64-bit value argument for the [`COMMAND_DRAW_LINE`] gfx ioctl +pub fn draw_line_value(end_x: u16, end_y: u16, colour: u32) -> u64 { + (end_x as u64) << 48 | (end_y as u64) << 32 | (colour & 0xFFFFFF) as u64 +} + // End of file diff --git a/src/lib.rs b/src/lib.rs index b85e30f..e23cce5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ pub use neotron_ffi::{FfiBuffer, FfiByteSlice, FfiString}; pub use neotron_api::{file::Flags, path, Api, Error}; -pub use neotron_common_bios::video::Mode as VideoMode; +pub use neotron_common_bios::video::{Format as VideoFormat, Mode as VideoMode}; use neotron_api as api; From dc84465052e22328eb9526ba1edd93670228c5ee Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 28 Dec 2024 20:43:28 +0000 Subject: [PATCH 4/5] Add a palette ioctl. --- src/ioctls/gfx.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/ioctls/gfx.rs b/src/ioctls/gfx.rs index bb8e021..7ca02df 100644 --- a/src/ioctls/gfx.rs +++ b/src/ioctls/gfx.rs @@ -55,6 +55,16 @@ pub const COMMAND_MOVE_CURSOR: u64 = 3; /// Use [`draw_line_value`] to construct a value. pub const COMMAND_DRAW_LINE: u64 = 4; +/// Set a palette entry +/// +/// The command contains [ | II | RR | GG | BB ] +/// +/// II, RR, GG and BB are 8-bit values where II is the index into the 256 long +/// palette, and RR, GG and BB are the 24-bit RGB colour for that index. +/// +/// Use [`set_palette_value`] to construct a value. +pub const COMMAND_SET_PALETTE: u64 = 5; + /// Calculate a 64-bit value argument for the [`COMMAND_CHUNKY_PLOT`] gfx ioctl pub fn chunky_plot_value(x: u16, y: u16, colour: u32) -> u64 { (x as u64) << 48 | (y as u64) << 32 | (colour & 0xFFFFFF) as u64 @@ -77,4 +87,13 @@ pub fn draw_line_value(end_x: u16, end_y: u16, colour: u32) -> u64 { (end_x as u64) << 48 | (end_y as u64) << 32 | (colour & 0xFFFFFF) as u64 } +/// Calculate a 64-bit value argument for the [`COMMAND_SET_PALETTE`] gfx ioctl +pub fn set_palette_value(index: u8, r: u8, g: u8, b: u8) -> u64 { + let mut result = (index as u64) << 24; + result |= (r as u64) << 16; + result |= (g as u64) << 8; + result |= b as u64; + result +} + // End of file From 645866224f5d4d74a8fc3884e4976a28be8b3b98 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Mon, 30 Dec 2024 15:22:29 +0000 Subject: [PATCH 5/5] Add better random number functions. Useful if you want to draw random lines. --- src/lib.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e23cce5..09f3a41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ static ARG_COUNT: AtomicUsize = AtomicUsize::new(0); static ARG_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); /// Random number generator state -static RAND_STATE: core::sync::atomic::AtomicU16 = core::sync::atomic::AtomicU16::new(0); +static RAND_STATE: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); // ============================================================================ // Types @@ -459,17 +459,34 @@ pub fn wait_for_key() -> WaitForKey { } /// Seed the 16-bit psuedorandom number generator -pub fn srand(seed: u16) { +pub fn srand(seed: u32) { RAND_STATE.store(seed, core::sync::atomic::Ordering::Relaxed); } -/// Get a 16-bit psuedorandom number -pub fn rand() -> u16 { +/// Get a 32-bit psuedorandom number +pub fn rand() -> u32 { let mut state = RAND_STATE.load(core::sync::atomic::Ordering::Relaxed); - let bit = (state ^ (state >> 2) ^ (state >> 3) ^ (state >> 5)) & 0x01; - state = (state >> 1) | (bit << 15); + state = state.wrapping_mul(1103515245).wrapping_add(12345); + let bits1 = state >> 16; + state = state.wrapping_mul(1103515245).wrapping_add(12345); + let bits2 = state >> 16; RAND_STATE.store(state, core::sync::atomic::Ordering::Relaxed); - state + (bits1 << 16) | bits2 +} + +/// Get a random number from a range +pub fn random_in(range: core::ops::Range) -> u32 { + let count = range.end - range.start; + let bucket_size = u32::MAX / count; + let limit = bucket_size * count; + let rand_value = loop { + let temp = rand() as u32; + if temp <= limit { + break temp; + } + }; + let result = rand_value / bucket_size; + result + range.start } /// Get the API structure so we can call APIs manually.