Skip to content

Commit 4a48153

Browse files
committed
Update to common 0.5.0
1 parent 3836cd0 commit 4a48153

3 files changed

Lines changed: 232 additions & 52 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ rp-pico = "0.3"
1515
# Cortex-M run-time (or start-up) code
1616
cortex-m-rt = "0.7"
1717
# The BIOS to OS API
18-
neotron-common-bios = "0.4.0"
18+
neotron-common-bios = "0.5.0"
1919
# For time keeping/handling
2020
embedded-time = "0.12"
2121
# For the RP2040 bootloader

src/main.rs

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//!
1010
//! The BIOS is started by having standard Cortex-M Interrupt Vector Table at
1111
//! address `0x1000_0100`. This IVT is found and jumped to by the RP2040 boot
12-
//! block(`0x1000_0000` to `0x1000_00FF`).
12+
//! block (`0x1000_0000` to `0x1000_00FF`).
1313
1414
// -----------------------------------------------------------------------------
1515
// Licence Statement
@@ -89,6 +89,7 @@ static API_CALLS: common::Api = common::Api {
8989
serial_configure,
9090
serial_get_info,
9191
serial_write,
92+
serial_read,
9293
time_get,
9394
time_set,
9495
configuration_get,
@@ -99,6 +100,14 @@ static API_CALLS: common::Api = common::Api {
99100
video_get_framebuffer,
100101
video_set_framebuffer,
101102
memory_get_region,
103+
video_mode_needs_vram,
104+
hid_get_event,
105+
hid_set_leds,
106+
video_wait_for_line,
107+
block_dev_get_info,
108+
block_write,
109+
block_read,
110+
block_verify,
102111
};
103112

104113
extern "C" {
@@ -336,6 +345,19 @@ pub extern "C" fn serial_write(
336345
common::Result::Err(common::Error::Unimplemented)
337346
}
338347

348+
/// Read bytes from a serial port. There is no sense of 'opening' or
349+
/// 'closing' the device - serial devices are always open. If the return value
350+
/// is `Ok(n)`, the value `n` may be less than the size of the given buffer.
351+
/// If so, that means not all of the data could be received - only the
352+
/// first `n` bytes were filled in.
353+
pub extern "C" fn serial_read(
354+
_device: u8,
355+
_data: common::ApiBuffer,
356+
_timeout: common::Option<common::Timeout>,
357+
) -> common::Result<usize> {
358+
common::Result::Err(common::Error::Unimplemented)
359+
}
360+
339361
/// Get the current wall time.
340362
///
341363
/// The Neotron BIOS does not understand time zones, leap-seconds or the
@@ -448,6 +470,13 @@ pub unsafe extern "C" fn video_set_framebuffer(_buffer: *const u8) -> common::Re
448470
common::Result::Err(common::Error::Unimplemented)
449471
}
450472

473+
/// Find out whether the given video mode needs more VRAM than we currently have.
474+
///
475+
/// The answer is no for any currently supported video mode (which is just the four text modes right now).
476+
pub extern "C" fn video_mode_needs_vram(_mode: common::video::Mode) -> bool {
477+
false
478+
}
479+
451480
/// Find out how large a given region of memory is.
452481
///
453482
/// The first region is the 'main application region' and is defined to always
@@ -480,6 +509,146 @@ pub extern "C" fn memory_get_region(region: u8) -> common::Result<common::Memory
480509
}
481510
}
482511

512+
/// Get the next available HID event, if any.
513+
///
514+
/// This function doesn't block. It will return `Ok(None)` if there is no event ready.
515+
pub extern "C" fn hid_get_event() -> common::Result<common::Option<common::hid::HidEvent>> {
516+
// TODO: Support some HID events
517+
common::Result::Ok(common::Option::None)
518+
}
519+
520+
/// Control the keyboard LEDs.
521+
pub extern "C" fn hid_set_leds(_leds: common::hid::KeyboardLeds) -> common::Result<()> {
522+
common::Result::Err(common::Error::Unimplemented)
523+
}
524+
525+
/// Wait for the next occurence of the specified video scan-line.
526+
///
527+
/// In general we must assume that the video memory is read top-to-bottom
528+
/// as the picture is being drawn on the monitor (e.g. via a VGA video
529+
/// signal). If you modify video memory during this *drawing period*
530+
/// there is a risk that the image on the monitor (however briefly) may
531+
/// contain some parts from before the modification and some parts from
532+
/// after. This can given rise to the *tearing effect* where it looks
533+
/// like the screen has been torn (or ripped) across because there is a
534+
/// discontinuity part-way through the image.
535+
///
536+
/// This function busy-waits until the video drawing has reached a
537+
/// specified scan-line on the video frame.
538+
///
539+
/// There is no error code here. If the line you ask for is beyond the
540+
/// number of visible scan-lines in the current video mode, it waits util
541+
/// the last visible scan-line is complete.
542+
///
543+
/// If you wait for the last visible line until drawing, you stand the
544+
/// best chance of your pixels operations on the video RAM being
545+
/// completed before scan-lines start being sent to the monitor for the
546+
/// next frame.
547+
///
548+
/// You can also use this for a crude `16.7 ms` delay but note that
549+
/// some video modes run at `70 Hz` and so this would then give you a
550+
/// `14.3ms` second delay.
551+
pub extern "C" fn video_wait_for_line(line: u16) {
552+
let desired_line = line.min(vga::get_num_scan_lines());
553+
loop {
554+
let current_line = vga::get_scan_line();
555+
if current_line == desired_line {
556+
break;
557+
}
558+
}
559+
}
560+
561+
/// Get information about the Block Devices in the system.
562+
///
563+
/// Block Devices are also known as *disk drives*. They can be read from
564+
/// (and often written to) but only in units called *blocks* or *sectors*.
565+
///
566+
/// The BIOS should enumerate removable devices first, followed by fixed
567+
/// devices.
568+
///
569+
/// The set of devices is not expected to change at run-time - removal of
570+
/// media is indicated with a boolean field in the
571+
/// `block_dev::DeviceInfo` structure.
572+
pub extern "C" fn block_dev_get_info(device: u8) -> common::Option<common::block_dev::DeviceInfo> {
573+
match device {
574+
0 => {
575+
common::Option::Some(common::block_dev::DeviceInfo {
576+
// This is the built-in SD card slot
577+
name: common::types::ApiString::new("SdCard0"),
578+
device_type: common::block_dev::DeviceType::SecureDigitalCard,
579+
// This is the standard for SD cards
580+
block_size: 512,
581+
// TODO: scan the card here
582+
num_blocks: 0,
583+
// No motorised eject
584+
ejectable: false,
585+
// But you can take the card out
586+
removable: true,
587+
// Pretend the card is out
588+
media_present: true,
589+
// Don't care about this value when card is out
590+
read_only: false,
591+
})
592+
}
593+
_ => {
594+
// Nothing else supported by this BIOS
595+
common::Option::None
596+
}
597+
}
598+
}
599+
600+
/// Write one or more sectors to a block device.
601+
///
602+
/// The function will block until all data is written. The array pointed
603+
/// to by `data` must be `num_blocks * block_size` in length, where
604+
/// `block_size` is given by `block_dev_get_info`.
605+
///
606+
/// There are no requirements on the alignment of `data` but if it is
607+
/// aligned, the BIOS may be able to use a higher-performance code path.
608+
pub extern "C" fn block_write(
609+
_device: u8,
610+
_block: u64,
611+
_num_blocks: u8,
612+
_data: common::ApiByteSlice,
613+
) -> common::Result<()> {
614+
common::Result::Err(common::Error::Unimplemented)
615+
}
616+
617+
/// Read one or more sectors to a block device.
618+
///
619+
/// The function will block until all data is read. The array pointed
620+
/// to by `data` must be `num_blocks * block_size` in length, where
621+
/// `block_size` is given by `block_dev_get_info`.
622+
///
623+
/// There are no requirements on the alignment of `data` but if it is
624+
/// aligned, the BIOS may be able to use a higher-performance code path.
625+
pub extern "C" fn block_read(
626+
_device: u8,
627+
_block: u64,
628+
_num_blocks: u8,
629+
_data: common::ApiBuffer,
630+
) -> common::Result<()> {
631+
common::Result::Err(common::Error::Unimplemented)
632+
}
633+
634+
/// Verify one or more sectors on a block device (that is read them and
635+
/// check they match the given data).
636+
///
637+
/// The function will block until all data is verified. The array pointed
638+
/// to by `data` must be `num_blocks * block_size` in length, where
639+
/// `block_size` is given by `block_dev_get_info`.
640+
///
641+
/// There are no requirements on the alignment of `data` but if it is
642+
/// aligned, the BIOS may be able to use a higher-performance code path.
643+
pub extern "C" fn block_verify(
644+
_device: u8,
645+
_block: u64,
646+
_num_blocks: u8,
647+
_data: common::ApiByteSlice,
648+
) -> common::Result<()> {
649+
common::Result::Err(common::Error::Unimplemented)
650+
}
651+
483652
/// Called when DMA raises IRQ0; i.e. when a DMA transfer to the pixel FIFO or
484653
/// the timing FIFO has completed.
485654
#[interrupt]

src/vga/mod.rs

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -204,56 +204,6 @@ static mut VIDEO_MODE: crate::common::video::Mode = crate::common::video::Mode::
204204
crate::common::video::Format::Text8x16,
205205
);
206206

207-
/// Gets the current video mode
208-
pub fn get_video_mode() -> crate::common::video::Mode {
209-
unsafe { VIDEO_MODE }
210-
}
211-
212-
/// Sets the current video mode
213-
pub fn set_video_mode(mode: crate::common::video::Mode) -> bool {
214-
cortex_m::interrupt::disable();
215-
let mode_ok = match (
216-
mode.timing(),
217-
mode.format(),
218-
mode.is_horiz_2x(),
219-
mode.is_vert_2x(),
220-
) {
221-
(
222-
crate::common::video::Timing::T640x480,
223-
crate::common::video::Format::Text8x16 | crate::common::video::Format::Text8x8,
224-
false,
225-
false,
226-
) => {
227-
unsafe {
228-
VIDEO_MODE = mode;
229-
TIMING_BUFFER = TimingBuffer::make_640x480();
230-
}
231-
true
232-
}
233-
(
234-
crate::common::video::Timing::T640x400,
235-
crate::common::video::Format::Text8x16 | crate::common::video::Format::Text8x8,
236-
false,
237-
false,
238-
) => {
239-
unsafe {
240-
VIDEO_MODE = mode;
241-
TIMING_BUFFER = TimingBuffer::make_640x400();
242-
}
243-
true
244-
}
245-
_ => false,
246-
};
247-
if mode_ok {
248-
NUM_TEXT_COLS.store(mode.text_width().unwrap_or(0) as usize, Ordering::SeqCst);
249-
NUM_TEXT_ROWS.store(mode.text_height().unwrap_or(0) as usize, Ordering::SeqCst);
250-
}
251-
unsafe {
252-
cortex_m::interrupt::enable();
253-
}
254-
mode_ok
255-
}
256-
257207
/// Tracks which scan-line we are currently on (for timing purposes => it goes 0..`TIMING_BUFFER.back_porch_ends_at`)
258208
static CURRENT_TIMING_LINE: AtomicU16 = AtomicU16::new(0);
259209

@@ -678,6 +628,67 @@ fn multicore_launch_core1_with_stack(
678628
debug!("Core 1 started!!");
679629
}
680630

631+
/// Gets the current video mode
632+
pub fn get_video_mode() -> crate::common::video::Mode {
633+
unsafe { VIDEO_MODE }
634+
}
635+
636+
/// Sets the current video mode
637+
pub fn set_video_mode(mode: crate::common::video::Mode) -> bool {
638+
cortex_m::interrupt::disable();
639+
let mode_ok = match (
640+
mode.timing(),
641+
mode.format(),
642+
mode.is_horiz_2x(),
643+
mode.is_vert_2x(),
644+
) {
645+
(
646+
crate::common::video::Timing::T640x480,
647+
crate::common::video::Format::Text8x16 | crate::common::video::Format::Text8x8,
648+
false,
649+
false,
650+
) => {
651+
unsafe {
652+
VIDEO_MODE = mode;
653+
TIMING_BUFFER = TimingBuffer::make_640x480();
654+
}
655+
true
656+
}
657+
(
658+
crate::common::video::Timing::T640x400,
659+
crate::common::video::Format::Text8x16 | crate::common::video::Format::Text8x8,
660+
false,
661+
false,
662+
) => {
663+
unsafe {
664+
VIDEO_MODE = mode;
665+
TIMING_BUFFER = TimingBuffer::make_640x400();
666+
}
667+
true
668+
}
669+
_ => false,
670+
};
671+
if mode_ok {
672+
NUM_TEXT_COLS.store(mode.text_width().unwrap_or(0) as usize, Ordering::SeqCst);
673+
NUM_TEXT_ROWS.store(mode.text_height().unwrap_or(0) as usize, Ordering::SeqCst);
674+
}
675+
unsafe {
676+
cortex_m::interrupt::enable();
677+
}
678+
mode_ok
679+
}
680+
681+
/// Get the current scan line.
682+
pub fn get_scan_line() -> u16 {
683+
CURRENT_DISPLAY_LINE.load(Ordering::Relaxed)
684+
}
685+
686+
/// Get how many visible lines there currently are
687+
pub fn get_num_scan_lines() -> u16 {
688+
let mode = get_video_mode();
689+
mode.vertical_lines()
690+
}
691+
681692
/// This function runs the video processing loop on Core 1.
682693
///
683694
/// It keeps the odd/even scan-line buffers updated, as per the contents of

0 commit comments

Comments
 (0)