1
0

Added falling tetrominos

This commit is contained in:
Mark 2025-02-28 22:24:59 -08:00
parent a8e8c1fcac
commit 8fb04f9ad0
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 393 additions and 32 deletions

View File

@ -40,23 +40,21 @@ impl Vga13h {
let seg = unsafe { self.segment() };
if self.show_fb_a {
seg.copy_from_slice(&self.fb_b);
self.show_fb_a = false
self.show_fb_a = false;
self.fb_a.fill(0);
} else {
seg.copy_from_slice(&self.fb_a);
self.show_fb_a = true
self.show_fb_a = true;
self.fb_b.fill(0);
}
}
pub fn get_fb(&mut self) -> &mut [u8; Vga13h::WIDTH * Vga13h::HEIGHT] {
unsafe { self.segment().try_into().unwrap() }
/*
if self.show_fb_a {
&mut self.fb_b
} else {
&mut self.fb_a
}
*/
}
pub fn pix_idx(x: usize, y: usize) -> usize {

278
tetros/src/fall/mod.rs Normal file
View File

@ -0,0 +1,278 @@
pub enum Tetromino {
/// The 1x4 line
Straight,
/// The 2x2 square
Square,
/// The "L" shape
AngleRight,
/// The mirror "L" shape
AngleLeft,
/// The skew shape "Z"
SkewRight,
/// The mirror skew shape
SkewLeft,
/// The "T" shape
Tee,
}
#[derive(Debug, Clone, Copy)]
pub enum Direction {
North,
East,
South,
West,
}
impl Direction {
pub fn rot_cw(self) -> Self {
match self {
Self::North => Self::East,
Self::East => Self::South,
Self::South => Self::West,
Self::West => Self::North,
}
}
pub fn rot_ccw(self) -> Self {
match self {
Self::North => Self::West,
Self::West => Self::South,
Self::South => Self::East,
Self::East => Self::North,
}
}
}
pub struct FallingTetromino {
tetromino: Tetromino,
direction: Direction,
center_x: usize,
center_y: usize,
}
impl FallingTetromino {
pub fn new(tetromino: Tetromino, center_x: usize, center_y: usize) -> Self {
Self {
tetromino,
direction: Direction::North,
center_x,
center_y,
}
}
pub fn translate(&mut self, x: usize, y: usize) {
self.center_x += x;
self.center_y += y;
}
pub fn rotate_cw(&mut self) {
self.direction = self.direction.rot_cw()
}
pub fn rotate_ccw(&mut self) {
self.direction = self.direction.rot_ccw()
}
/// Returns the positions of this falling tetromino's tiles.
pub fn tiles(&self) -> [(usize, usize); 4] {
match (&self.tetromino, self.direction) {
(Tetromino::Square, _) => [
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
(self.center_x, self.center_y + 1),
(self.center_x + 1, self.center_y + 1),
],
//
// Straight
//
(Tetromino::Straight, Direction::North) => [
(self.center_x - 2, self.center_y - 1),
(self.center_x - 1, self.center_y - 1),
(self.center_x, self.center_y - 1),
(self.center_x + 1, self.center_y - 1),
],
(Tetromino::Straight, Direction::East) => [
(self.center_x + 1, self.center_y - 2),
(self.center_x + 1, self.center_y - 1),
(self.center_x + 1, self.center_y),
(self.center_x + 1, self.center_y + 1),
],
(Tetromino::Straight, Direction::South) => [
(self.center_x - 2, self.center_y + 1),
(self.center_x - 1, self.center_y + 1),
(self.center_x, self.center_y + 1),
(self.center_x + 1, self.center_y + 1),
],
(Tetromino::Straight, Direction::West) => [
(self.center_x - 1, self.center_y - 2),
(self.center_x - 1, self.center_y - 1),
(self.center_x - 1, self.center_y),
(self.center_x - 1, self.center_y + 1),
],
//
// Right Angle
//
(Tetromino::AngleRight, Direction::North) => [
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
(self.center_x + 1, self.center_y + 1),
],
(Tetromino::AngleRight, Direction::East) => [
(self.center_x - 1, self.center_y + 1),
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
],
(Tetromino::AngleRight, Direction::South) => [
(self.center_x - 1, self.center_y - 1),
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
],
(Tetromino::AngleRight, Direction::West) => [
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
(self.center_x + 1, self.center_y - 1),
],
//
// Left Angle
//
(Tetromino::AngleLeft, Direction::North) => [
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
(self.center_x - 1, self.center_y + 1),
],
(Tetromino::AngleLeft, Direction::East) => [
(self.center_x - 1, self.center_y - 1),
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
],
(Tetromino::AngleLeft, Direction::South) => [
(self.center_x + 1, self.center_y - 1),
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
],
(Tetromino::AngleLeft, Direction::West) => [
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
(self.center_x + 1, self.center_y + 1),
],
//
// Left Skew
//
(Tetromino::SkewLeft, Direction::North) => [
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
(self.center_x + 1, self.center_y + 1),
],
(Tetromino::SkewLeft, Direction::East) => [
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x - 1, self.center_y),
(self.center_x - 1, self.center_y - 1),
],
(Tetromino::SkewLeft, Direction::South) => [
(self.center_x - 1, self.center_y - 1),
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
],
(Tetromino::SkewLeft, Direction::West) => [
(self.center_x + 1, self.center_y - 1),
(self.center_x + 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x, self.center_y - 1),
],
//
// Right Skew
//
(Tetromino::SkewRight, Direction::North) => [
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x, self.center_y - 1),
(self.center_x + 1, self.center_y - 1),
],
(Tetromino::SkewRight, Direction::East) => [
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
(self.center_x + 1, self.center_y - 1),
],
(Tetromino::SkewRight, Direction::South) => [
(self.center_x - 1, self.center_y + 1),
(self.center_x, self.center_y + 1),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
],
(Tetromino::SkewRight, Direction::West) => [
(self.center_x - 1, self.center_y - 1),
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x, self.center_y - 1),
],
//
// Tee
//
(Tetromino::Tee, Direction::North) => [
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
(self.center_x, self.center_y - 1),
],
(Tetromino::Tee, Direction::East) => [
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
(self.center_x + 1, self.center_y),
],
(Tetromino::Tee, Direction::South) => [
(self.center_x - 1, self.center_y),
(self.center_x, self.center_y),
(self.center_x + 1, self.center_y),
(self.center_x, self.center_y + 1),
],
(Tetromino::Tee, Direction::West) => [
(self.center_x, self.center_y - 1),
(self.center_x, self.center_y),
(self.center_x, self.center_y + 1),
(self.center_x - 1, self.center_y),
],
}
}
}

View File

@ -4,7 +4,9 @@
#![feature(abi_x86_interrupt)]
#![allow(internal_features)]
use core::arch::asm;
use core::num::NonZeroUsize;
use fall::{FallingTetromino, Tetromino};
use lazy_static::lazy_static;
use spin::Mutex;
@ -13,6 +15,7 @@ use idt::{InterruptDescriptorTable, InterruptStackFrame};
use os::thunk::ThunkData;
use tetrisboard::TetrisBoard;
mod fall;
mod idt;
mod os;
mod tetrisboard;
@ -22,6 +25,9 @@ mod drivers;
pub(crate) static VGA: Mutex<Vga13h> = Mutex::new(unsafe { Vga13h::new() });
pub(crate) static PIC: Mutex<PICDriver> = Mutex::new(PICDriver::new(32, 32 + 8));
pub(crate) static TICK_COUNTER: Mutex<u32> = Mutex::new(0);
pub(crate) static BOARD: Mutex<TetrisBoard> = Mutex::new(TetrisBoard::new());
pub(crate) static FALLING: Mutex<Option<FallingTetromino>> = Mutex::new(None);
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
@ -39,8 +45,12 @@ extern "x86-interrupt" fn divide_handler(stack_frame: InterruptStackFrame) {
println!("DIVIDE ERROR {:?}", stack_frame);
}
extern "x86-interrupt" fn timer_handler(stack_frame: InterruptStackFrame) {
println!("TIMER {:?}", stack_frame);
extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) {
let mut t = TICK_COUNTER.lock();
*t = (*t).wrapping_add(1);
drop(t);
tick();
PIC.lock().send_eoi(32);
}
@ -52,6 +62,32 @@ extern "x86-interrupt" fn double_fault_handler(
panic!("DOUBLE FAULT (err = 0x{error_code:x}\n{:#?}", stack_frame);
}
fn tick() {
let mut v = VGA.lock();
let t = *TICK_COUNTER.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 % 6 == 0 {
if board.tetromino_free_below(fall_inner) {
fall_inner.translate(0, 1);
fall_inner.rotate_cw();
} else {
let mut x = None;
core::mem::swap(&mut x, &mut fall);
board.place_tetromino(x.unwrap());
}
}
} else {
*fall = Some(FallingTetromino::new(Tetromino::AngleLeft, 5, 1))
}
}
#[no_mangle]
pub unsafe extern "C" fn start(
_boot_disk: usize,
@ -86,11 +122,18 @@ pub unsafe extern "C" fn start(
// Clear screen
let mut v = VGA.lock();
let board = BOARD.lock();
let t = TetrisBoard::new();
t.draw(&mut v);
let mut fall = FALLING.lock();
*fall = Some(FallingTetromino::new(Tetromino::SkewRight, 5, 1));
asm!("int $0");
board.draw(&mut v, fall.as_ref());
v.swap();
panic!("kernel");
// Important to drop these, or tick() will deadlock
drop(v);
drop(fall);
drop(board);
loop {}
}

View File

@ -1,5 +1,5 @@
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TetrisCell {
Empty,
Blue,

View File

@ -1,4 +1,4 @@
use crate::drivers::vga::Vga13h;
use crate::{drivers::vga::Vga13h, fall::FallingTetromino};
use super::{TetrisBoard, TetrisCell};
@ -21,9 +21,8 @@ impl TetrisBoard {
}
}
pub fn draw(&self, vga: &mut Vga13h) {
pub fn draw(&self, vga: &mut Vga13h, falling: Option<&FallingTetromino>) {
let fb = vga.get_fb();
// Draw cells
for bx in 0..TetrisBoard::BOARD_WIDTH {
for by in 0..TetrisBoard::BOARD_HEIGHT {
@ -35,6 +34,18 @@ impl TetrisBoard {
}
}
if let Some(falling) = falling {
for (x, y) in falling.tiles() {
let x = x as usize;
let y = y as usize;
let cell = TetrisCell::Blue;
let dx = (x + 1) * TetrisBoard::CELL_SIZE;
let dy = (y + 1) * TetrisBoard::CELL_SIZE;
self.draw_cell(fb, cell, dx, dy);
}
}
// Draw frame
for bx in 0..TetrisBoard::BOARD_WIDTH + 2 {
self.draw_frame(fb, bx * TetrisBoard::CELL_SIZE, 0);

View File

@ -2,6 +2,8 @@ mod cell;
mod draw;
pub use cell::*;
use crate::fall::FallingTetromino;
pub struct TetrisBoard {
board: [TetrisCell; TetrisBoard::BOARD_WIDTH * TetrisBoard::BOARD_HEIGHT],
}
@ -12,24 +14,53 @@ impl TetrisBoard {
const CELL_SIZE: usize = 9;
pub fn new() -> Self {
let mut x = Self {
pub const fn new() -> Self {
Self {
board: [TetrisCell::Empty; TetrisBoard::BOARD_WIDTH * TetrisBoard::BOARD_HEIGHT],
};
}
}
x.board[0] = TetrisCell::Blue;
x.board[1] = TetrisCell::Cyan;
x.board[2] = TetrisCell::Orange;
x.board[3] = TetrisCell::Red;
x.board[4] = TetrisCell::Green;
x.board[5] = TetrisCell::Purple;
x.board[6] = TetrisCell::Yellow;
pub fn place_tetromino(&mut self, tetromino: FallingTetromino) {
for (x, y) in tetromino.tiles() {
let cell = self.get_cell_mut(x as usize, y as usize);
if let Some(cell) = cell {
*cell = TetrisCell::Blue;
}
}
}
x.board[9] = TetrisCell::Blue;
x.board[8] = TetrisCell::Blue;
x.board[10] = TetrisCell::Blue;
x.board[12] = TetrisCell::Blue;
pub fn tetromino_free_below(&self, tetromino: &FallingTetromino) -> bool {
for (x, y) in tetromino.tiles() {
let cell = self.get_cell(x as usize, y as usize + 1);
if cell != Some(&TetrisCell::Empty) {
return false;
}
}
x
return true;
}
pub fn get_cell(&self, x: usize, y: usize) -> Option<&TetrisCell> {
if y >= TetrisBoard::BOARD_HEIGHT {
return None;
}
if x >= TetrisBoard::BOARD_WIDTH {
return None;
}
return Some(&self.board[y * TetrisBoard::BOARD_WIDTH + x]);
}
pub fn get_cell_mut(&mut self, x: usize, y: usize) -> Option<&mut TetrisCell> {
if y >= TetrisBoard::BOARD_HEIGHT {
return None;
}
if x >= TetrisBoard::BOARD_WIDTH {
return None;
}
return Some(&mut self.board[y * TetrisBoard::BOARD_WIDTH + x]);
}
}