Скачиваний:
3
Добавлен:
27.01.2022
Размер:
68.19 Кб
Скачать

Министерство образования и науки Кыргызской Республики Государственный Технический Университет им. Раззакова

Факультет Информационных Технологий

Кафедра «Информатика и Вычислительная техника»

Отчет

Лабораторная работа № 5

Бишкек 2019 г.

Задание.

Реализовать тетрис на Atmega16.

Листинг кода

#define __ASSERT_USE_STDERR

//#define NDEBUG

#include <avr/io.h>

#include <avr/pgmspace.h>

#include <stdlib.h>

#include <string.h>

#include "my_assert.h"

#include "pcd8544.h"

#include "pcd8544.c"

typedef enum

{

check = 0,

store = 1,

draw = 2

} TStoreMode;

const uint8_t tetriminos[8*4] = { // there are 7 tetriminos, each of them has 4 orientations (0, 90deg., 180deg., and 270deg.)

0xE0, 0x92, 0xE0, 0x92, // I is only 3 blocks long due to optimization

0xE4, 0xD2, 0x9C, 0x4B, // J

0xF0, 0x93, 0x3C, 0xC9, // L

0xD8, 0xD8, 0xD8, 0xD8, // o

0x78, 0x99, 0x78, 0x99, // S

0xE8, 0x9A, 0x5C, 0x59, // T

0xCC, 0x5A, 0xCC, 0x5A, // Z

0x80, 0x80, 0x80, 0x80 // . additional shape which is not a real tetrimino, but it adds some fun to the game

};

// Global variables

uint8_t currentTetrimino; // tetrimino currently being dropped

uint8_t currentTetriminoPosition; // linear position of tetrimino on screen calculated as x+8*y

uint8_t nextTetrimino; // tetrimino next to be dropped once current finished

#define NEXT_TETRIMINO_POSITION (3 + 8*19) // (X + 8*Y) position

uint8_t matrix[16] = // 16rows, 8 blocks per row, each block is represented by one bit

{

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

};

uint8_t g_score = 0;

#define LEFT_BUTTON_PRESSED (PIND & (1<<PD0)) // returns TRUE if left button is pressed

#define RIGHT_BUTTON_PRESSED (PIND & (1<<PD2)) // returns TRUE if right button is pressed

#define DOWN_BUTTON_PRESSED (PIND & (1<<PD1)) // returns TRUE if down button is pressed

#define ROTATION_BUTTON_PRESSED (PIND & (1<<PD3)) // returns TRUE if rotation button is pressed

#define TIMER_HAS_EXPIRED ((TIFR & (1 << TOV1) ) > 0) // returns TRUE if timer has expired

static uint8_t myrand()

{

static uint8_t g_randomNumber; // uninitialized value; it is ok to be random at init :)

// we use ADC conversion of unconnected ATMEGA ADC pin to read the noise. The noise is added (XOR) to randomized value to make it more random.

ADCSRA |= (1<<ADSC); // run ADC conversion once

while(ADCSRA & (1<<ADSC)); // wait until ADC conversion finishes

g_randomNumber = (g_randomNumber<<1)^ADC;

return g_randomNumber & 0x1C;

}

void startTimer()

{

TIFR = (1 << TOV1); // reset the overflow flag (by writing '1')

TCNT1 = 65535-3580+(g_score*10); // starting from about 0.5s period

TCCR1B = (1 << CS10) | (1 << CS12); // start the timer by setting 1024 prescaler

}

static void randomizeNextTetrimino()

{

currentTetrimino = nextTetrimino;

currentTetriminoPosition = 3; // top middle initial position of current tetrimino

nextTetrimino = myrand();

assert((currentTetrimino >> 2) < 8); // check if correctly randomized

assert((currentTetrimino & 0x03) == 0); // ...

assert((nextTetrimino >> 2) < 8); // ...

assert((nextTetrimino & 0x03) == 0); // ...

}

static void gameInit()

{

// инициализация АЦП

ADMUX = (1 << REFS0) | (1 << REFS1); // ADC0 + internal 2.56V reference

ADCSRA = (1 << ADPS2) | (1 << ADPS1) // 64 prescaler for 4Mhz

| (1 << ADEN); // Enable the ADC

for (uint8_t i = 8; i; --i)

{

randomizeNextTetrimino(); // run it a couple of times to get really random values

}

LcdInit();

// initialize the timer

startTimer(); // start the timer means to start the game play

}

static void drawTile (uint8_t x, uint8_t y)

{

assert(x<8);

assert(y<21);

uint8_t scrX = y*4; // convert virtual coordinates to screen coordinates

uint8_t scrY = 48-(8 + x*4)-4;

LcdBar(scrX, scrY, 4,4);

LcdBar(scrX+1, scrY+1, 2,2);

}

static bool canPlaceTetrimino(uint8_t tetrimino, uint8_t position, TStoreMode storePermanently)

{

assert(position<136 || position==NEXT_TETRIMINO_POSITION);

assert(tetrimino<8*4);

// example tetrimino specification coded as 0x9A ( 01011100 binary; or 010 111 000 as three rows)

uint8_t tetriminoSpec = tetriminos[tetrimino];

uint8_t xPos = position & 0x7; // split position into X and Y coordinates

uint8_t yPos = position >> 3;

if ((!storePermanently) && (yPos>=16)) // check yPos only in "check" mode

{

return FALSE;

}

uint8_t bitMask;

for (bitMask = 0x80; bitMask != 0; bitMask >>= 1)

{

assert(xPos<10);

uint8_t matrixMask = 0x80 >> xPos;

//assert(yPos<18);

if (tetriminoSpec&bitMask)

{

if (storePermanently == draw)

{

drawTile(xPos, yPos);

}

else if (storePermanently == store)

{

matrix[yPos] |= matrixMask;

}

else // if (storePermanently == check)

{

if (xPos >= 8)

{

return FALSE;

}

if (yPos >= 16)

{

return FALSE;

}

if (matrix[yPos] & matrixMask)

{

return FALSE;

}

}

}

++xPos;

if (bitMask & 0x24) // go to next line and correct "xPos" and "yPos"

{

xPos -= 3;

++yPos;

}

}

return TRUE;

}

static void moveTetriminoDown()

{

uint8_t newPosition = currentTetriminoPosition+8; // next row

if (canPlaceTetrimino(currentTetrimino, newPosition, check))

{

currentTetriminoPosition = newPosition;

}

else

{ // store current tetrimino permanently (in the "matrix") in current location

canPlaceTetrimino(currentTetrimino, currentTetriminoPosition, store);

// verify if there is any full line to drop

uint8_t row;

for (row=15; row!=255; --row)

{

while (matrix[row] == 0xff) // we found a row full of tiles; "while" loop is used to remove all full lines dropped to "row" position

{

++g_score;

// drop all rows above "row" one row down

uint8_t rowUp;

for (rowUp=row; rowUp>0; --rowUp)

{

matrix[rowUp] = matrix[rowUp-1];

}

matrix[0] = 0;

}

}

randomizeNextTetrimino();

if (!canPlaceTetrimino(currentTetrimino, currentTetriminoPosition, check))

{

// GAME OVER

while(1){}; // go to infinite loop

}

}

}

static void showScore()

{

LcdBar(2,3,64-(g_score>>2), 1);

}

static void displayScene()

{

// display screen decoration

memset(LcdCache,0x00,LCD_CACHE_SIZE); // clear LCD screen buffer

LcdBar(0,0,72,48);

LcdBar(0,7,65,34);

// draw all tiles dropped till now

uint8_t * lineAddr = &matrix[15];

uint8_t y=16;

while(y)

{

--y;

uint8_t bitMask = 0x01;

uint8_t x=8;

while (x)

{

--x;

if ((*lineAddr)&bitMask)

{

drawTile(x,y);

}

bitMask <<= 1;

}

--lineAddr;

}

// draw current tile

canPlaceTetrimino(currentTetrimino, currentTetriminoPosition, draw);

// draw next tetrimino

canPlaceTetrimino(nextTetrimino, NEXT_TETRIMINO_POSITION, draw);

showScore();

LcdUpdate(); // move the content from screen buffer to the LCD driver memory in order to display

}

static void delayIfButtonPressed()

{

uint32_t t = 165535; // tuned to get the right timing in button repetition

while (--t && (PIND))

{

}

}

int main()

{

gameInit();

while (1)

{

if ((TIMER_HAS_EXPIRED) || (DOWN_BUTTON_PRESSED))

{

moveTetriminoDown();

startTimer();

}

if (ROTATION_BUTTON_PRESSED)

{

uint8_t newTetrimino;

if ((currentTetrimino&0x03) == 0x00)

{

newTetrimino = currentTetrimino | 0x03;

}

else

{

newTetrimino = currentTetrimino - 1;

}

if (canPlaceTetrimino(newTetrimino, currentTetriminoPosition, check))

{

currentTetrimino = newTetrimino;

}

}

uint8_t newPosition;

if (LEFT_BUTTON_PRESSED)

{

if ((currentTetriminoPosition&0x07) != 0)

{

newPosition = currentTetriminoPosition - 1;

goto labelNewPosition; // this is not a good practice, but it was needed for optimization

}

}

if (RIGHT_BUTTON_PRESSED)

{

newPosition = currentTetriminoPosition + 1;

if ((newPosition & 0x07) != 0) // if it is 0 it means that currentTetriminoPosition is on the very right end and we can't move to the right anymore

{

labelNewPosition:

if (canPlaceTetrimino(currentTetrimino, newPosition, check))

{

currentTetriminoPosition = newPosition;

}

}

}

if (PIND) // any button is pressed

{

displayScene();

delayIfButtonPressed();

}

else

{

displayScene();

}

}

return 0;

}

Результат

Рис. 1. Собранная схема тетриса на Proteus

Соседние файлы в папке Микропроцессорные средства и системы