Added falling tetrominos
This commit is contained in:
parent
a8e8c1fcac
commit
8fb04f9ad0
@ -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
278
tetros/src/fall/mod.rs
Normal 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),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TetrisCell {
|
||||
Empty,
|
||||
Blue,
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user