diff --git a/tetros/src/drivers/serial.rs b/tetros/src/drivers/serial.rs index 6e771ed..cb5bad5 100644 --- a/tetros/src/drivers/serial.rs +++ b/tetros/src/drivers/serial.rs @@ -1,9 +1,9 @@ -use core::arch::asm; - use lazy_static::lazy_static; use spin::Mutex; use uart_16550::SerialPort; +use crate::os::util::without_interrupts; + lazy_static! { pub static ref SERIAL1: Mutex = { let mut serial_port = unsafe { SerialPort::new(0x3F8) }; @@ -16,22 +16,14 @@ lazy_static! { pub fn _print(args: core::fmt::Arguments<'_>) { use core::fmt::Write; - // TODO: preserve previous interrupt state - // Disable interrupts to prevent deadlocks // (we might get an interrupt while printing) - unsafe { - asm!("cli", options(preserves_flags, nostack)); - } - - SERIAL1 - .lock() - .write_fmt(args) - .expect("Printing to serial failed"); - - unsafe { - asm!("sti", options(preserves_flags, nostack)); - } + without_interrupts(|| { + SERIAL1 + .lock() + .write_fmt(args) + .expect("Printing to serial failed"); + }) } /// Prints to the host through the serial interface. diff --git a/tetros/src/idt/stackframe.rs b/tetros/src/idt/stackframe.rs index e0c82e1..fb207b5 100644 --- a/tetros/src/idt/stackframe.rs +++ b/tetros/src/idt/stackframe.rs @@ -1,9 +1,9 @@ +use bitflags::bitflags; +use core::arch::asm; use core::{fmt, ops::Deref}; use super::VirtAddr; -use bitflags::bitflags; - bitflags! { /// The EFLAGS register. All bit patterns are valid representations for this type. /// @@ -82,6 +82,24 @@ bitflags! { } } +impl EFlags { + #[inline] + pub fn read() -> EFlags { + EFlags::from_bits_truncate(EFlags::read_raw()) + } + + #[inline] + fn read_raw() -> u32 { + let r: u32; + + unsafe { + asm!("pushfd; pop {0:e}", out(reg) r, options(nomem, preserves_flags)); + } + + r + } +} + /// Wrapper type for the interrupt stack frame pushed by the CPU. /// /// This type derefs to an [`InterruptStackFrameValue`], which allows reading the actual values. diff --git a/tetros/src/lib.rs b/tetros/src/lib.rs index 2017f8c..660421b 100644 --- a/tetros/src/lib.rs +++ b/tetros/src/lib.rs @@ -11,7 +11,10 @@ use spin::Mutex; use drivers::{pic::PICDriver, vga::Vga13h}; use idt::{InterruptDescriptorTable, InterruptStackFrame}; -use os::thunk::ThunkData; +use os::{ + thunk::ThunkData, + util::{sti, without_interrupts}, +}; use tetrisboard::{FallingTetromino, TetrisBoard}; mod idt; @@ -28,7 +31,7 @@ pub(crate) static PIC: Mutex = Mutex::new(PICDriver::new(PIC_OFFSET, pub(crate) static TICK_COUNTER: Mutex = Mutex::new(0); pub(crate) static BOARD: Mutex = Mutex::new(TetrisBoard::new()); pub(crate) static FALLING: Mutex> = Mutex::new(None); -pub(crate) static RUN_TICKS: Mutex = Mutex::new(false); +pub(crate) static LAST_INPUT: Mutex> = Mutex::new(None); lazy_static! { static ref RNG: Mutex = Mutex::new(SmallRng::seed_from_u64(1337)); @@ -85,9 +88,7 @@ enum InputKey { Down, } -extern "x86-interrupt" fn keyboard_handler(stack_frame: InterruptStackFrame) { - println!("KEYBOARD {:?}", stack_frame); - +extern "x86-interrupt" fn keyboard_handler(_stack_frame: InterruptStackFrame) { let scancode = unsafe { inb(0x60) }; println!("{:x?}", scancode); @@ -104,84 +105,13 @@ extern "x86-interrupt" fn keyboard_handler(stack_frame: InterruptStackFrame) { _ => None, }; - if let Some(fall) = &mut *FALLING.lock() { - let board = BOARD.lock(); - let mut fall_test = fall.clone(); - - match key { - Some(InputKey::Up) => { - fall_test.rotate_cw(); - if board.tetromino_valid(&fall_test) { - fall.rotate_cw() - }; - } - - Some(InputKey::Down) => { - fall_test.rotate_ccw(); - if board.tetromino_valid(&fall_test) { - fall.rotate_ccw() - }; - } - - Some(InputKey::Left) => { - fall_test.translate(-1, 0); - if board.tetromino_valid(&fall_test) { - fall.translate(-1, 0); - }; - } - - Some(InputKey::Right) => { - fall_test.translate(1, 0); - if board.tetromino_valid(&fall_test) { - fall.translate(1, 0); - }; - } - - _ => {} - } - } - + *LAST_INPUT.lock() = key; PIC.lock().send_eoi(InterruptIndex::Keyboard.as_u8()); } extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) { - if !*RUN_TICKS.lock() { - PIC.lock().send_eoi(InterruptIndex::Timer.as_u8()); - return; - } - - // Step tick counter - let t = { - let mut t = TICK_COUNTER.lock(); - *t = (*t).wrapping_add(1); - *t - }; - - let mut v = VGA.lock(); - let mut board = BOARD.lock(); - let mut fall = FALLING.lock(); - - v.swap(); - board.draw(&mut v, fall.as_ref()); - - if let Some(fall_inner) = fall.as_mut() { - if t % 5 == 0 { - let mut fall_test = fall_inner.clone(); - fall_test.translate(0, 1); - - if board.tetromino_valid(&fall_test) { - fall_inner.translate(0, 1); - } else { - let mut x = None; - core::mem::swap(&mut x, &mut fall); - board.place_tetromino(x.unwrap()); - board.collapse(); - } - } - } else { - *fall = Some(FallingTetromino::random(5, 1)) - } - + let mut t = TICK_COUNTER.lock(); + *t = (*t).wrapping_add(1); PIC.lock().send_eoi(InterruptIndex::Timer.as_u8()); } @@ -224,11 +154,96 @@ pub unsafe extern "C" fn start( pic.init(); } - *RUN_TICKS.lock() = true; + // We're ready for interrupts, + // enable them + sti(); - // Do-nothing loop, - // frames are driven by interrupts + let mut last_t = 0; loop { - asm!("hlt") + // All locks use `without_interrupts` + // to prevent deadlocks + let t = without_interrupts(|| { + let l = TICK_COUNTER.lock(); + let t = *l; + drop(l); + return t; + }); + + if t == last_t { + continue; + } + last_t = t; + + // Handle user input + without_interrupts(|| { + if let Some(fall) = &mut *FALLING.lock() { + let board = BOARD.lock(); + let mut fall_test = fall.clone(); + let mut last_input = LAST_INPUT.lock(); + + match *last_input { + Some(InputKey::Up) => { + fall_test.rotate_cw(); + if board.tetromino_valid(&fall_test) { + fall.rotate_cw() + }; + } + + Some(InputKey::Down) => { + fall_test.rotate_ccw(); + if board.tetromino_valid(&fall_test) { + fall.rotate_ccw() + }; + } + + Some(InputKey::Left) => { + fall_test.translate(-1, 0); + if board.tetromino_valid(&fall_test) { + fall.translate(-1, 0); + }; + } + + Some(InputKey::Right) => { + fall_test.translate(1, 0); + if board.tetromino_valid(&fall_test) { + fall.translate(1, 0); + }; + } + + _ => {} + } + + // Clear last input, it was handled. + *last_input = None; + } + }); + + // Update board + without_interrupts(|| { + let mut v = VGA.lock(); + let mut board = BOARD.lock(); + let mut fall = FALLING.lock(); + + v.swap(); + board.draw(&mut v, fall.as_ref()); + + if let Some(fall_inner) = fall.as_mut() { + if t % 5 == 0 { + let mut fall_test = fall_inner.clone(); + fall_test.translate(0, 1); + + if board.tetromino_valid(&fall_test) { + fall_inner.translate(0, 1); + } else { + let mut x = None; + core::mem::swap(&mut x, &mut fall); + board.place_tetromino(x.unwrap()); + board.collapse(); + } + } + } else { + *fall = Some(FallingTetromino::random(5, 1)) + } + }) } } diff --git a/tetros/src/os/mod.rs b/tetros/src/os/mod.rs index 99795d2..5b1be8e 100644 --- a/tetros/src/os/mod.rs +++ b/tetros/src/os/mod.rs @@ -1,4 +1,5 @@ pub mod thunk; +pub mod util; #[macro_use] pub mod panic; diff --git a/tetros/src/os/util.rs b/tetros/src/os/util.rs new file mode 100644 index 0000000..56cd62a --- /dev/null +++ b/tetros/src/os/util.rs @@ -0,0 +1,47 @@ +use core::arch::asm; + +use crate::idt::EFlags; + +/// Disable interrupts +#[inline] +pub fn cli() { + unsafe { + asm!("cli", options(preserves_flags, nostack)); + } +} + +/// Enable interrupts +#[inline] +pub fn sti() { + unsafe { + asm!("sti", options(preserves_flags, nostack)); + } +} + +/// Run a closure with disabled interrupts. +/// +/// Run the given closure, disabling interrupts before running it (if they aren't already disabled). +/// Afterwards, interrupts are enabling again if they were enabled before. +/// +/// If you have other `enable` and `disable` calls _within_ the closure, things may not work as expected. +#[inline] +pub fn without_interrupts(f: F) -> R +where + F: FnOnce() -> R, +{ + // true if the interrupt flag is set (i.e. interrupts are enabled) + let saved_intpt_flag = EFlags::read().contains(EFlags::INTERRUPT_FLAG); + + // if interrupts are enabled, disable them for now + if saved_intpt_flag { + cli(); + } + + let ret = f(); + + if saved_intpt_flag { + sti(); + } + + ret +}