13 Oct 09:13
#include "platform.h"
#include "xil_printf.h"
#include "displayport.h"
#include "vtc.h"
#include "tpg.h"
#include "xparameters.h"
#include <stdint.h>
#include "xil_types.h"
#include "xil_io.h"
extern char inbyte(void);
// =========================================================================
// KONFIGURACJA EKRANU (Dostosuj do rozdzielczości ustawionej w VTC / TPG)
// =========================================================================
#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 720
#define BLOCK_SIZE 32 // Rozmiar jednego kafelka gry w pikselach (np. 32x32)
// Zdefiniuj wskaźnik na bufor ramki w DDR (sprawdź adres w pliku displayport.c / xparameters.h)
// Często jest to domyślny adres przypisany do modułu VDMA lub DisplayPort Subsystem
#define DISPLAY_FRAME_ADDR 0x10000000
volatile u32* frame_buffer = (volatile u32*)DISPLAY_FRAME_ADDR;
// Wymiary planszy gry w kafelkach
#define BOARD_WIDTH (SCREEN_WIDTH / BLOCK_SIZE) // np. 40 kafelków
#define BOARD_HEIGHT (SCREEN_HEIGHT / BLOCK_SIZE) // np. 22 kafelki
#define MAX_SNAKE_LEN 200
typedef enum { UP, DOWN, LEFT, RIGHT } Direction;
typedef struct {
int16_t x;
int16_t y;
} Point;
// Konwersja Hue na standardowy format 32-bitowy XRGB (0x00RRGGBB)
u32 hsv_to_xrgb(u16 hue, u8 value) {
if (hue >= 360) hue = hue % 360;
u8 max_val = (value * 255) / 100;
if (max_val == 0) return 0x00000000;
u8 r = 0, g = 0, b = 0;
u32 sector = hue / 60;
u32 remainder = hue % 60;
u8 x_val = (max_val * remainder) / 60;
u8 inv_x_val = max_val - x_val;
switch (sector) {
case 0: r = max_val; g = x_val; b = 0; break;
case 1: r = inv_x_val; g = max_val; b = 0; break;
case 2: r = 0; g = max_val; b = x_val; break;
case 3: r = 0; g = inv_x_val; b = max_val; break;
case 4: r = x_val; g = 0; b = max_val; break;
case 5: r = max_val; g = 0; b = inv_x_val; break;
default: r = 0; g = 0; b = 0; break;
}
return ((u32)r << 16) | ((u32)g << 8) | b;
}
// Funkcja rysująca fizyczny kwadrat (kafelek) bezpośrednio w pamięci ekranu
void draw_tile(int16_t tile_x, int16_t tile_y, u32 color_xrgb) {
// Obliczanie początkowych współrzędnych pikseli na ekranie
int16_t start_x = tile_x * BLOCK_SIZE;
int16_t start_y = tile_y * BLOCK_SIZE;
// Rysowanie klocka o wymiarach BLOCK_SIZE x BLOCK_SIZE
for (int16_t y = 0; y < BLOCK_SIZE; y++) {
for (int16_t x = 0; x < BLOCK_SIZE; x++) {
u32 pixel_index = (start_y + y) * SCREEN_WIDTH + (start_x + x);
frame_buffer[pixel_index] = color_xrgb;
}
}
}
void simple_delay(u32 cycles) {
volatile u32 i;
for (i = 0; i < cycles; i++) { __asm__("nop"); }
}
int main() {
init_platform();
xil_printf("\n\n\n--- SNAKE DIRECT FRAMEBUFFER RENDERER ---\r\n");
displayport_init();
displayport_setup_interrupts();
vtc_init();
tpg_init();
// Definiowanie kolorów
u32 COLOR_BG = hsv_to_xrgb(200, 15); // Ciemne tło (przyciemniony niebieski)
u32 COLOR_SNAKE = hsv_to_xrgb(120, 100); // Zielony wąż
u32 COLOR_APPLE = hsv_to_xrgb(0, 100); // Czerwone jabłko
// Wyczyszczenie całego ekranu na kolor tła przed startem
for (u32 i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) {
frame_buffer[i] = COLOR_BG;
}
// Stan gry
Point snake[MAX_SNAKE_LEN];
u8 snake_len = 3;
Direction dir = RIGHT;
Point apple = {12, 7};
Point tail_end = {0, 0};
u8 game_over = 0;
u32 score = 0;
u32 rand_counter = 0;
// Pozycja początkowa
snake[0].x = 10; snake[0].y = 7;
snake[1].x = 9; snake[1].y = 7;
snake[2].x = 8; snake[2].y = 7;
// Narysowanie obiektów początkowych
for(int i = 0; i < snake_len; i++) {
draw_tile(snake[i].x, snake[i].y, COLOR_SNAKE);
}
draw_tile(apple.x, apple.y, COLOR_APPLE);
while (1) {
rand_counter++;
// 1. STEROWANIE UART (Wpisz klawisz w terminalu)
char c = inbyte();
if (c == 0x1B) {
c = inbyte();
if (c == 0x5B) {
c = inbyte();
switch (c) {
case 0x41: if (dir != DOWN) dir = UP; break;
case 0x42: if (dir != UP) dir = DOWN; break;
case 0x43: if (dir != LEFT) dir = RIGHT; break;
case 0x44: if (dir != RIGHT) dir = LEFT; break;
}
}
}
if (game_over) {
xil_printf("\rPrzegrana! Czyszczenie ekranu i restart...");
// Czyszczenie ekranu kafelkami
for(int16_t x = 0; x < BOARD_WIDTH; x++) {
for(int16_t y = 0; y < BOARD_HEIGHT; y++) {
draw_tile(x, y, COLOR_BG);
}
}
// Reset parametrów
snake_len = 3;
dir = RIGHT;
snake[0].x = 10; snake[0].y = 7;
snake[1].x = 9; snake[1].y = 7;
snake[2].x = 8; snake[2].y = 7;
apple.x = (rand_counter % (BOARD_WIDTH - 2)) + 1;
apple.y = (rand_counter / 3 % (BOARD_HEIGHT - 2)) + 1;
score = 0;
game_over = 0;
// Ponowne rysowanie obiektów
for(int i = 0; i < snake_len; i++) {
draw_tile(snake[i].x, snake[i].y, COLOR_SNAKE);
}
draw_tile(apple.x, apple.y, COLOR_APPLE);
continue;
}
// Zapamiętanie końca ogona do zgaszenia
tail_end = snake[snake_len - 1];
// 2. LOGIKA RUCHU
for (int i = snake_len - 1; i > 0; i--) {
snake[i] = snake[i - 1];
}
switch (dir) {
case UP: snake[0].y--; break;
case DOWN: snake[0].y++; break;
case LEFT: snake[0].x--; break;
case RIGHT: snake[0].x++; break;
}
// 3. KOLIZJE ZE ŚCIANAMI I OGONEM
if (snake[0].x < 0 || snake[0].x >= BOARD_WIDTH || snake[0].y < 0 || snake[0].y >= BOARD_HEIGHT) {
game_over = 1;
continue;
}
for (int i = 1; i < snake_len; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
game_over = 1;
}
}
// 4. LOGIKA JABŁKA
if (snake[0].x == apple.x && snake[0].y == apple.y) {
score += 10;
if (snake_len < MAX_SNAKE_LEN) {
snake_len++;
}
apple.x = (rand_counter % (BOARD_WIDTH - 2)) + 1;
apple.y = (rand_counter / 3 % (BOARD_HEIGHT - 2)) + 1;
draw_tile(apple.x, apple.y, COLOR_APPLE);
} else {
// Nadpisanie starego ogona kolorem tła ekranu
draw_tile(tail_end.x, tail_end.y, COLOR_BG);
}
// 5. RENDEROWANIE GŁOWY WĘŻA NA EKRANIE
draw_tile(snake[0].x, snake[0].y, COLOR_SNAKE);
xil_printf("\rScore: %d | Glowa: [%d,%d]", score, snake[0].x, snake[0].y);
simple_delay(2500000);
}
cleanup_platform();
return 0;
}