439 lines
12 KiB
C
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);
|
|
}
|