Skip to content

Commit

Permalink
Changed the video mode change API.
Browse files Browse the repository at this point in the history
Now you have to pass the framebuffer address and the mode together.
This lets you change them in a single atomic operation. Otherwise
it was possible to crash the system by reading too much data from
the wrong framebuffer pointer.
  • Loading branch information
thejpster committed Feb 4, 2024
1 parent 000f318 commit 6aa856d
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 30 deletions.
48 changes: 18 additions & 30 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,19 +272,26 @@ pub struct Api {
// ========================================================================
/// Does this Neotron BIOS support this video mode?
pub video_is_valid_mode: extern "C" fn(mode: video::Mode) -> bool,
/// Does this Neotron BIOS require extra VRAM (passed with
/// `video_set_framebuffer`) before this mode will work?
/// Does this Neotron BIOS require extra VRAM for this mode to work?
///
/// If `true` returned here, you must pass some VRAM in the call to
/// [`Api::video_set_mode`], otherwise that function will return an error.
///
/// If `false` returned here, you can pass NULL to [`Api::video_set_mode`].
pub video_mode_needs_vram: extern "C" fn(mode: video::Mode) -> bool,
/// Switch to a new video mode.
/// Switch to a new video mode, passing an optional pointer to some VRAM.
///
/// The contents of the screen are undefined after a call to this function.
/// If the `vram` pointer is NULL, the BIOS will attempt to use any internal
/// VRAM it has available. If it doesn't have enough VRAM, you will get no
/// picture.
///
/// If the BIOS does not have enough reserved RAM (or dedicated VRAM) to
/// support this mode, the change will succeed but a subsequent call to
/// `video_get_framebuffer` will return `null`. You must then supply a
/// pointer to a block of size `Mode::frame_size_bytes()` to
/// `video_set_framebuffer` before any video will appear.
pub video_set_mode: extern "C" fn(mode: video::Mode) -> crate::ApiResult<()>,
/// # Safety
///
/// If a non-null `vram` value is given, it must be the start of a 32-bit
/// aligned block which is at least [`frame_size_bytes()`](
/// video::Mode::frame_size_bytes) bytes in length
pub video_set_mode:
unsafe extern "C" fn(mode: video::Mode, vram: *mut u32) -> crate::ApiResult<()>,
/// Returns the video mode the BIOS is currently in.
///
/// The OS should call this function immediately after start-up and note
Expand All @@ -304,26 +311,7 @@ pub struct Api {
/// framebuffer to `video_set_framebuffer`. The BIOS will always be able
/// to provide the 'basic' text buffer experience from reserves, so this
/// function will never return `null` on start-up.
pub video_get_framebuffer: extern "C" fn() -> *mut u8,
/// Set the framebuffer address.
///
/// Tell the BIOS where it should start fetching pixel or textual data
/// from(depending on the current video mode). This pointer is retained
/// and the memory is continually acccessed after this function call ends.
///
/// This value is forgotten after a video mode change and must be
/// re-supplied.
///
/// Once the BIOS has handed over to the OS, it will never write to this
/// video memory, only read from it.
///
/// # Safety
///
/// The region pointed to by `start_address` must be large enough to
/// contain however much video memory is required by both the current
/// video mode.
pub video_set_framebuffer:
unsafe extern "C" fn(start_address: *const u8) -> crate::ApiResult<()>,
pub video_get_framebuffer: extern "C" fn() -> *mut u32,
/// Wait for the next occurence of the specified video scan-line.
///
/// In general we must assume that the video memory is read top-to-bottom
Expand Down
17 changes: 17 additions & 0 deletions src/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ make_ffi_enum!("Describes the format of the video memory.",
#[doc = "character, the second `u8` unit is the foreground/background colour."]
#[doc = ""]
#[doc = "The font consists of 8px by 16px glyphs."]
#[doc = ""]
#[doc = "There must be an even number of characters per line."]
Text8x16,
#[doc = "Text mode with an 8x8 font."]
#[doc = ""]
#[doc = "Memory is arranged into `(u8, u8)` units. The first `u8` is the"]
#[doc = "character, the second `u8` unit is the foreground/background colour."]
#[doc = ""]
#[doc = "The font consists of 8px by 8px glyphs."]
#[doc = ""]
#[doc = "There must be an even number of characters per line."]
Text8x8,
#[doc = "True-colour graphics mode, with 24-bit pixels in 32-bit units."]
#[doc = ""]
Expand All @@ -69,26 +73,36 @@ make_ffi_enum!("Describes the format of the video memory.",
#[doc = ""]
#[doc = "Memory is arranged into `u16` units. Each unit is of the format"]
#[doc = "`0bRRRRR_GGGGGG_BBBBB`."]
#[doc = ""]
#[doc = "There must be an even number of pixels per line."]
Chunky16,
#[doc = "Colour graphics mode, with 8-bit indexed pixels."]
#[doc = ""]
#[doc = "Memory is arranged into `u8` units. Each unit is a lookup into the"]
#[doc = "palette."]
#[doc = ""]
#[doc = "The number of pixels per line must be a multiple of 8."]
Chunky8,
#[doc = "Colour graphics mode, with 4-bit indexed pixels."]
#[doc = ""]
#[doc = "Memory is arranged into `u8` units. Each unit is two 4-bit pixels,"]
#[doc = "each a lookup into the palette, or `0bAAAA_BBBB`."]
#[doc = ""]
#[doc = "The number of pixels per line must be a multiple of 8."]
Chunky4,
#[doc = "Colour graphics mode, with 2-bit indexed pixels."]
#[doc = ""]
#[doc = "Memory is arranged into `u8` units. Each unit is four 2-bit pixels,"]
#[doc = "each a lookup into the palette, or `0bAA_BB_CC_DD`"]
#[doc = ""]
#[doc = "The number of pixels per line must be a multiple of 16."]
Chunky2,
#[doc = "Mono graphics mode, with 1-bit per pixel."]
#[doc = ""]
#[doc = "Memory is arranged into `u8` units. Each unit is eight 1-bit pixels,"]
#[doc = "each a lookup into the palette, or `0bA_B_C_D_E_F_G_H`"]
#[doc = ""]
#[doc = "The number of pixels per line must be a multiple of 32."]
Chunky1
});

Expand Down Expand Up @@ -326,6 +340,9 @@ impl Mode {
}

/// Gets how big the frame is, in bytes.
///
/// This will always be a multiple of four, because of the constraints
/// placed on the various formats we support.
#[inline]
pub const fn frame_size_bytes(self) -> usize {
let line_size = self.line_size_bytes();
Expand Down

0 comments on commit 6aa856d

Please sign in to comment.