diff --git a/Cargo.lock b/Cargo.lock index 864aade..d040d9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -412,6 +412,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "grounded" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917d82402c7eb9755fdd87d52117701dae9e413a6abb309fac2a13af693b6080" +dependencies = [ + "portable-atomic", +] + [[package]] name = "hash32" version = "0.2.1" @@ -629,7 +638,6 @@ checksum = "d37886e73d87732421aaf5da617eead9d69a7daf6b0d059780f76157d9ce5372" name = "neotron-pico-bios" version = "0.7.0" dependencies = [ - "atomic-polyfill", "chrono", "cortex-m", "cortex-m-rt", @@ -640,6 +648,7 @@ dependencies = [ "embedded-hal 0.2.7", "embedded-sdmmc", "fugit", + "grounded", "heapless 0.7.17", "mcp794xx", "neotron-bmc-commands", @@ -649,6 +658,7 @@ dependencies = [ "pc-keyboard", "pio", "pio-proc", + "portable-atomic", "rp2040-boot2", "rp2040-hal", "shared-bus", @@ -802,9 +812,12 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +dependencies = [ + "critical-section", +] [[package]] name = "precomputed-hash" diff --git a/Cargo.toml b/Cargo.toml index dbe30d3..2470a50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,56 +8,31 @@ name = "neotron-pico-bios" version = "0.7.0" [dependencies] -# Useful Cortex-M specific functions (e.g. SysTick) -cortex-m = {version = "0.7", features = ["inline-asm"]} -# The Raspberry Pi RP2040 HAL (so we can turn defmt on) -rp2040-hal = { version = "0.10", features = [ "defmt", "rt", "critical-section-impl", "rom-func-cache" ] } -# Cortex-M run-time (or start-up) code -cortex-m-rt = "0.7" -# The BIOS to OS API -neotron-common-bios = "0.12.0" -# For the RP2040 bootloader -rp2040-boot2 = "0.3.0" -# For hardware abstraction traits -embedded-hal = "0.2" -# Gives us formatted PC-side logging -defmt = "=0.3.2" -# Sends defmt logs to the SWD debugger -defmt-rtt = "0.4" -# Send panics to the debugger -panic-probe = "0.2" -# RP2040 PIO assembler -pio = "0.2.1" -# Macros for RP2040 PIO assembler -pio-proc = "0.2" -# Hardware locks for sharing data with interrupts -critical-section = "1.0" -# Commands for talking to a Neotron BMC. The tag is for the repo as a whole, of which the commands crate is a small part. -neotron-bmc-commands = { version = "0.2.0" } -# Protocol for talking to a Neotron BMC. The tag is for the repo as a whole, of which the protocol crate is a small part. -neotron-bmc-protocol = { version = "0.1.0", features = ["defmt"] } -# Time and frequency related functions -fugit = "0.3" -# PS/2 scancode decoding -pc-keyboard = "0.7.0" -# Useful queues and other structures -heapless = "0.7" -# The Dallas DS1307 RTC driver -ds1307 = "0.5.0" -# The Microchip MCP7940x RTC driver -mcp794xx = "0.3.0" -# I2C Bus Sharing -shared-bus = "0.3" -# Gets us compare-swap atomic operations -atomic-polyfill = "1.0.2" -# SD Card driver -embedded-sdmmc = { version = "0.6", default-features = false, features = [ - "defmt-log" -] } -# CODEC register control -tlv320aic23 = "0.1.0" -# Calendar/Date/Time functions and types -chrono = { version = "0.4.38", default-features = false } +chrono = { version = "0.4.38", default-features = false } # Calendar/Date/Time functions and types +cortex-m = {version = "0.7", features = ["inline-asm"]} # Useful Cortex-M specific functions (e.g. SysTick) +cortex-m-rt = "0.7" # Cortex-M run-time (or start-up) code +critical-section = "1.0" # Hardware locks for sharing data with interrupts +defmt = "=0.3.2" # Gives us formatted PC-side logging +defmt-rtt = "0.4" # Sends defmt logs to the SWD debugger +ds1307 = "0.5.0" # The Dallas DS1307 RTC driver +embedded-hal = "0.2" # For hardware abstraction traits +embedded-sdmmc = { version = "0.6", default-features = false, features = [ "defmt-log" ] } # SD Card driver +fugit = "0.3" # Time and frequency related functions +grounded = { version = "0.2.0", features = ["critical-section"] } +heapless = "0.7" # Useful queues and other structures +mcp794xx = "0.3.0" # The Microchip MCP7940x RTC driver +neotron-bmc-commands = { version = "0.2.0" } # Commands for talking to a Neotron BMC. The tag is for the repo as a whole, of which the commands crate is a small part. +neotron-bmc-protocol = { version = "0.1.0", features = ["defmt"] } # Protocol for talking to a Neotron BMC. The tag is for the repo as a whole, of which the protocol crate is a small part. +neotron-common-bios = "0.12.0" # The BIOS to OS API +panic-probe = "0.2" # Send panics to the debugger +pc-keyboard = "0.7.0" # PS/2 scancode decoding +pio = "0.2.1" # RP2040 PIO assembler +pio-proc = "0.2" # Macros for RP2040 PIO assembler +portable-atomic = { version = "1.10.0", features = ["critical-section"] } # Atomic CAS for non-CAS CPUs +rp2040-boot2 = "0.3.0" # For the RP2040 bootloader +rp2040-hal = { version = "0.10", features = [ "defmt", "rt", "critical-section-impl", "rom-func-cache" ] } # The Raspberry Pi RP2040 HAL (so we can turn defmt on) +shared-bus = "0.3" # I2C Bus Sharing +tlv320aic23 = "0.1.0" # CODEC register control [build-dependencies] neotron-common-bios = "0.12.0" diff --git a/memory.x b/memory.x index 615d4b9..eba2c7a 100644 --- a/memory.x +++ b/memory.x @@ -25,7 +25,7 @@ MEMORY { /* * This is the bottom of the four striped banks of SRAM in the RP2040. */ - RAM_OS : ORIGIN = 0x20000000, LENGTH = 0x42000 - 0x9680 + RAM_OS : ORIGIN = 0x20000000, LENGTH = 0x42000 - 0x9630 /* * This is the top of the four striped banks of SRAM in the RP2040, plus * SRAM_BANK4 and SRAM_BANK5. @@ -33,10 +33,10 @@ MEMORY { * This is carefully calculated to give us 8 KiB of stack space and ensure * the defmt buffer doesn't span across SRAM_BANK3 and SRAM_BANK4. * - * 0x9680 should be the (size of .data + size of .bss + size of .uninit + + * 0x9630 should be the (size of .data + size of .bss + size of .uninit + * 0x2000 for the stack). */ - RAM : ORIGIN = 0x20042000 - 0x9680, LENGTH = 0x9680 + RAM : ORIGIN = 0x20042000 - 0x9630, LENGTH = 0x9630 } /* diff --git a/src/i2s.rs b/src/i2s.rs index da00026..cb9b481 100644 --- a/src/i2s.rs +++ b/src/i2s.rs @@ -1,9 +1,13 @@ //! Code to set up a PIO to generate I2S Audio +use core::cell::UnsafeCell; + +use grounded::uninit::GroundedCell; + use crate::hal::{pac::interrupt, prelude::*}; /// Holds the objects we need to read from the RAM fifo and write to the PIO hardware FIFO. -static mut PLAYBACK_TO_PIO: Option = None; +static PLAYBACK_TO_PIO: GroundedCell = GroundedCell::uninit(); /// The reader end of the RAM FIFO and the writer end of the PIO hardware FIFO. struct PlaybackToPio { @@ -11,6 +15,8 @@ struct PlaybackToPio { ram_fifo: heapless::spsc::Consumer<'static, u32, 1024>, } +unsafe impl Sync for PlaybackToPio {} + /// Used to 'play' samples by writing them to the RAM FIFO. /// /// Holds the writer end of the RAM FIFO. @@ -181,17 +187,21 @@ pub fn init(pio: super::pac::PIO1, resets: &mut super::pac::RESETS) -> Player { let _running_sam = samples_sm.start(); - static mut SAMPLE_QUEUE: heapless::spsc::Queue = heapless::spsc::Queue::new(); + static SAMPLE_QUEUE: QueueWrapper = QueueWrapper::new(); + // # Safety + // Interrupts are disabled at this point, so we can split the queue. let (q_producer, q_consumer) = unsafe { SAMPLE_QUEUE.split() }; pio_tx_fifo.enable_tx_not_full_interrupt(crate::hal::pio::PioIRQ::Irq0); - critical_section::with(|_| unsafe { - PLAYBACK_TO_PIO.replace(PlaybackToPio { + // # Safety + // the PIO interrupt is currently disabled, so this is OK + unsafe { + PLAYBACK_TO_PIO.get().write(PlaybackToPio { pio_fifo: pio_tx_fifo, ram_fifo: q_consumer, }); - }); + } unsafe { cortex_m::peripheral::NVIC::unmask(crate::pac::Interrupt::PIO1_IRQ_0); @@ -200,6 +210,28 @@ pub fn init(pio: super::pac::PIO1, resets: &mut super::pac::RESETS) -> Player { Player { fifo: q_producer } } +struct QueueWrapper(UnsafeCell>); + +impl QueueWrapper { + const fn new() -> QueueWrapper { + QueueWrapper(UnsafeCell::new(heapless::spsc::Queue::new())) + } + + /// Only call this when interrupts are off and the queue isn't being used + unsafe fn split( + &self, + ) -> ( + heapless::spsc::Producer<'_, u32, 1024>, + heapless::spsc::Consumer<'_, u32, 1024>, + ) { + let ptr = self.0.get(); + let queue_ref = unsafe { &mut *ptr }; + queue_ref.split() + } +} + +unsafe impl Sync for QueueWrapper {} + /// Called when the PIO1 IRQ fires. /// /// We mapped this to "TX FIFO is not empty", so this interrupt will be @@ -209,9 +241,7 @@ fn PIO1_IRQ_0() { // This is the only function (apart from `init`) which accesses this // variable, and `init()` is sure to disable interrupts until the global is // set up, so this is safe. - let Some(fifo) = (unsafe { PLAYBACK_TO_PIO.as_mut() }) else { - return; - }; + let fifo = unsafe { &mut *PLAYBACK_TO_PIO.get() }; // Read from fifo.ram_fifo if let Some(sample) = fifo.ram_fifo.dequeue() { // .. and write to fifo.pio_fifo diff --git a/src/main.rs b/src/main.rs index 63c715d..8f3f6be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -742,9 +742,13 @@ fn paint_stacks() { *b = CORE0_STACK_PAINT_WORD; } - info!("Painting Core 1 stack: {:?}", CORE1_STACK.as_ptr_range()); - for b in CORE1_STACK.iter_mut() { - *b = CORE1_STACK_PAINT_WORD; + let stack_start = addr_of_mut!(CORE1_STACK) as *mut usize; + let stack_end = addr_of_mut!(CORE1_STACK).add(1) as *mut usize; + info!("Painting Core 1 stack @ {:?}", stack_start..stack_end); + let mut p = stack_start; + while p != stack_end { + p.write_volatile(CORE1_STACK_PAINT_WORD); + p = p.add(1); } } } @@ -764,17 +768,12 @@ fn check_stacks() { static mut __sheap: usize; static mut _stack_start: usize; } - let stack_len = unsafe { (addr_of!(_stack_start) as usize) - (addr_of!(__sheap) as usize) }; - check_stack( - unsafe { addr_of!(__sheap) }, - stack_len, - CORE0_STACK_PAINT_WORD, - ); - check_stack( - unsafe { CORE1_STACK.as_ptr() }, - unsafe { CORE1_STACK.len() * core::mem::size_of::() }, - CORE1_STACK_PAINT_WORD, - ); + let stack_len = (addr_of!(_stack_start) as usize) - (addr_of!(__sheap) as usize); + check_stack(addr_of!(__sheap), stack_len, CORE0_STACK_PAINT_WORD); + let stack_start = addr_of!(CORE1_STACK) as *const usize; + let stack_end = unsafe { addr_of!(CORE1_STACK).add(1) } as *const usize; + let stack_len = unsafe { stack_end.offset_from(stack_start) } as usize; + check_stack(stack_start, stack_len, CORE1_STACK_PAINT_WORD); } /// Dummy stack checker that does nothing @@ -1915,8 +1914,8 @@ pub extern "C" fn memory_get_region(region: u8) -> FfiOption { // Application Region FfiOption::Some(MemoryRegion { - start: unsafe { addr_of!(_ram_os_start) } as *mut u8, - length: unsafe { addr_of!(_ram_os_len) } as usize, + start: addr_of!(_ram_os_start) as *mut u8, + length: addr_of!(_ram_os_len) as usize, kind: common::MemoryKind::Ram.into(), }) } @@ -2663,17 +2662,7 @@ extern "C" fn compare_and_swap_bool(value: &AtomicBool, old_value: bool, new_val /// This should only be when the MCP23S17 has driven our IRQ pin low. #[interrupt] fn IO_IRQ_BANK0() { - // The `#[interrupt]` attribute covertly converts this to `&'static mut - // Option` - static mut LOCAL_IRQ_PIN: Option = None; - - // This is one-time lazy initialisation. We steal the variables given to us - // via `IRQ_PIN`. - if LOCAL_IRQ_PIN.is_none() { - let mut lock = IRQ_PIN.lock(); - *LOCAL_IRQ_PIN = lock.take(); - } - if let Some(pin) = LOCAL_IRQ_PIN { + if let Some(pin) = IRQ_PIN.lock().as_mut() { let is_low = pin.is_low().unwrap(); INTERRUPT_PENDING.store(is_low, Ordering::Relaxed); pin.clear_interrupt(hal::gpio::Interrupt::EdgeLow); diff --git a/src/mutex/mod.rs b/src/mutex/mod.rs index c6347dc..4da28d3 100644 --- a/src/mutex/mod.rs +++ b/src/mutex/mod.rs @@ -3,14 +3,14 @@ //! Unlock the cortex-m mutex, it panics on collision, rather than disabling //! interrupts while the lock is held. -use atomic_polyfill::{AtomicBool, Ordering}; +use portable_atomic::{AtomicBool, Ordering}; /// A simple no-std mutex. /// /// Uses critical-section to hold an atomic bool, for when you don't have /// atomic-compare-swap. pub struct NeoMutex { - locked: atomic_polyfill::AtomicBool, + locked: AtomicBool, value: core::cell::UnsafeCell, } @@ -48,13 +48,13 @@ pub struct NeoMutexGuard<'a, T> { parent: &'a NeoMutex, } -impl<'a, T> Drop for NeoMutexGuard<'a, T> { +impl Drop for NeoMutexGuard<'_, T> { fn drop(&mut self) { self.parent.locked.store(false, Ordering::Release); } } -impl<'a, T> core::ops::Deref for NeoMutexGuard<'a, T> { +impl core::ops::Deref for NeoMutexGuard<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -62,7 +62,7 @@ impl<'a, T> core::ops::Deref for NeoMutexGuard<'a, T> { } } -impl<'a, T> core::ops::DerefMut for NeoMutexGuard<'a, T> { +impl core::ops::DerefMut for NeoMutexGuard<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *self.parent.value.get() } } diff --git a/src/sdcard.rs b/src/sdcard.rs index 785391f..9a2bdfb 100644 --- a/src/sdcard.rs +++ b/src/sdcard.rs @@ -27,7 +27,7 @@ // Imports // ----------------------------------------------------------------------------- -use atomic_polyfill::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use super::Hardware; @@ -38,7 +38,7 @@ use super::Hardware; /// A type that `embedded-sdmmc` can use to talk over our SPI bus. pub(crate) struct FakeSpi<'a>(pub(crate) &'a mut Hardware, pub bool); -impl<'a> embedded_hal::blocking::spi::Transfer for FakeSpi<'a> { +impl embedded_hal::blocking::spi::Transfer for FakeSpi<'_> { type Error = core::convert::Infallible; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { @@ -68,7 +68,7 @@ impl<'a> embedded_hal::blocking::spi::Transfer for FakeSpi<'a> { } } -impl<'a> embedded_hal::blocking::spi::Write for FakeSpi<'a> { +impl embedded_hal::blocking::spi::Write for FakeSpi<'_> { type Error = core::convert::Infallible; fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { diff --git a/src/vga/mod.rs b/src/vga/mod.rs index f24b6fe..4e864aa 100644 --- a/src/vga/mod.rs +++ b/src/vga/mod.rs @@ -42,13 +42,14 @@ mod rgb; use crate::hal::{self, pac::interrupt, pio::PIOExt}; use core::{ - cell::{RefCell, UnsafeCell}, + cell::UnsafeCell, ptr::addr_of_mut, sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}, }; use defmt::{debug, trace}; use neotron_common_bios::video::{Attr, GlyphAttr, TextBackgroundColour, TextForegroundColour}; +use core::sync::atomic::{AtomicU8, AtomicUsize}; pub use rgb::{RGBColour, RGBPair}; // ----------------------------------------------------------------------------- @@ -70,7 +71,8 @@ pub struct ModeInfo { /// Holds a video mode and a pointer, but as an atomic value suitable for use in /// a `static`. pub struct VideoMode { - inner: critical_section::Mutex>, + raw_mode: AtomicU8, + raw_ptr: AtomicUsize, } unsafe impl Sync for VideoMode {} @@ -78,14 +80,14 @@ unsafe impl Sync for VideoMode {} impl VideoMode { /// Construct a new [`NewModeWrapper`] with the given mode. const fn new() -> VideoMode { + let mode = neotron_common_bios::video::Mode::new( + neotron_common_bios::video::Timing::T640x480, + neotron_common_bios::video::Format::Text8x16, + ); + let ptr = 0; VideoMode { - inner: critical_section::Mutex::new(RefCell::new(ModeInfo { - mode: neotron_common_bios::video::Mode::new( - neotron_common_bios::video::Timing::T640x480, - neotron_common_bios::video::Format::Text8x16, - ), - ptr: core::ptr::null_mut(), - })), + raw_mode: AtomicU8::new(mode.as_u8()), + raw_ptr: AtomicUsize::new(ptr), } } @@ -94,17 +96,19 @@ impl VideoMode { if modeinfo.ptr.is_null() { modeinfo.ptr = GLYPH_ATTR_ARRAY.as_ptr() as *mut u32; } - critical_section::with(|cs| { - self.inner.replace(cs, modeinfo); - }) + self.raw_mode + .store(modeinfo.mode.as_u8(), Ordering::Relaxed); + self.raw_ptr.store(modeinfo.ptr as usize, Ordering::Relaxed); } /// Get the current video mode. pub fn get_mode(&self) -> ModeInfo { - let mut modeinfo = critical_section::with(|cs| { - let info = self.inner.borrow_ref(cs); - *info - }); + let raw_mode = self.raw_mode.load(Ordering::Relaxed); + let raw_ptr = self.raw_ptr.load(Ordering::Relaxed); + let mut modeinfo = ModeInfo { + mode: unsafe { neotron_common_bios::video::Mode::from_u8(raw_mode) }, + ptr: raw_ptr as *mut u32, + }; if modeinfo.ptr.is_null() { modeinfo.ptr = GLYPH_ATTR_ARRAY.as_ptr() as *mut u32; } @@ -175,20 +179,19 @@ impl RenderEngine { self.frame_count += 1; // Update video mode only on first line of video - let modeinfo = VIDEO_MODE.get_mode(); - self.current_video_ptr = modeinfo.ptr; - self.current_video_mode = modeinfo.mode; - match self.current_video_mode.timing() { - neotron_common_bios::video::Timing::T640x400 => unsafe { - TIMING_BUFFER = TimingBuffer::make_640x400(); - }, - neotron_common_bios::video::Timing::T640x480 => unsafe { - TIMING_BUFFER = TimingBuffer::make_640x480(); - }, - neotron_common_bios::video::Timing::T800x600 => { - panic!("Can't do 800x600"); + loop { + let modeinfo = VIDEO_MODE.get_mode(); + self.current_video_ptr = modeinfo.ptr; + self.current_video_mode = modeinfo.mode; + let modeinfo2 = VIDEO_MODE.get_mode(); + // make sure we get these two as a pair and they can't update one without the other + if modeinfo.mode == modeinfo2.mode { + break; } } + // Tell the ISR to now generate our newly chosen timing + CURRENT_TIMING_MODE.store(self.current_video_mode.timing() as usize, Ordering::Relaxed); + // set up our text console to be the right size self.num_text_cols = self.current_video_mode.text_width().unwrap_or(0) as usize; self.num_text_rows = self.current_video_mode.text_height().unwrap_or(0) as usize; } @@ -1629,10 +1632,17 @@ static VIDEO_PALETTE: [AtomicU16; 256] = [ /// This is to make more efficient use of DMA and FIFO resources. const MAX_NUM_PIXEL_PAIRS_PER_LINE: usize = MAX_NUM_PIXELS_PER_LINE / 2; -/// Stores our timing data which we DMA into the timing PIO State Machine. +/// Stores timing data which we DMA into the timing PIO State Machine. +/// +/// Must match the `Timing` enum, except we don't support 800x600. +#[link_section = ".data"] +static TIMING_BUFFER: [TimingBuffer; 2] = + [TimingBuffer::make_640x480(), TimingBuffer::make_640x400()]; + +/// Tracks which timing mode we use /// -/// Default value must match [`RenderEngine::new`] -static mut TIMING_BUFFER: TimingBuffer = TimingBuffer::make_640x480(); +/// Ensure this matches the default chosen in [`RenderEngine::new()`] +static CURRENT_TIMING_MODE: AtomicUsize = AtomicUsize::new(0); /// Tracks which scan-line will be shown next. /// @@ -1831,15 +1841,16 @@ pub fn init( w.sniff_en().clear_bit(); w }); + let timing_buffer = &TIMING_BUFFER[0]; dma.ch(TIMING_DMA_CHAN) .ch_read_addr() - .write(|w| unsafe { w.bits(TIMING_BUFFER.visible_line.data.as_ptr() as usize as u32) }); + .write(|w| unsafe { w.bits(&raw const timing_buffer.visible_line.data as u32) }); dma.ch(TIMING_DMA_CHAN) .ch_write_addr() .write(|w| unsafe { w.bits(timing_fifo.fifo_address() as usize as u32) }); dma.ch(TIMING_DMA_CHAN) .ch_trans_count() - .write(|w| unsafe { w.bits(TIMING_BUFFER.visible_line.data.len() as u32) }); + .write(|w| unsafe { w.bits(timing_buffer.visible_line.data.len() as u32) }); // Read from the pixel buffer (even first) and write to the pixel FIFO dma.ch(PIXEL_DMA_CHAN).ch_ctrl_trig().write(|w| { @@ -1881,26 +1892,18 @@ pub fn init( // cannot be reconfigured at a later time, but they do keep on running // as-is. - debug!( - "Core 1 stack: {:08x}, {} bytes", - unsafe { super::CORE1_STACK.as_ptr() }, - unsafe { super::CORE1_STACK.len() * core::mem::size_of::() } - ); + let stack_start = addr_of_mut!(super::CORE1_STACK); + let stack_end = unsafe { stack_start.add(1) }; + let stack_start = stack_start as *mut usize; + let stack_end = stack_end as *mut usize; + let stack_len = unsafe { stack_end.offset_from(stack_start) } as usize; + debug!("Core 1 stack @ {:?}", stack_start..stack_end); // No-one else is looking at this right now. TEXT_COLOUR_LOOKUP.init(&VIDEO_PALETTE); CHUNKY4_COLOUR_LOOKUP.init(&VIDEO_PALETTE); - unsafe { - crate::multicore::launch_core1_with_stack( - core1_main, - addr_of_mut!(super::CORE1_STACK) as *mut usize, - super::CORE1_STACK.len(), - ppb, - fifo, - psm, - ); - } + crate::multicore::launch_core1_with_stack(core1_main, stack_start, stack_len, ppb, fifo, psm); debug!("Core 1 running"); } @@ -2070,10 +2073,13 @@ unsafe fn PIO0_IRQ_1() { // Clear the interrupt pio.irq().write_with_zero(|w| w.irq().bits(1 << 1)); + // Current mode + let current_mode = CURRENT_TIMING_MODE.load(Ordering::Relaxed); + let timing_data = &TIMING_BUFFER[current_mode]; // This is now the line we are currently playing let current_timing_line = NEXT_SCAN_LINE.load(Ordering::Relaxed); // This is the line we should cue up to play next - let next_timing_line = if current_timing_line == TIMING_BUFFER.back_porch_ends_at { + let next_timing_line = if current_timing_line == timing_data.back_porch_ends_at { // Wrap around 0 } else { @@ -2084,7 +2090,7 @@ unsafe fn PIO0_IRQ_1() { // Are we in the visible portion *right* now? If so, copy some pixels into // the Pixel SM FIFO using DMA. Hopefully the main thread has them ready for // us (though we're playing them, ready or not). - if current_timing_line <= TIMING_BUFFER.visible_lines_ends_at { + if current_timing_line <= timing_data.visible_lines_ends_at { if (current_timing_line & 1) == 1 { // Load the odd line into the Pixel SM FIFO for immediate playback dma.ch(PIXEL_DMA_CHAN) @@ -2106,26 +2112,26 @@ unsafe fn PIO0_IRQ_1() { // Work out what sort of sync pulses we need on the *next* scan-line, and // also tell the main thread what to draw ready for the *next* scan-line. - let buffer = if next_timing_line <= TIMING_BUFFER.visible_lines_ends_at { + let buffer = if next_timing_line <= timing_data.visible_lines_ends_at { // A visible line is *up next* so start drawing it *right now*. DRAW_THIS_LINE.store(true, Ordering::Release); - &TIMING_BUFFER.visible_line - } else if next_timing_line <= TIMING_BUFFER.front_porch_end_at { + &raw const timing_data.visible_line + } else if next_timing_line <= timing_data.front_porch_end_at { // VGA front porch before VGA sync pulse - &TIMING_BUFFER.vblank_porch_buffer - } else if next_timing_line <= TIMING_BUFFER.sync_pulse_ends_at { + &raw const timing_data.vblank_porch_buffer + } else if next_timing_line <= timing_data.sync_pulse_ends_at { // Sync pulse - &TIMING_BUFFER.vblank_sync_buffer + &raw const timing_data.vblank_sync_buffer } else { // VGA back porch following VGA sync pulse. - &TIMING_BUFFER.vblank_porch_buffer + &raw const timing_data.vblank_porch_buffer }; // Start transferring the next block of timing info into the FIFO, ready for // the next line. We will be back in this interrupt once it starts actually // playing. dma.ch(TIMING_DMA_CHAN) .ch_al3_read_addr_trig() - .write(|w| w.bits(buffer as *const _ as usize as u32)); + .write(|w| w.bits(buffer as u32)); } // -----------------------------------------------------------------------------