Add space12 with sound

main
Michael Hayes 15 years ago
parent a0e7c89fb6
commit 3cc1ebee48

@ -0,0 +1,93 @@
# File: Makefile
# Author: M. P. Hayes, UCECE
# Date: 12 Sep 2010
# Descr: Makefile for space12
# Definitions.
CC = avr-gcc
CFLAGS = -mmcu=atmega32u2 -Os -Wall -Wstrict-prototypes -Wextra -g -I. -I../../drivers/avr -I../../extra -I../../utils -I../../fonts -I../../drivers
OBJCOPY = avr-objcopy
SIZE = avr-size
DEL = rm
# Default target.
all: space12.out
# Compile: create object files from C source files.
space12.o: space12.c ../../drivers/avr/eeprom.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../drivers/display.h ../../drivers/led.h ../../drivers/navswitch.h ../../extra/mmelody.h ../../extra/squeaker.h ../../extra/ticker.h ../../extra/tweeter.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/task.h ../../utils/tinygl.h ../../utils/uint8toa.h flasher.h spacey.h
$(CC) -c $(CFLAGS) $< -o $@
flasher.o: flasher.c ../../drivers/avr/system.h flasher.h
$(CC) -c $(CFLAGS) $< -o $@
spacey.o: spacey.c ../../drivers/avr/system.h spacey.h
$(CC) -c $(CFLAGS) $< -o $@
eeprom.o: ../../drivers/avr/eeprom.c ../../drivers/avr/eeprom.h ../../drivers/avr/system.h
$(CC) -c $(CFLAGS) $< -o $@
pio.o: ../../drivers/avr/pio.c ../../drivers/avr/pio.h ../../drivers/avr/system.h
$(CC) -c $(CFLAGS) $< -o $@
system.o: ../../drivers/avr/system.c ../../drivers/avr/system.h
$(CC) -c $(CFLAGS) $< -o $@
timer.o: ../../drivers/avr/timer.c ../../drivers/avr/system.h ../../drivers/avr/timer.h
$(CC) -c $(CFLAGS) $< -o $@
display.o: ../../drivers/display.c ../../drivers/avr/system.h ../../drivers/display.h ../../drivers/ledmat.h
$(CC) -c $(CFLAGS) $< -o $@
led.o: ../../drivers/led.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/led.h
$(CC) -c $(CFLAGS) $< -o $@
ledmat.o: ../../drivers/ledmat.c ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/ledmat.h
$(CC) -c $(CFLAGS) $< -o $@
navswitch.o: ../../drivers/navswitch.c ../../drivers/avr/delay.h ../../drivers/avr/pio.h ../../drivers/avr/system.h ../../drivers/navswitch.h
$(CC) -c $(CFLAGS) $< -o $@
mmelody.o: ../../extra/mmelody.c ../../drivers/avr/system.h ../../extra/mmelody.h ../../extra/ticker.h
$(CC) -c $(CFLAGS) $< -o $@
squeaker.o: ../../extra/squeaker.c ../../drivers/avr/system.h ../../extra/squeaker.h ../../extra/ticker.h
$(CC) -c $(CFLAGS) $< -o $@
ticker.o: ../../extra/ticker.c
$(CC) -c $(CFLAGS) $< -o $@
tweeter.o: ../../extra/tweeter.c ../../drivers/avr/system.h ../../extra/ticker.h ../../extra/tweeter.h
$(CC) -c $(CFLAGS) $< -o $@
task.o: ../../utils/task.c ../../drivers/avr/system.h ../../drivers/avr/timer.h ../../utils/task.h
$(CC) -c $(CFLAGS) $< -o $@
tinygl.o: ../../utils/tinygl.c ../../drivers/avr/system.h ../../drivers/display.h ../../utils/font.h ../../utils/tinygl.h
$(CC) -c $(CFLAGS) $< -o $@
uint8toa.o: ../../utils/uint8toa.c ../../drivers/avr/system.h
$(CC) -c $(CFLAGS) $< -o $@
# Link: create ELF output file from object files.
space12.out: space12.o flasher.o spacey.o eeprom.o pio.o system.o timer.o display.o led.o ledmat.o navswitch.o mmelody.o squeaker.o ticker.o tweeter.o task.o tinygl.o uint8toa.o
$(CC) $(CFLAGS) $^ -o $@ -lm
$(SIZE) $@
# Target: clean project.
.PHONY: clean
clean:
-$(DEL) *.o *.out *.hex
# Target: program project.
.PHONY: program
program: space12.out
$(OBJCOPY) -O ihex space12.out space12.hex
dfu-programmer atmega32u2 erase; dfu-programmer atmega32u2 flash space12.hex; dfu-programmer atmega32u2 start

@ -0,0 +1,88 @@
# File: Makefile
# Author: M. P. Hayes, UCECE
# Date: 11 Sep 2010
# Descr: Makefile for space12
CC = gcc
CFLAGS = -Wall -Wstrict-prototypes -Wextra -g -I. -I../../drivers/test -I../../extra -I../../utils -I../../fonts -I../../drivers
DEL = rm
# Default target.
all: space12
# Compile: create object files from C source files.
space12-test.o: space12.c ../../drivers/display.h ../../drivers/led.h ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/eeprom.h ../../drivers/test/pio.h ../../drivers/test/system.h ../../drivers/test/timer.h ../../extra/mmelody.h ../../extra/squeaker.h ../../extra/ticker.h ../../extra/tweeter.h ../../fonts/font3x5_1.h ../../utils/font.h ../../utils/task.h ../../utils/tinygl.h ../../utils/uint8toa.h flasher.h spacey.h
$(CC) -c $(CFLAGS) $< -o $@
flasher-test.o: flasher.c ../../drivers/test/system.h flasher.h
$(CC) -c $(CFLAGS) $< -o $@
spacey-test.o: spacey.c ../../drivers/test/system.h spacey.h
$(CC) -c $(CFLAGS) $< -o $@
display-test.o: ../../drivers/display.c ../../drivers/display.h ../../drivers/ledmat.h ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
led-test.o: ../../drivers/led.c ../../drivers/led.h ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
ledmat-test.o: ../../drivers/ledmat.c ../../drivers/ledmat.h ../../drivers/test/avrtest.h ../../drivers/test/pio.h ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
navswitch-test.o: ../../drivers/navswitch.c ../../drivers/navswitch.h ../../drivers/test/avrtest.h ../../drivers/test/delay.h ../../drivers/test/pio.h ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
eeprom-test.o: ../../drivers/test/eeprom.c ../../drivers/test/eeprom.h ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
mgetkey-test.o: ../../drivers/test/mgetkey.c ../../drivers/test/mgetkey.h
$(CC) -c $(CFLAGS) $< -o $@
pio-test.o: ../../drivers/test/pio.c
$(CC) -c $(CFLAGS) $< -o $@
system-test.o: ../../drivers/test/system.c ../../drivers/test/avrtest.h ../../drivers/test/mgetkey.h ../../drivers/test/pio.h ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
timer-test.o: ../../drivers/test/timer.c ../../drivers/test/system.h ../../drivers/test/timer.h
$(CC) -c $(CFLAGS) $< -o $@
mmelody-test.o: ../../extra/mmelody.c ../../drivers/test/system.h ../../extra/mmelody.h ../../extra/ticker.h
$(CC) -c $(CFLAGS) $< -o $@
squeaker-test.o: ../../extra/squeaker.c ../../drivers/test/system.h ../../extra/squeaker.h ../../extra/ticker.h
$(CC) -c $(CFLAGS) $< -o $@
ticker-test.o: ../../extra/ticker.c
$(CC) -c $(CFLAGS) $< -o $@
tweeter-test.o: ../../extra/tweeter.c ../../drivers/test/system.h ../../extra/ticker.h ../../extra/tweeter.h
$(CC) -c $(CFLAGS) $< -o $@
task-test.o: ../../utils/task.c ../../drivers/test/system.h ../../drivers/test/timer.h ../../utils/task.h
$(CC) -c $(CFLAGS) $< -o $@
tinygl-test.o: ../../utils/tinygl.c ../../drivers/display.h ../../drivers/test/system.h ../../utils/font.h ../../utils/tinygl.h
$(CC) -c $(CFLAGS) $< -o $@
uint8toa-test.o: ../../utils/uint8toa.c ../../drivers/test/system.h
$(CC) -c $(CFLAGS) $< -o $@
# Link: create executable file from object files.
space12: space12-test.o flasher-test.o spacey-test.o display-test.o led-test.o ledmat-test.o navswitch-test.o eeprom-test.o mgetkey-test.o pio-test.o system-test.o timer-test.o mmelody-test.o squeaker-test.o ticker-test.o tweeter-test.o task-test.o tinygl-test.o uint8toa-test.o
$(CC) $(CFLAGS) $^ -o $@ -lrt
# Clean: delete derived files.
.PHONY: clean
clean:
-$(DEL) space12 space12-test.o flasher-test.o spacey-test.o display-test.o led-test.o ledmat-test.o navswitch-test.o eeprom-test.o mgetkey-test.o pio-test.o system-test.o timer-test.o mmelody-test.o squeaker-test.o ticker-test.o tweeter-test.o task-test.o tinygl-test.o uint8toa-test.o

@ -0,0 +1,42 @@
# File: Makefile
# Author: M. P. Hayes, UCECE
# Date: 11 Sep 2010
# Descr: Makefile for space12 docs
DEL = rm
all: file_dependencies.pdf module_dependencies.pdf makefile_dependencies.pdf build_dependencies.pdf callgraph.pdf
file_dependencies.pdf: files.d
../../../etc/graphdeps.py $< --out $@
module_dependencies.pdf: modules.d
../../../etc/graphdeps.py $< --modules --out $@
makefile_dependencies.pdf: ../Makefile
../../../etc/graphdeps.py $< --out $@
build_dependencies.pdf: ../Makefile
../../../etc/graphdeps.py $< --out $@ --showops
callgraph.pdf: callgraph.d
../../../etc/graphdeps.py --calls --modules $< --out $@ --showops
files.d: ../Makefile
(cd ..; ../../etc/makemake.py --relpath --files . . ../../drivers ../../drivers/avr ../../utils ../../extra --exclude system.h > doc/files.d)
modules.d: ../Makefile
(cd ..; ../../etc/makemake.py --relpath --modules . . ../../drivers ../../drivers/avr ../../utils ../../extra --exclude system > doc/modules.d)
callgraph.d: ../Makefile
(cd ..; ../../etc/makemake.py --cc="avr-gcc" --cflags="-Os -mmcu=atmega32u2" --relpath --calls . . ../../drivers ../../drivers/avr ../../utils ../../extra --exclude system.h > doc/callgraph.d)
# Clean: delete derived files.
.PHONY: clean
clean:
-$(DEL) *.d *.pdf

@ -0,0 +1,16 @@
Running make in this directory will generate a number of PDF graphs.
In the callgraph, the arrows means "calls". In the dependency graphs,
the arrows means "requires" (or "depends upon").
callgraph.pdf This shows the callgraph, i.e., what functions each
function in the program calls.
module_dependencies.pdf This shows the dependencies between the modules.
file_dependencies.pdf This shows the dependencies between the files.
makefile_dependencies.pdf This shows the dependencies required by make when
building the program.
build_dependencies.pdf This is like makefile_dependencies.pdf but shows
the operations performed to generate the new file.
callgraph.d This shows the callgraph in text format.
files.d This shows the file dependencies in text format.
modules.d This shows the module dependencies in text format.

@ -0,0 +1,100 @@
/** @file flasher.c
@author M. P. Hayes, UCECE
@date 13 March 2005
@brief
*/
#include "flasher.h"
/* These routines are for flashing a LED or beeping a piezo tweeter.
Perhaps they should be separated into software PWM and flash
pattern sequencing. */
int8_t
flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern)
{
flasher->pattern = pattern;
flasher->mod_count = 0;
flasher->flasher_count = 0;
flasher->flashes_count = 0;
flasher->flasher_prescale = 0;
return 1;
}
flasher_pattern_t *
flasher_pattern_get (flasher_t flasher)
{
return flasher->pattern;
}
/* FIXME. */
int8_t
flasher_phase_set (flasher_t flasher, uint8_t phase)
{
flasher->mod_count = 0;
flasher->flasher_count = 0;
flasher->flashes_count = phase;
return 1;
}
/* Return the next state for the associated device. For example,
to control the flashing of a LED use:
led_set (led, flasher_update (flasher)); */
bool
flasher_update (flasher_t flasher)
{
if (!flasher->pattern)
return 0;
flasher->mod_count++;
if (flasher->mod_count >= flasher->pattern->mod_period)
{
flasher->mod_count = 0;
flasher->flasher_prescale++;
if (flasher->flasher_prescale >= FLASHER_PRESCALE)
{
flasher->flasher_prescale = 0;
flasher->flasher_count++;
if (flasher->flasher_count >= flasher->pattern->flasher_period)
{
flasher->flasher_count = 0;
flasher->flashes_count++;
if (!flasher->pattern->period)
{
/* One shot mode. */
if (flasher->flashes_count >= flasher->pattern->flashes)
{
/* Disable pattern. */
flasher->pattern = 0;
return 1;
}
}
else if (flasher->flashes_count >= flasher->pattern->period)
{
flasher->flashes_count = 0;
}
}
}
}
return flasher->mod_count < flasher->pattern->mod_duty
&& flasher->flasher_count < flasher->pattern->flasher_duty
&& flasher->flashes_count < flasher->pattern->flashes;
}
/* Create a new flasher device. */
flasher_t
flasher_init (flasher_obj_t *flasher)
{
flasher_pattern_set (flasher, 0);
return flasher;
}

@ -0,0 +1,97 @@
/** @file flasher.h
@author M. P. Hayes, UCECE
@date 13 March 2005
@brief Combined software PWM and flashing module. Use at your peril!
*/
#ifndef FLASHER_H
#define FLASHER_H
#include "system.h"
/* This parameter is for internal use only. It's purpose is to
reduce the chances of flasher_period and flasher_duty overflow.
It could possibly be made an additional parameter for flasher_pattern_t
and calculated by FLASHER_PATTERN. */
#define FLASHER_PRESCALE 8
/* POLL_RATE (Hz)
MOD_FREQ (Hz)
MOD_DUTY (percent)
FLASHER_PERIOD (s) - period between flashes
FLASHER_DUTY (percent) - proportion of flash period that is lit
FLASHES (integer) - how many flashes per flash pattern
PERIOD (s) - how often the flash pattern repeats
*/
#define FLASHER_PATTERN(POLL_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD, \
FLASHER_DUTY, FLASHES, PERIOD) \
(POLL_RATE) / (double)(MOD_FREQ) + 0.5, \
(POLL_RATE) * (double)(MOD_DUTY) / (MOD_FREQ) / 100.0 + 0.5, \
(MOD_FREQ) * (FLASHER_PERIOD) / (double)FLASHER_PRESCALE + 0.5, \
(MOD_FREQ) * (FLASHER_PERIOD) * (FLASHER_DUTY) / 100.0 / FLASHER_PRESCALE + 0.5, \
(FLASHES), \
(PERIOD) / (double)(FLASHER_PERIOD) + 0.5
typedef struct
{
/* This is the modulation period. It determines the frequency
of a tone or flicker rate of a LED. */
uint8_t mod_period;
/* This is the modulation duty. It determines the luminance of a LED. */
uint8_t mod_duty;
/* This is the period between the start of two flashes in a sequence. */
uint8_t flasher_period;
/* This is the flash period. */
uint8_t flasher_duty;
/* This is the number of flashes in the sequence. */
uint8_t flashes;
/* This is the number of flasher periods before the sequence repeats. */
uint8_t period;
} flasher_pattern_t;
#define FLASHER_ACTIVE_P(FLASHER) \
(((flasher_obj_t *)(FLASHER))->pattern != 0)
#define FLASHER_PATTERN_FLASHES_SET(PATTERN, FLASHES) \
(PATTERN)->flashes = (FLASHES)
/* This structure is defined here so the compiler can allocate enough
memory for it. However, its fields should be treated as
private. */
typedef struct
{
flasher_pattern_t *pattern;
uint8_t mod_count;
uint8_t flasher_count;
uint8_t flashes_count;
uint8_t flasher_prescale;
} flasher_private_t;
typedef flasher_private_t flasher_obj_t;
typedef flasher_obj_t *flasher_t;
extern int8_t
flasher_pattern_set (flasher_t flasher, flasher_pattern_t *pattern);
extern flasher_pattern_t *
flasher_pattern_get (flasher_t flasher);
extern int8_t
flasher_phase_set (flasher_t flasher, uint8_t phase);
extern bool
flasher_update (flasher_t);
/* INFO is a pointer into RAM that stores the state of the FLASH.
CFG is a pointer into ROM to define the port the FLASH is connected to.
The returned handle is passed to the other flasher_xxx routines to denote
the FLASH to operate on. */
extern flasher_t
flasher_init (flasher_obj_t *info);
#endif

@ -0,0 +1 @@
"*16<G///>3<D#//A#G///>2 ///D+///D+///D+///D#+//A#F#///D#//A#G/// ///"

@ -0,0 +1,525 @@
/** @file space12.c
@author M. P. Hayes, UCECE
@date 20 April 2007
@brief A simple space invaders game with different difficulty levels.
@defgroup space12 A simple space invaders game.
*/
#include <string.h>
#include "system.h"
#include "display.h"
#include "tinygl.h"
#include "task.h"
#include "navswitch.h"
#include "led.h"
#include "flasher.h"
#include "spacey.h"
#include "eeprom.h"
#include "uint8toa.h"
#include "../fonts/font3x5_1.h"
#include "tweeter.h"
#include "squeaker.h"
#include "mmelody.h"
#include "pio.h"
#define VERSION "1.6"
/** Define polling rates in Hz. The tweeter task needs to be the highest
priority since any jitter will make the sound awful. */
enum {TWEETER_TASK_RATE = 20000};
enum {DISPLAY_TASK_RATE = 300};
enum {TUNE_TASK_RATE = 100};
enum {BUTTON_TASK_RATE = 20};
enum {GAME_TASK_RATE = 100};
enum {FLASHER_UPDATE_RATE = 500};
enum {TUNE_BPM_RATE = 200};
enum {GAME_OVER_PERIOD = 2};
enum {BUTTON_HOLD_PERIOD = 1};
/* Connect piezo tweeter to outermost pins of UCFK4 P1 connector. */
#define PIEZO_PIO PIO_DEFINE (PORT_D, 6)
/** Define flasher modes. */
static flasher_pattern_t flasher_patterns[] =
{
/** TASK_RATE, MOD_FREQ, MOD_DUTY, FLASHER_PERIOD,
FLASHER_DUTY, FLASHES, PERIOD. */
{FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)},
{FLASHER_PATTERN (FLASHER_UPDATE_RATE, 100, 100, 0.4, 100, 1, 0.4)},
{FLASHER_PATTERN (FLASHER_UPDATE_RATE, 200, 100, 0.1, 50, 1, 0.1)},
};
typedef enum {FLASH_MODE_GUN, FLASH_MODE_SHELL,
FLASH_MODE_ALIEN, FLASH_MODE_NUM} flash_mode_t;
typedef enum {STATE_DUMMY, STATE_INIT, STATE_START,
STATE_PLAYING, STATE_OVER,
STATE_READY, STATE_MENU_LEVEL} state_t;
enum {GAME_LEVEL_MAX = 5};
typedef struct
{
uint8_t level;
uint8_t games;
} game_data_t;
static state_t state = STATE_INIT;
static game_data_t game_data;
static uint8_t display[TINYGL_WIDTH * TINYGL_HEIGHT];
static tweeter_scale_t scale_table[] = TWEETER_SCALE_TABLE (TWEETER_TASK_RATE);
static tweeter_t tweeter;
static mmelody_t melody;
static mmelody_obj_t melody_info;
static tweeter_obj_t tweeter_info;
static const char game_intro_tune[] =
{
#include "temple_of_love.mmel"
"> "
};
static const char game_over_tune[] =
{
#include "imperial_march.mmel"
" >"
};
static void tweeter_task_init (void)
{
tweeter = tweeter_init (&tweeter_info, TWEETER_TASK_RATE, scale_table);
pio_config_set (PIEZO_PIO, PIO_OUTPUT_LOW);
}
static void tweeter_task (__unused__ void *data)
{
pio_output_set (PIEZO_PIO, tweeter_update (tweeter));
}
static void tune_task_init (void)
{
melody = mmelody_init (&melody_info, TUNE_TASK_RATE,
(mmelody_callback_t) tweeter_note_play, tweeter);
mmelody_speed_set (melody, TUNE_BPM_RATE);
}
static void tune_task (__unused__ void *data)
{
mmelody_update (melody);
}
/** Draw pixel on display. */
static void
display_handler (void *data, uint8_t col, uint8_t row, spacey_pix_t type)
{
uint8_t *display = data;
uint8_t pixel;
pixel = row * TINYGL_WIDTH + col;
display[pixel] = type;
}
/** Display the game over message. */
static void
game_over_display (char *buffer)
{
char *str = buffer;
strcpy (str, "GAME OVER ");
while (*str)
str++;
uint8toa (spacey_aliens_killed_get (), str, 0);
while (*str)
str++;
*str++ = '/';
uint8toa (spacey_shells_fired_get (), str, 0);
tinygl_clear ();
tinygl_text (buffer);
}
static void
game_text_display (uint8_t num, char *buffer, char *msg)
{
char *str = buffer;
while (*msg)
*str++ = *msg++;
uint8toa (num, str, 0);
tinygl_clear ();
tinygl_text (buffer);
}
/** Display the game level of difficulty. */
static void
game_level_display (uint8_t level, char *buffer)
{
game_text_display (level, buffer, "L");
}
/** Set the game level of difficulty. */
static void
game_level_set (uint8_t level)
{
spacey_options_t options;
uint8_t alien_num = 3;
uint8_t alien_steps = 4;
switch (level)
{
default:
case 0:
options.aliens_wrap = 0;
options.aliens_evasive = 0;
break;
case 1:
options.aliens_wrap = 0;
options.aliens_evasive = 1;
alien_steps = 3;
break;
case 2:
options.aliens_wrap = 1;
options.aliens_evasive = 0;
alien_steps = 3;
break;
case 3:
options.aliens_wrap = 1;
options.aliens_evasive = 1;
alien_steps = 3;
break;
case 4:
options.aliens_wrap = 1;
options.aliens_evasive = 1;
alien_steps = 3;
alien_num = 4;
break;
case 5:
options.aliens_wrap = 1;
options.aliens_evasive = 1;
alien_steps = 2;
alien_num = 4;
break;
}
spacey_alien_num_set (alien_num);
spacey_alien_create_steps_set (alien_steps);
spacey_options_set (options);
}
static void
game_start (game_data_t *data)
{
tinygl_clear ();
data->games++;
game_level_set (data->level);
spacey_start ();
eeprom_write (0, data, sizeof (*data));
/* Stop infuriating tune. */
mmelody_play (melody, "");
}
static void game_event (__unused__ void *data, spacey_event_t event)
{
switch (event)
{
case SPACEY_EVENT_ALIEN_HIT:
mmelody_play (melody, "C+");
break;
case SPACEY_EVENT_ALIEN_LANDED:
mmelody_play (melody, "C");
break;
}
}
static void game_task (__unused__ void *data)
{
static bool init = 0;
static uint8_t game_over_ticks;
char message[44];
if (!init)
{
led_init ();
led_set (LED1, 1);
/* When the EEPROM is erased all the bytes are 0xFF so set to
sensible defaults. */
eeprom_read (0, &game_data, sizeof (game_data));
if (game_data.level == 0xff)
{
game_data.level = 0;
game_data.games = 0;
}
spacey_init (GAME_TASK_RATE, TINYGL_WIDTH, TINYGL_HEIGHT,
display_handler, display);
spacey_event_handler_set (game_event, 0);
init = 1;
}
switch (state)
{
case STATE_PLAYING:
if (! spacey_update ())
{
game_over_display (message);
mmelody_play (melody, game_over_tune);
game_over_ticks = 0;
led_set (LED1, 1);
state = STATE_OVER;
}
break;
case STATE_INIT:
tinygl_text ("SPACEY READY V" VERSION " BY MPH ");
mmelody_play (melody, game_intro_tune);
state = STATE_READY;
break;
case STATE_OVER:
game_over_ticks ++;
if (game_over_ticks >= GAME_TASK_RATE * GAME_OVER_PERIOD)
state = STATE_READY;
/* Fall through. */
case STATE_READY:
case STATE_MENU_LEVEL:
default:
break;
case STATE_START:
/* Turn that bloody blimey space invader off... */
game_start (&game_data);
led_set (LED1, 0);
state = STATE_PLAYING;
break;
}
}
static void display_task (__unused__ void *data)
{
static bool init = 0;
static flasher_t flashers[SPACEY_PIX_TYPE_NUM];
static uint8_t flasher_state[SPACEY_PIX_TYPE_NUM];
static flasher_obj_t flashers_info[SPACEY_PIX_TYPE_NUM];
if (!init)
{
uint8_t i;
tinygl_init (DISPLAY_TASK_RATE);
tinygl_font_set (&font3x5_1);
tinygl_text_mode_set (TINYGL_TEXT_MODE_SCROLL_LEFT);
tinygl_text_mode_set (TINYGL_TEXT_MODE_ROTATE_SCROLL_DOWN);
tinygl_text_speed_set (10);
for (i = 0; i < ARRAY_SIZE (flashers); i++)
{
flashers[i] = flasher_init (&flashers_info[i]);
flasher_state[i] = 0;
}
for (i = 0; i < ARRAY_SIZE (display); i++)
display[i] = 0;
/* Set up flash patterns for different pixel types. */
flasher_pattern_set (flashers[SPACEY_PIX_GUN],
&flasher_patterns[FLASH_MODE_GUN]);
flasher_pattern_set (flashers[SPACEY_PIX_SHELL],
&flasher_patterns[FLASH_MODE_SHELL]);
flasher_pattern_set (flashers[SPACEY_PIX_ALIEN],
&flasher_patterns[FLASH_MODE_ALIEN]);
init = 1;
}
if (state == STATE_PLAYING)
{
uint8_t *src;
uint8_t i;
uint8_t j;
/* Update flasher states. NB, the first flasher is always off. */
for (i = 1; i < ARRAY_SIZE (flashers); i++)
flasher_state[i] = flasher_update (flashers[i]);
/* Update display. */
src = display;
for (j = 0; j < TINYGL_HEIGHT; j++)
for (i = 0; i < TINYGL_WIDTH; i++)
{
tinygl_point_t point = {i, j};
tinygl_draw_point (point, flasher_state[*src++]);
}
}
/* Advance messages and refresh display. */
tinygl_update ();
}
static void navswitch_task (__unused__ void *data)
{
static bool init = 0;
char message[44];
if (!init)
{
navswitch_init ();
init = 1;
}
navswitch_update ();
if (navswitch_push_event_p (NAVSWITCH_EAST))
{
switch (state)
{
case STATE_READY:
break;
case STATE_MENU_LEVEL:
if (game_data.level < GAME_LEVEL_MAX - 1)
game_data.level++;
game_level_display (game_data.level, message);
break;
case STATE_PLAYING:
spacey_gun_move_right ();
break;
default:
break;
}
}
if (navswitch_push_event_p (NAVSWITCH_WEST))
{
switch (state)
{
case STATE_READY:
break;
case STATE_MENU_LEVEL:
if (game_data.level > 1)
game_data.level--;
game_level_display (game_data.level, message);
break;
case STATE_PLAYING:
spacey_gun_move_left ();
break;
default:
break;
}
}
if (navswitch_push_event_p (NAVSWITCH_WEST))
{
switch (state)
{
case STATE_READY:
state = STATE_MENU_LEVEL;
game_level_display (game_data.level, message);
break;
case STATE_MENU_LEVEL:
break;
case STATE_PLAYING:
break;
default:
break;
}
}
if (navswitch_push_event_p (NAVSWITCH_PUSH))
{
switch (state)
{
case STATE_READY:
state = STATE_START;
break;
case STATE_PLAYING:
spacey_gun_fire ();
break;
case STATE_MENU_LEVEL:
state = STATE_READY;
break;
default:
break;
}
}
}
int
main (void)
{
task_t tasks[] =
{
{.func = tweeter_task, .period = TASK_RATE / TWEETER_TASK_RATE,
.data = 0},
{.func = tune_task, .period = TASK_RATE / TUNE_TASK_RATE,
.data = 0},
{.func = display_task, .period = TASK_RATE / DISPLAY_TASK_RATE,
.data = 0},
{.func = navswitch_task, .period = TASK_RATE / BUTTON_TASK_RATE,
.data = 0},
{.func = game_task, .period = TASK_RATE / GAME_TASK_RATE,
.data = 0},
};
system_init ();
tweeter_task_init ();
tune_task_init ();
task_schedule (tasks, ARRAY_SIZE (tasks));
return 0;
}

@ -0,0 +1,578 @@
/** @file spacey.c
@author M. P. Hayes, UCECE
@date 13 March 2007
@brief This implements a simple space invaders game.
*/
#include <stdlib.h>
#include "spacey.h"
/* This function implements a simple space invades program. Aliens
randomly appear on the top row of the display and work their way
randomly down the display. The game ends when 5 aliens have
reached the bottom of the display. To kill the aliens, shells can
be fired at them from a gun on the bottom of the display. The gun
can only move across the bottom and when it gets to the side it
wraps around to the other side.
The aliens could move at different speeds but currently they are
set at the same speed to keep things simple.
*/
#ifndef DISPLAY_INTERLACE
#define DISPLAY_INTERLACE 0
#endif
#if DISPLAY_INTERLACE
/* With an interlaced display we do not have odd numbered rows and
columns. */
enum {SPACEY_SHELL_INC = 2, SPACEY_GUN_INC = 2};
#else
enum {SPACEY_SHELL_INC = 1, SPACEY_GUN_INC = 1};
#endif
enum {SPACEY_LIVES_DEFAULT = 5};
enum {SPACEY_SHELL_SPEED_DEFAULT = 16};
enum {SPACEY_ALIEN_SPEED_DEFAULT = 2};
enum {SPACEY_ALIEN_CREATE_STEPS_DEFAULT = 4};
enum {SPACEY_ALIEN_NUM_DEFAULT = 3};
enum {SPACEY_SHELL_NUM_DEFAULT = 2};
#define min(x, y) ((x) < (y) ? (x) : (y))
static spacey_t spacey_data;
static spacey_t * const spacey = &spacey_data;
void
spacey_lives_set (uint8_t lives)
{
spacey->lives = lives;
}
void
spacey_alien_speed_set (uint8_t alien_speed)
{
spacey->aliens.move_period = spacey->poll_rate / alien_speed;
}
void
spacey_shell_speed_set (uint8_t shell_speed)
{
spacey->shells.move_period = spacey->poll_rate / shell_speed;
}
void
spacey_alien_create_steps_set (uint8_t alien_create_steps)
{
spacey->alien_create_steps = alien_create_steps;
}
void
spacey_alien_num_set (uint8_t aliens_num)
{
spacey->aliens.num = min (aliens_num, SPACEY_ALIENS_MAX);
}
void
spacey_shell_num_set (uint8_t shells_num)
{
spacey->shells.num = min (shells_num, SPACEY_SHELLS_MAX);
}
void
spacey_options_set (spacey_options_t options)
{
spacey->options = options;
}
void
spacey_init (uint16_t poll_rate,
uint8_t x_size, uint8_t y_size,
void (*display_handler) (void *data, uint8_t row, uint8_t col,
spacey_pix_t type),
void *display_data)
{
spacey->size.x = x_size;
spacey->size.y = y_size;
spacey->display_hook = display_handler;
spacey->display_data = display_data;
spacey->active = 0;
spacey->poll_rate = poll_rate;
spacey->event_hook = NULL;
spacey->options.aliens_wrap = 0;
spacey->options.aliens_evasive = 0;
spacey_alien_num_set (SPACEY_ALIEN_NUM_DEFAULT);
spacey_shell_num_set (SPACEY_SHELL_NUM_DEFAULT);
spacey_shell_speed_set (SPACEY_SHELL_SPEED_DEFAULT);
spacey_alien_speed_set (SPACEY_ALIEN_SPEED_DEFAULT);
spacey_alien_create_steps_set (SPACEY_ALIEN_CREATE_STEPS_DEFAULT);
spacey->lives = SPACEY_LIVES_DEFAULT;
}
void
spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t),
void *event_data)
{
spacey->event_hook = event_handler;
spacey->event_data = event_data;
}
static void
spacey_pixel_set (spacey_pos_t *pos,
spacey_pix_t type)
{
spacey->display_hook (spacey->display_data, pos->x, pos->y, type);
}
/* Return if obj is at the same point as another alien. */
static spacey_obj_t *
spacey_alien_conflict_p (spacey_obj_t *obj)
{
int8_t i;
for (i = 0; i < spacey->aliens.num; i++)
{
spacey_obj_t *alien = &spacey->aliens.array[i];
if (!alien->active || alien == obj)
continue;
if (alien->pos.x == obj->pos.x
&& alien->pos.y == obj->pos.y)
return alien;
}
return 0;
}
/* Return if obj is at the same point as another shell. */
static spacey_obj_t *
spacey_shell_conflict_p (spacey_obj_t *obj)
{
int8_t i;
for (i = 0; i < spacey->shells.num; i++)
{
spacey_obj_t *shell = &spacey->shells.array[i];
if (!shell->active || shell == obj)
continue;
if (shell->pos.x == obj->pos.x
&& shell->pos.y == obj->pos.y)
return shell;
}
return 0;
}
static void
spacey_gun_move (int8_t inc)
{
int8_t gun_x;
spacey_pixel_set (&spacey->gun, SPACEY_PIX_OFF);
gun_x = spacey->gun.x + inc;
if (gun_x >= spacey->size.x)
gun_x = 0;
else if (gun_x < 0)
gun_x = spacey->size.x - 1;
spacey->gun.x = gun_x;
spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN);
}
/* Return true if move off display. */
static bool
spacey_shell_move (spacey_obj_t *shell)
{
/* Erase the previous position of the shell except if still
at gun. */
if (shell->pos.x != spacey->gun.x
|| shell->pos.y != spacey->gun.y)
spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF);
/* Shells only go straight up. */
shell->pos.y -= SPACEY_SHELL_INC;
if (shell->pos.y < 0)
return 1;
spacey_pixel_set (&shell->pos, SPACEY_PIX_SHELL);
return 0;
}
static void
spacey_alien_erase (spacey_obj_t *alien)
{
spacey_pixel_set (&alien->pos, SPACEY_PIX_OFF);
}
/* Return true if move off display or get to the bottom. */
static bool
spacey_alien_move (spacey_obj_t *alien)
{
int8_t x_jump;
/* Aliens move down randomly zig zagging. */
alien->pos.y++;
/* Randomly move left or right. */
x_jump = 2 * (rand () & 1) - 1;
/* Don't move into line of gun if being evasive. */
if (spacey->options.aliens_evasive &&
spacey->gun.x == alien->pos.x + x_jump)
x_jump = -x_jump;
alien->pos.x += x_jump;
/* Keep the alien within bounds. */
if (alien->pos.x < 0 || alien->pos.x >= spacey->size.x)
{
if (spacey->options.aliens_wrap)
alien->pos.x = spacey->size.x - abs (alien->pos.x);
else
alien->pos.x -= 2 * x_jump;
}
if (alien->pos.y < spacey->size.y)
spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN);
if (alien->pos.y >= (spacey->size.y - 1))
return 1;
return 0;
}
static void
spacey_alien_create (void)
{
int8_t i;
for (i = 0; i < spacey->aliens.num; i++)
{
spacey_obj_t *alien = &spacey->aliens.array[i];
if (alien->active)
continue;
/* Aliens start at the top. */
alien->pos.y = 0;
while (1)
{
/* Choose random x position. */
alien->pos.x = rand () % spacey->size.x;
#if DISPLAY_INTERLACE
/* Ensure x position is even. */
alien->pos.x &= ~1;
#endif
alien->active = 1;
/* Ensure aliens start at different spots to be fair! */
if (!spacey_alien_conflict_p (alien))
break;
}
spacey->stats.aliens_live++;
spacey_pixel_set (&alien->pos, SPACEY_PIX_ALIEN);
return;
}
/* If we get to here then we already have too many aliens. */
}
static void
spacey_shell_create (void)
{
int8_t i;
for (i = 0; i < spacey->shells.num; i++)
{
spacey_obj_t *shell = &spacey->shells.array[i];
if (shell->active)
continue;
shell->pos = spacey->gun;
shell->active = 1;
spacey->stats.shells_fired++;
/* Don't turn shell on initially since it will erase the gun. */
return;
}
/* If we get to here then we already have too many shells. */
}
static void
spacey_alien_kill (spacey_obj_t *alien)
{
alien->active = 0;
spacey->stats.aliens_live--;
}
static void
spacey_shell_kill (spacey_obj_t *shell)
{
shell->active = 0;
}
static void
spacey_event (spacey_event_t event)
{
if (spacey->event_hook)
spacey->event_hook (spacey->event_data, event);
}
static void
spacey_alien_hit (spacey_obj_t *alien, spacey_obj_t *shell)
{
spacey_alien_kill (alien);
spacey->stats.aliens_killed++;
spacey_shell_kill (shell);
spacey_pixel_set (&shell->pos, SPACEY_PIX_OFF);
spacey_event (SPACEY_EVENT_ALIEN_HIT);
}
static void
spacey_alien_landed (void)
{
spacey->stats.aliens_landed++;
if (spacey->stats.aliens_landed >= spacey->lives)
spacey->active = 0;
spacey_event (SPACEY_EVENT_ALIEN_LANDED);
}
static void
spacey_shells_move (void)
{
int8_t i;
spacey->shells.move_clock++;
if (spacey->shells.move_clock < spacey->shells.move_period)
return;
spacey->shells.move_clock = 0;
/* Shells move until they hit an alien or move off the display. */
for (i = 0; i < spacey->shells.num; i++)
{
spacey_obj_t *shell = &spacey->shells.array[i];
if (!shell->active)
continue;
if (spacey_shell_move (shell))
spacey_shell_kill (shell);
else
{
spacey_obj_t *alien;
/* Check if we've hit an alien. */
while ((alien = spacey_alien_conflict_p (shell)))
{
/* We've hit an alien so kill it and the shell. */
spacey_alien_hit (alien, shell);
}
}
}
}
static void
spacey_aliens_move (void)
{
int8_t i;
uint8_t steps;
spacey->aliens.move_clock++;
if (spacey->aliens.move_clock < spacey->aliens.move_period)
return;
spacey->aliens.move_clock = 0;
/* Erase all aliens before moving them. */
for (i = 0; i < spacey->aliens.num; i++)
{
spacey_obj_t *alien = &spacey->aliens.array[i];
if (!alien->active)
continue;
spacey_alien_erase (alien);
}
/* Now move and redraw aliens. */
for (i = 0; i < spacey->aliens.num; i++)
{
spacey_obj_t *alien = &spacey->aliens.array[i];
if (!alien->active)
continue;
if (spacey_alien_move (alien))
{
if (alien->pos.y == spacey->gun.y)
{
/* The alien has reached Earth. */
spacey_alien_landed ();
}
else
{
/* The alien has moved off the display so disable it. */
spacey_alien_kill (alien);
/* Redraw the gun in case it got hit by the alien. */
spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN);
}
}
else
{
spacey_obj_t *shell;
while ((shell = spacey_shell_conflict_p (alien)))
{
/* The alien hit a shell so kill it and the shell. */
spacey_alien_hit (alien, shell);
}
}
}
/* Randomly create an alien. If there are no aliens create a new
one more rapidly. */
if (!spacey->stats.aliens_live)
steps = 2;
else
steps = spacey->alien_create_steps;
if ((rand () % steps) == 0)
spacey_alien_create ();
}
/* Update the state of the game. */
bool
spacey_update (void)
{
/* Allow playing with the gun even if game inactive. */
spacey_shells_move ();
if (!spacey->active)
return 0;
spacey_aliens_move ();
return 1;
}
/* Move the gun position to the right wrapping back around on left. */
void
spacey_gun_move_right (void)
{
spacey_gun_move (SPACEY_GUN_INC);
}
/* Move the gun position to the left wrapping back around on right. */
void
spacey_gun_move_left (void)
{
spacey_gun_move (-SPACEY_GUN_INC);
}
/* Fire the gun. */
void
spacey_gun_fire (void)
{
spacey_shell_create ();
}
/* Start a new game. */
void
spacey_start (void)
{
int8_t i;
int8_t j;
spacey->shells.move_clock = 0;
spacey->aliens.move_clock = 0;
for (i = 0; i < spacey->shells.num; i++)
{
spacey_obj_t *shell = &spacey->shells.array[i];
shell->active = 0;
}
for (i = 0; i < spacey->aliens.num; i++)
{
spacey_obj_t *alien = &spacey->aliens.array[i];
alien->active = 0;
}
/* Turn all pixels off. */
for (i = 0; i < spacey->size.x; i++)
for (j = 0; j < spacey->size.y; j++)
{
spacey_pos_t pos = {i, j};
spacey_pixel_set (&pos, SPACEY_PIX_OFF);
}
/* Display gun in centre of display. */
spacey->gun.x = (spacey->size.x / 2) & ~1;
spacey->gun.y = spacey->size.y - 1;
spacey_pixel_set (&spacey->gun, SPACEY_PIX_GUN);
spacey->stats.aliens_live = 0;
spacey->stats.aliens_killed = 0;
spacey->stats.aliens_landed = 0;
spacey->stats.shells_fired = 0;
spacey->active = 1;
}
uint8_t
spacey_aliens_killed_get (void)
{
return spacey->stats.aliens_killed;
}
uint8_t
spacey_shells_fired_get (void)
{
return spacey->stats.shells_fired;
}

@ -0,0 +1,152 @@
/** @file spacey.h
@author M. P. Hayes, UCECE
@date 13 March 2007
@brief This is the interface for a simple space invaders game.
*/
#ifndef SPACEY_H
#define SPACEY_H
#include "system.h"
enum {SPACEY_ALIENS_MAX = 4, SPACEY_SHELLS_MAX = 4};
typedef enum {SPACEY_PIX_OFF, SPACEY_PIX_GUN, SPACEY_PIX_ALIEN,
SPACEY_PIX_SHELL, SPACEY_PIX_TYPE_NUM} spacey_pix_t;
typedef enum {SPACEY_EVENT_ALIEN_HIT, SPACEY_EVENT_ALIEN_LANDED} spacey_event_t;
typedef enum {SPACEY_ALIEN, SPACEY_SHELL} spacey_type_t;
/* Positions must be signed. */
typedef struct
{
int8_t x;
int8_t y;
} spacey_pos_t;
typedef struct
{
spacey_pos_t pos;
bool active;
} spacey_obj_t;
typedef struct
{
spacey_obj_t array[SPACEY_ALIENS_MAX];
uint8_t num;
uint16_t move_period;
uint16_t move_clock;
} spacey_objs_t;
typedef struct
{
uint8_t aliens_killed;
uint8_t aliens_landed;
uint8_t shells_fired;
uint8_t aliens_live;
} spacey_stats_t;
typedef struct
{
int aliens_wrap:1;
int aliens_evasive:1;
} spacey_options_t;
typedef struct
{
/* If we have lots of objects then a linked list would be more
efficient for the aliens and shells. */
spacey_objs_t aliens;
spacey_objs_t shells;
spacey_pos_t gun;
spacey_pos_t size;
spacey_stats_t stats;
uint16_t poll_rate; /* Hz */
uint8_t alien_create_steps;
uint8_t lives;
void (*display_hook) (void *data, uint8_t row, uint8_t col, spacey_pix_t type);
void *display_data;
void (*event_hook) (void *data, spacey_event_t event);
void *event_data;
spacey_options_t options;
bool active;
} spacey_t;
void
spacey_event_handler_set (void (*event_handler) (void *, spacey_event_t),
void *event_data);
void
spacey_init (uint16_t poll_rate,
uint8_t x_size, uint8_t y_size,
void (*display_handler) (void *data, uint8_t row, uint8_t col,
spacey_pix_t type),
void *display_data);
/** Start a new game. */
void
spacey_start (void);
/** Update state of game. */
bool
spacey_update (void);
/** Specify number of lives we have before we die. */
void
spacey_lives_set (uint8_t lives);
/** Set alien speed in Hz. */
void
spacey_alien_speed_set (uint8_t alien_speed);
/** Set shell speed in Hz. */
void
spacey_shell_speed_set (uint8_t shell_speed);
/** Set number of steps that a new alien is created on average. */
void
spacey_alien_create_steps_set (uint8_t alien_create_steps);
/** Set maximum number of aliens on display at once. */
void
spacey_alien_num_set (uint8_t aliens_num);
/** Set maximum number of shells on display at once. */
void
spacey_shell_num_set (uint8_t shells_num);
/** Set game options. */
void
spacey_options_set (spacey_options_t options);
/** Move gun to right. */
void
spacey_gun_move_right (void);
/** Move gun to left. */
void
spacey_gun_move_left (void);
/** Fire gun. */
void
spacey_gun_fire (void);
/** Return number of aliens killed in current game. */
uint8_t
spacey_aliens_killed_get (void);
/** Return number of shells fired in current game. */
uint8_t
spacey_shells_fired_get (void);
#endif

@ -0,0 +1,3 @@
/* E2, D4 */
"*8E3EBBE2EB3BDDAAD4DA3AA2AE3EAABBC4CB3BAAGGEEBBE2EB3BDDAAD4DA3AA2AE3EAABBC4CB3BAABB"
Loading…
Cancel
Save