diff --git a/src/lib.rs b/src/lib.rs index d0bf77f..a3a1f8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 you won't get a picture. + /// + /// 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 @@ -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 diff --git a/src/video.rs b/src/video.rs index f03931e..1452969 100644 --- a/src/video.rs +++ b/src/video.rs @@ -52,6 +52,8 @@ 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 = ""] @@ -59,6 +61,8 @@ 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 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 = ""] @@ -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 }); @@ -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();