From 57a634d4ee2d8504ba2562685d00441a38d37a74 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 1 Mar 2025 20:09:44 -0800 Subject: [PATCH] Controls and collisions --- README.md | 9 ++-- tetros/src/lib.rs | 85 ++++++++++++++++++++++++++++++- tetros/src/tetrisboard/falling.rs | 1 + tetros/src/tetrisboard/mod.rs | 6 ++- 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index eadde6c..ee73498 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -- improve collisions -- controls (input) - clear lines -- deadlocks +- deadlocks? +- fix arrow keys +- better rng +- keypress on start - enum and consts for interrupt index - fix asm loader +- minimal interrupts +- quick drop - document everything diff --git a/tetros/src/lib.rs b/tetros/src/lib.rs index 9a2d620..c8cfcd1 100644 --- a/tetros/src/lib.rs +++ b/tetros/src/lib.rs @@ -38,6 +38,7 @@ lazy_static! { idt.divide_error.set_handler_fn(divide_handler); idt.double_fault.set_handler_fn(double_fault_handler); idt.interrupts[InterruptIndex::Timer.as_idx()].set_handler_fn(timer_handler); + idt.interrupts[InterruptIndex::Keyboard.as_idx()].set_handler_fn(keyboard_handler); idt }; @@ -64,6 +65,85 @@ extern "x86-interrupt" fn divide_handler(stack_frame: InterruptStackFrame) { println!("DIVIDE ERROR {:?}", stack_frame); } +unsafe fn inb(port: u32) -> u8 { + let mut out; + + asm!( + "in al, dx", + out("al") out, + in("dx") port, + ); + + return out; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum InputKey { + Left, + Right, + Up, + Down, +} + +extern "x86-interrupt" fn keyboard_handler(stack_frame: InterruptStackFrame) { + println!("KEYBOARD {:?}", stack_frame); + + let scancode = unsafe { inb(0x60) }; + println!("{:x?}", scancode); + + let key = match scancode { + 0x11 => Some(InputKey::Up), + 0x1E => Some(InputKey::Left), + 0x1F => Some(InputKey::Down), + 0x20 => Some(InputKey::Right), + 0xE0 => { + let scancode = unsafe { inb(0x60) }; + println!("e {:x?}", scancode); + None + } + _ => 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); + }; + } + + _ => {} + } + } + + 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()); @@ -86,7 +166,10 @@ extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) { if let Some(fall_inner) = fall.as_mut() { if t % 6 == 0 { - if board.tetromino_free_below(fall_inner) { + 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; diff --git a/tetros/src/tetrisboard/falling.rs b/tetros/src/tetrisboard/falling.rs index 58e5ffb..ef25a8b 100644 --- a/tetros/src/tetrisboard/falling.rs +++ b/tetros/src/tetrisboard/falling.rs @@ -72,6 +72,7 @@ impl Direction { } } +#[derive(Debug, Clone)] pub struct FallingTetromino { tetromino: Tetromino, direction: Direction, diff --git a/tetros/src/tetrisboard/mod.rs b/tetros/src/tetrisboard/mod.rs index fec2b23..01c9d33 100644 --- a/tetros/src/tetrisboard/mod.rs +++ b/tetros/src/tetrisboard/mod.rs @@ -39,9 +39,11 @@ impl TetrisBoard { } } - pub fn tetromino_free_below(&self, tetromino: &FallingTetromino) -> bool { + /// Returns `false` if the given tetromino intersects a filled cell + /// or exits the board boundary + pub fn tetromino_valid(&self, tetromino: &FallingTetromino) -> bool { for (x, y) in tetromino.tiles() { - let cell = self.get_cell(x, y + 1); + let cell = self.get_cell(x, y); if cell != Some(&TetrisCell::Empty) { return false; }