Files
fmn_processor/bench/invaders.c
2025-11-03 11:00:11 +01:00

439 lines
12 KiB
C

#include "fmnsoc.h"
#include "invaders.h"
#define N_OBJECTS 7
/*
* Definition of bitmap for each line in the 8x8 pattern
*
* Since we have a word addressable bitmap, we need to reorder the patterns
* so that they display correctly.
* We do that in the definitions to avoid run time penalties.
*/
#define R(x) (((x & 0xf0) >> 4) | ((x & 0x0f) << 4))
static uint8_t sprite_sship[8] = {
R(0b00000000),
R(0b00111100),
R(0b01111110),
R(0b11111111),
R(0b11111111),
R(0b11100111),
R(0b11000011),
R(0b11000011),
};
static uint8_t sprite_laser[8] = {
R(0b00011000),
R(0b00011000),
R(0b00011000),
R(0b00011000),
R(0b00011000),
R(0b00011000),
R(0b00011000),
R(0b00011000),
};
static uint8_t sprite_alien1[8] = {
R(0b11000011),
R(0b00111100),
R(0b01011010),
R(0b11111111),
R(0b11111111),
R(0b10000001),
R(0b01000010),
R(0b00100100),
};
static uint8_t sprite_alien2[8] = {
R(0b11000011),
R(0b00111100),
R(0b01011010),
R(0b11111111),
R(0b11111111),
R(0b10100101),
R(0b10100101),
R(0b01011010),
};
static uint8_t sprite_alien3[8] = {
R(0b01000010),
R(0b00100100),
R(0b00111100),
R(0b01011010),
R(0b11111111),
R(0b10111101),
R(0b10000001),
R(0b01000010),
};
static uint8_t sprite_alien4[8] = {
R(0b11000011),
R(0b00100100),
R(0b10111101),
R(0b11100111),
R(0b00111100),
R(0b00100100),
R(0b01011010),
R(0b10011001),
};
static uint8_t sprite_alien5[8] = {
R(0b01100110),
R(0b01111110),
R(0b11011011),
R(0b01111110),
R(0b10111101),
R(0b10100101),
R(0b10000001),
R(0b01100110),
};
// bleu, vert, rouge pour tenir dans l'uint8_t de color_t comme il faut
static const color_t black = {0, 0, 0};
static const color_t white = {7, 7, 3};
static const color_t red = {0, 0, 3};
static const color_t green = {0, 7, 0};
static const color_t blue = {7, 0, 0};
static const color_t magenta = {7, 0, 3};
static const color_t cyan = {7, 7, 1};
static const color_t yellow = {0, 7, 3};
/* sprite objects */
object_t object[N_OBJECTS] = {
/* blue spaceship */
{1, 3, 1, 18, 23, 0, 0, sprite_sship, blue, {[0 ... 7]={0}}, 0, 0},
/* white laser */
{0, 1, 1, 18, 0, 0, 0, sprite_laser, white, {[0 ... 7]={0}}, 0, 0},
/* green alien */
{1, 4, 1, 10, 0, -1, 0, sprite_alien1, green, {[0 ... 7]={0}}, 0, 0},
/* red alien */
{1, 4, 1, 18, 0, -1, 0, sprite_alien2, red, {[0 ... 7]={0}}, 0, 0},
/* magenta alien */
{1, 4, 1, 26, 0, -1, 0, sprite_alien3, magenta, {[0 ... 7]={0}}, 0, 0},
/* yellow alien */
{1, 4, 1, 14, 2, -1, 0, sprite_alien4, yellow, {[0 ... 7]={0}}, 0, 0},
/* cyan alien */
{1, 4, 1, 22, 2, -1, 0, sprite_alien5, cyan, {[0 ... 7]={0}}, 0, 0}
};
/* Tableau d'états pour la mécanique du jeu */
state_t state[5] = {
{0, 1, 1},
{0, 1, 2},
{1, 0, 3},
{0, 1, 4},
{-1, 0, 1}
};
/*
* Deux pointeurs vers le frame buffer pour éviter les problèmes
* d'aliasing sur les types.
*/
static volatile color_t *img = (volatile color_t *)FRAMEBUFFER;
static volatile uint32_t *framebuffer = (volatile uint32_t *)FRAMEBUFFER;
static volatile uint32_t *led = (volatile uint32_t *)LED_LEDS;
static volatile uint32_t *push = (volatile uint32_t *)LED_PUSHBUTTONS;
static volatile uint64_t *timer = (volatile uint64_t *)CLINT_TIMER;
static volatile uint64_t *timer_cmp = (volatile uint64_t *)CLINT_TIMER_CMP;
static volatile uint32_t *timer_hi = (volatile uint32_t *)CLINT_TIMER_HI;
static volatile uint32_t *timer_lo = (volatile uint32_t *)CLINT_TIMER_LOW;
static volatile uint32_t *timer_cmp_hi = (volatile uint32_t *)CLINT_TIMER_CMP_HI;
static volatile uint32_t *timer_cmp_lo = (volatile uint32_t *)CLINT_TIMER_CMP_LO;
uint32_t mult(uint64_t x, uint64_t y)
{
uint64_t res = 0;
while (y != 0) {
if (y % 2 == 1) {
res += x;
}
x <<= 1;
y >>= 1;
}
return res;
}
void timer_set(uint32_t period, uint32_t time)
{
uint64_t now = *timer;
*timer_cmp = now + mult(((uint64_t)period >> 8), time);
}
void timer_wait(void)
{
while (*timer <= *timer_cmp);
}
void timer_set_and_wait(uint32_t period, uint32_t time)
{
timer_set(period, time);
timer_wait();
}
void led_set(uint32_t value)
{
*led = value;
}
uint32_t push_button_get()
{
return *push;
}
/*
* main program
* ---------------------------------------------------------------------------
*/
int main(void)
{
/* declaration of local variables */
uint32_t i;
uint32_t push_state, led_state, alien_state, edge_reached;
uint32_t n_aliens;
object_t *spaceship, *laser;
init:
/* initialization stage */
push_state = 0; /* no button pressed at beginning */
led_state = 0; /* initial value displayed on leds */
alien_state = 0; /* state of alien in a line */
edge_reached = 0; /* no edge reached at beginning */
n_aliens = N_OBJECTS - 2; /* number of displayed aliens */
initialize();
laser = &object[1]; /* laser is the second declared object */
spaceship = &object[0]; /* spaceship is the first declared object */
spaceship->x = 20; /* set spaceship at the middle */
spaceship->y = 25; /* and bottom of the screen */
#define MAX_X 39
/* display stage */
while (1) {
edge_reached = 0;
/* decrease deadline of alive objects */
for (i = 0; i < N_OBJECTS; i++) {
if (object[i].alive)
object[i].deadline--;
}
/* display all alive objects */
for (i = 0; i < N_OBJECTS; i++) {
if (object[i].alive)
display_sprite(&object[i]);
}
/* determine new positions of all alive objects */
for (i = 0; i < N_OBJECTS; i++) {
/* update object state when deadline is reached */
if (object[i].alive && object[i].deadline == 0) {
/* reinitialize the object deadline to period */
object[i].deadline = object[i].period;
/* determine new position and manage screen edges */
object[i].x += object[i].dx;
if (object[i].x < 0)
object[i].x = 0;
if (object[i].x > MAX_X)
object[i].x = MAX_X;
object[i].y += object[i].dy;
/* test if an edge of the screen was reached by an alien */
if (i >= 2 && (object[i].x == 0 || object[i].x == MAX_X))
edge_reached = 1;
/* store background of the next position */
if (i > 1 && object[i].y >= spaceship->y) {
clear_screen(blue); /* blue screen */
timer_set_and_wait(TIMER_FREQ, 1000);
initialize();
}
}
}
/* test if alien is hit by an alive laser */
if (laser->alive) {
for (i = 2; i < N_OBJECTS; i++) {
if (object[i].alive && laser->x == object[i].x && laser->y == object[i].y) {
n_aliens--;
object[i].alive = 0;
laser->alive = 0;
if (n_aliens == 0) {
/* no more aliens */
spaceship->alive = 0;
clear_screen(yellow); /* yellow screen */
timer_set_and_wait(TIMER_FREQ, 1000);
clear_screen(red); /* red screen */
} else {
display_sprite(&object[i]);
display_sprite(laser);
}
}
}
}
/* when an alien reaches a screen edge, the group of aliens is moved */
if (edge_reached) {
for (i = 2; i < N_OBJECTS; i++) {
object[i].dx = state[alien_state].dx;
object[i].dy = state[alien_state].dy;
}
alien_state = state[alien_state].next_state;
}
/* laser disappears when it reaches the screen top */
if (laser->alive && laser->y == 0) {
laser->alive = 0;
display_sprite(laser);
}
/* manage push buttons */
push_state = push_button_get();
// if we won, press fire to restart
if ((n_aliens == 0)
&& (push_state & 0x4)) {
goto init;
}
if ((spaceship->deadline == 1)
|| (n_aliens == 0)) {
spaceship->dx = 0;
if (push_state & 0x1)
/* to the right */
spaceship->dx = 1;
if (push_state & 0x2)
/* to the left */
spaceship->dx = -1;
if (push_state & 0x4) {
/* fire a laser */
if (!laser->alive) {
laser->alive = 1;
laser->dx = 0;
laser->dy = -1;
laser->x = spaceship->x;
laser->y = spaceship->y - 1;
laser->deadline = laser->period;
}
}
}
/* manage leds' state */
led_set(led_state);
led_state++;
timer_set_and_wait(TIMER_FREQ, 4);
}
}
/*
* definition of functions
* ---------------------------------------------------------------------------
*/
/* function to read a pixel from a (x,y) position of video framebuffer */
color_t read_pixel(uint32_t x, uint32_t y)
{
uint32_t real_y = y * DISPLAY_WIDTH * DISPLAY_SCALE;
uint32_t real_x = x * DISPLAY_SCALE;
return img[real_y + real_x];
}
void write_pixel_scaling(color_t pixel, uint32_t x, uint32_t y)
{
for (int i = 0; i < DISPLAY_SCALE; ++i) {
for (int j = 0; j < DISPLAY_SCALE; ++j) {
uint32_t real_y = y * DISPLAY_SCALE + i;
uint32_t real_x = x * DISPLAY_SCALE + j;
img[real_y * DISPLAY_WIDTH + real_x] = pixel;
}
}
}
void memsetw(volatile uint32_t* dest, uint32_t c, uint32_t n)
{
volatile uint32_t *p = dest;
while (n-- > 0) {
*(volatile uint32_t *)dest++ = c;
}
}
void clear_screen(color_t color)
{
pixels_t p;
p.pixel[0] = color;
p.pixel[1] = color;
p.pixel[2] = color;
p.pixel[3] = color;
memsetw(framebuffer, p.pixels, DISPLAY_SIZE / 4);
}
/* function to initialize all objects */
void initialize()
{
uint32_t i, dx, dy;
clear_screen(black); /* black screen */
for (i = 0; i < N_OBJECTS; i++) {
if (i == 1) {
/* laser */
object[i].alive = 0;
object[i].period = 1;
} else {
/* spaceship or aliens */
object[i].alive = 1;
if (i == 0)
/* spaceship */
object[i].period = 3;
else
/* aliens */
object[i].period = 4;
}
object[i].deadline = 1;
if (i > 1) {
/* aliens */
if (i > 4) {
/* alien4 or alien5 */
object[i].y = 3; /* 3rd line */
object[i].x = 6 + (i - 4) * 8;
} else {
/* alien1, alien2 or alien3 */
object[i].y = 1; /* 1st line */
object[i].x = 10 + (i - 2) * 8;
}
object[i].dx = -1;
object[i].dy = 0;
}
object[i].ax = -1;
object[i].ay = -1;
/* initialization of object background considering the last one */
for (dx = 0; dx < 8; dx++)
for (dy = 0; dy < 8; dy++)
object[i].bg[dx][dy] = black;
}
}
/* function to display the 8 pixels of a pattern line */
void display_pattern_line(uint8_t m, uint32_t x, uint32_t y, color_t color)
{
for (int i = 0; i < 8; i++) {
color_t new_color = (m & 1) == 1 ? color : black;
m >>= 1;
write_pixel_scaling(new_color, x + i, y);
}
}
/* function to display an 8x8 object considering the last background */
void display_pattern(uint8_t pattern[8], uint32_t x, uint32_t y, color_t color)
{
for (int i = 0; i < 8; i++)
display_pattern_line(pattern[i], x, y + i, color);
}
/* function to display an 8x8 object (spaceship, laser or alien) */
void display_sprite(object_t *object)
{
if ((object->ax > -1 && object->ay > -1) && (object->x != object->ax || object->y != object->ay || !object->alive)) {
for (uint32_t dx = 0; dx < 8; dx++) {
for (uint32_t dy = 0; dy < 8; dy++) {
write_pixel_scaling(object->bg[dx][dy], ((object->ax) << 3) + dx, ((object->ay) << 3) + dy);
if (!object->alive)
object->bg[dx][dy] = black;
}
}
}
object->ax = object->x;
object->ay = object->y;
if (object->alive)
display_pattern(object->pattern, (object->x) << 3, (object->y) << 3, object->color);
}