Refactor tests
This commit is contained in:
438
bench/programs/invaders.c
Normal file
438
bench/programs/invaders.c
Normal file
@@ -0,0 +1,438 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user