started to add menu

This commit is contained in:
Steffen Vogel 2011-07-14 20:10:54 +02:00
parent e7a7000ff5
commit 6ded692102
11 changed files with 1385 additions and 227 deletions

445
Makefile Normal file
View file

@ -0,0 +1,445 @@
# Hey Emacs, this is a -*- makefile -*-
#----------------------------------------------------------------------------
# avr-gcc Makefile
#
# by Steffen Vogel <info@steffenvogel.de>
#
# Released to the Public Domain - Credits to:
# Eric B. Weddington, Jörg Wunsch, Peter Fleury, Tim Henigan Colin O'Flynn,
# Reiner Patommel, Markus Pfaff, Sander Pool, Frederik Rouleau, Carlos Lamas
#----------------------------------------------------------------------------
# On command line:
#
# make all = Make software
#
# make clean = Clean out built project files
#
# make program = Download the hex file to the device, using avrdude
# Please customize the avrdude settings below first!
#
# make filename.s = Just compile filename.c into the assembler code only
#
# make filename.i = Create a preprocessed source file for use in submitting
# bug reports to the GCC project.
#
# To rebuild project do "make clean" then "make all".
#----------------------------------------------------------------------------
#------------------------------ General Options -----------------------------
# MCU name
#MCU = atmega8
#MCU = atmega328p
MCU = atmega16
# Processor frequency
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
# Typical values are:
F_CPU = 14746000
#F_CPU = 1843200
#F_CPU = 2000000
#F_CPU = 3686400
#F_CPU = 4000000
#F_CPU = 7372800
#F_CPU = 8000000
#F_CPU = 11059200
#F_CPU = 14745600
#F_CPU = 16000000
#F_CPU = 18432000
#F_CPU = 20000000
# Output format
#FORMAT = srec
#FORMAT = binary
FORMAT = ihex
# Target file name (without extension)
TARGET = main
# Object files directory
OBJDIR = obj
# List C source files here (C dependencies are automatically generated)
SRC = $(TARGET).c tetris.c display.c
# List C++ source files here (C dependencies are automatically generated)
CPPSRC =
# List Assembler source files here
# Make them always end in a capital .S. Files ending in a lowercase .s
# will not be considered source files but generated files (assembler
# output from the compiler), and will be deleted upon "make clean"!
# Even though the DOS/Win* filesystem matches both .s and .S the same,
# it will preserve the spelling of the filenames, and gcc itself does
# are about how the name is spelled on its command-line.
ASRC =
# Optimization level, can be [0, 1, 2, 3, ..., s]
# 0 = turn off optimization
# s = optimize for size
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s
# Debugging format
# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
# AVR Studio 4.10 requires dwarf-2.
# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
DEBUG = dwarf-2
# List any extra directories to look for include files here.
# Each directory must be seperated by a space.
# Use forward slashes for directory separators.
# For a directory that has spaces, enclose it in quotes.
EXTRAINCDIRS =
# Compiler flag to set the C Standard level
# c89 = "ANSI" C
# gnu89 = c89 plus GCC extensions
# c99 = ISO C99 standard (not yet fully implemented)
# gnu99 = c99 plus GCC extensions
CSTANDARD = -std=gnu99
# Place -D or -U options here for C sources
CDEFS = -DF_CPU=$(F_CPU)UL
# Place -D or -U options here for C++ sources
#CPPDEFS += -D__STDC_LIMIT_MACROS
#CPPDEFS += -D__STDC_CONSTANT_MACROS
CPPDEFS = -DF_CPU=$(F_CPU)UL
#------------------------------ Compiler Options -----------------------------
# C Compiler Options
# -g generate debugging information
# -O optimization level
# -f tuning, see GCC manual and avr-libc documentation
# -Wall warning level
# -Wa tell GCC to pass this to the assembler.
# -adhlns create assembler listing
#CFLAGS = -g$(DEBUG)
#CFLAGS += -mint8
#CFLAGS += -mshort-calls
#CFLAGS += -Wunreachable-code
#CFLAGS += -Wsign-compare
CFLAGS += -Wall
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wundef
#CFLAGS += -fno-unit-at-a-time
CFLAGS += -funsigned-char
CFLAGS += -funsigned-bitfields
CFLAGS += -fpack-struct
CFLAGS += -fshort-enums
CFLAGS += $(CDEFS)
CFLAGS += -O$(OPT)
CFLAGS += -Wa,-adhlns=$(<:%.c=$(OBJDIR)/%.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)
# C++ Compiler Options
# -g generate debugging information
# -O optimization level
# -f tuning, see GCC manual and avr-libc documentation
# -Wall warning level
# -Wa tell GCC to pass this to the assembler.
# -adhlns create assembler listing
#CPPFLAGS = -g$(DEBUG)
#CPPFLAGS += -mint8
#CPPFLAGS += -mshort-calls
#CPPFLAGS += $(CSTANDARD)
#CPPFLAGS += -Wstrict-prototypes
#CPPFLAGS += -Wunreachable-code
#CPPFLAGS += -Wsign-compare
CPPFLAGS += -Wall
CFLAGS += -Wundef
#CPPFLAGS += -fno-unit-at-a-time
CPPFLAGS += -funsigned-char
CPPFLAGS += -funsigned-bitfields
CPPFLAGS += -fpack-struct
CPPFLAGS += -fshort-enums
CPPFLAGS += -fno-exceptions
CPPFLAGS += $(CPPDEFS)
CPPFLAGS += -O$(OPT)
CPPFLAGS += -Wa,-adhlns=$(<:%.cpp=$(OBJDIR)/%.lst)
CPPFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
# Assembler Options
# -Wa tell GCC to pass this to the assembler.
# -adhlms create listing
# -gstabs have the assembler create line number information;
# note that for use in COFF files, additional information
# about filenames and function names needs to be present
# in the assembler source files
ASFLAGS = -Wa,-adhlns=$(<:%.S=$(OBJDIR)/%.lst),-gstabs
#------------------------------ Library Options ------------------------------
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
# If this is left blank, then it will use the Standard printf version.
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)
PRINTF_LIB =
# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
# If this is left blank, then it will use the Standard scanf version.
#SCANF_LIB = $(SCANF_LIB_MIN)
#SCANF_LIB = $(SCANF_LIB_FLOAT)
SCANF_LIB =
MATH_LIB = -lm
#-------------------------- External Memory Options --------------------------
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
EXTMEMOPTS =
#------------------------------- Linker Options ------------------------------
# -Wl tell GCC to pass this to linker.
# -Map create map file
# --cref add cross reference to map file
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) -lc
#------------------------ Programmer Options (avrdude) -----------------------
# Programmer hardware
# avr910
# avrisp
# arduino
# picoweb
# pony-stk200
# stk200
# stk500
# "avrdude -c ?" to get a full listing
#AVRDUDE_PROGRAMMER = avr910
#AVRDUDE_PROGRAMMER = arduino
AVRDUDE_PROGRAMMER = avrisp2
# The port your programmer is connected to
#AVRDUDE_PORT = /dev/ttyUSB0
#AVRDUDE_PORT = /dev/ttyS0
#AVRDUDE_PORT = /dev/ttyACM0
AVRDUDE_PORT = usb
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
# Uncomment the following if you want avrdude's erase cycle counter.
# Note that this counter needs to be initialized first using -Yn,
# see avrdude manual.
#AVRDUDE_ERASE_COUNTER = -y
# Uncomment the following if you do /not/ wish a verification to be
# performed after programming the device.
#AVRDUDE_NO_VERIFY = -V
# Increase verbosity level
#AVRDUDE_VERBOSE = -v -v
AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)
#=========================== End of Configuration ===========================
# Define programs and commands.
SHELL = bash
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude
REMOVE = rm -f
REMOVEDIR = rm -rf
COPY = cp
# Define messages (english)
MSG_ERRORS_NONE = Errors: none
MSG_SIZE_BEFORE = Size before:
MSG_SIZE_AFTER = Size after:
MSG_FLASH = Creating load file for Flash:
MSG_EEPROM = Creating load file for EEPROM:
MSG_BINARY = Creating binary:
MSG_EXTENDED_LISTING = Creating Extended Listing:
MSG_SYMBOL_TABLE = Creating Symbol Table:
MSG_LINKING = Linking:
MSG_COMPILING = Compiling C:
MSG_COMPILING_CPP = Compiling C++:
MSG_ASSEMBLING = Assembling:
MSG_CLEANING = Cleaning project:
MSG_CREATING_LIBRARY = Creating library:
MSG_PROGRAM = Flashing controller:
# Define all object files
OBJ = $(SRC:%.c=$(OBJDIR)/%.o) $(CPPSRC:%.cpp=$(OBJDIR)/%.o) $(ASRC:%.S=$(OBJDIR)/%.o)
# Define all listing files
LST = $(SRC:%.c=$(OBJDIR)/%.lst) $(CPPSRC:%.cpp=$(OBJDIR)/%.lst) $(ASRC:%.S=$(OBJDIR)/%.lst)
# Compiler flags to generate dependency files
GENDEPFLAGS = -MD -MP -MF .dep/$(@F).d
# Combine all necessary flags and optional flags
# Add target processor to flags
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
# Default target
all: gccversion sizebefore build sizeafter
# Change the build target to build a HEX file or a library
build: elf hex eep bin lss sym
#build: lib
#LIBNAME=lib$(TARGET).a
elf: $(TARGET).elf
hex: $(TARGET).hex
eep: $(TARGET).eep
bin: $(TARGET).bin
lss: $(TARGET).lss
sym: $(TARGET).sym
lib: $(LIBNAME)
# Display size of file
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
ELFSIZE = $(SIZE) -A $(TARGET).elf
sizebefore:
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \
$(SHELL) avr-mem.sh $(TARGET) $(MCU); fi
sizeafter:
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \
$(SHELL) avr-mem.sh $(TARGET) $(MCU); fi
# Display compiler version information
gccversion:
@$(CC) --version
# Program the device
program: $(TARGET).hex $(TARGET).eep
@echo $(MSG_PROGRAM)
@echo
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
$(AVRDUDE_WRITE_EEPROM)
# Create final output files (.hex, .eep) from ELF output file
%.hex: %.elf
@echo
@echo $(MSG_FLASH) $@
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
%.eep: %.elf
@echo
@echo $(MSG_EEPROM) $@
$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@
# Create binary output
%.bin: %.elf
@echo
@echo $(MSG_BINARY) $@
$(OBJCOPY) $(TARGET).elf -O binary $(TARGET).bin
# Create extended listing file from ELF output file
%.lss: %.elf
@echo
@echo $(MSG_EXTENDED_LISTING) $@
$(OBJDUMP) -h -S $< > $@
# Create a symbol table from ELF output file
%.sym: %.elf
@echo
@echo $(MSG_SYMBOL_TABLE) $@
$(NM) -n $< > $@
# Create library from object files
.SECONDARY: $(TARGET).a
.PRECIOUS: $(OBJ)
%.a: $(OBJ)
@echo
@echo $(MSG_CREATING_LIBRARY) $@
$(AR) $@ $(OBJ)
# Link: create ELF output file from object files
.SECONDARY : $(TARGET).elf
.PRECIOUS : $(OBJ)
%.elf: $(OBJ)
@echo
@echo $(MSG_LINKING) $@
$(CC) $(ALL_CFLAGS) $^ -o $@ $(LDFLAGS)
# Compile: create object files from C source files
$(OBJDIR)/%.o : %.c
@echo $(MSG_COMPILING) $<
$(CC) -c $(ALL_CFLAGS) $< -o $@
# Compile: create object files from C++ source files
$(OBJDIR)/%.o : %.cpp
@echo
@echo $(MSG_COMPILING_CPP) $<
$(CC) -c $(ALL_CPPFLAGS) $< -o $@
# Compile: create assembler files from C source files
%.s : %.c
$(CC) -S $(ALL_CFLAGS) $< -o $@
# Compile: create assembler files from C++ source files
%.s : %.cpp
$(CC) -S $(ALL_CPPFLAGS) $< -o $@
# Assemble: create object files from assembler source files
$(OBJDIR)/%.o : %.S
@echo
@echo $(MSG_ASSEMBLING) $<
$(CC) -c $(ALL_ASFLAGS) $< -o $@
# Create preprocessed source for use in sending a bug report
%.i : %.c
$(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@
# Clean project
clean:
@echo $(MSG_CLEANING)
$(REMOVE) $(TARGET).hex
$(REMOVE) $(TARGET).bin
$(REMOVE) $(TARGET).eep
$(REMOVE) $(TARGET).elf
$(REMOVE) $(TARGET).map
$(REMOVE) $(TARGET).sym
$(REMOVE) $(TARGET).lss
$(REMOVEDIR) $(OBJDIR)
$(REMOVE) $(SRC:.c=.s)
$(REMOVE) $(SRC:.c=.d)
$(REMOVEDIR) .dep
# Create object files directory
mkdir $(OBJDIR)
# Include the dependency files
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
# Listing of phony targets
.PHONY: all finish sizebefore sizeafter gccversion \
build elf hex eep lss sym \
clean program

26
avr-mem.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
MAXPERCENT=40
IMAGESIZE=$(ls -l $1.bin | awk '{ print $5 }')
FLASHSIZE=$(perl -e "print 0x$(echo -e "#include <avr/io.h>\nFLASHEND" | avr-cpp -mmcu=$2 | tail -n 1 |cut -c3-);")
FLASHSIZE=$[$FLASHSIZE + 1]
PERCENT=$(perl -e "printf('%.2f', $IMAGESIZE.0 / $FLASHSIZE.0 *100.0);" )
PER=$(perl -e "printf('%i', $IMAGESIZE / $FLASHSIZE *$MAXPERCENT);" )
echo "Imagesize: $IMAGESIZE/$FLASHSIZE bytes (${PERCENT}%)"
if [ $IMAGESIZE -gt $FLASHSIZE ];then
echo " WARNING! Your Image is too big for the selected chip. WARNING!"
echo ""
exit 1
else
echo -n " ["
COUNTER=0
while [ $COUNTER -lt $MAXPERCENT ]; do
if [ $COUNTER -lt $PER ]; then
echo -n "="
else
echo -n "-"
fi
let COUNTER=COUNTER+1
done
echo "]"
fi

90
conway.c Normal file
View file

@ -0,0 +1,90 @@
#include "main.h"
#include "conway.h"
volatile extern uint8_t *volatile display_buffer; /* Buffer für Display */
uint8_t conway_cell_neighbours(uint8_t x, uint8_t y, uint8_t *world) {
uint8_t neighbours = 0;
int8_t a, b;
for (a = x-1; a <= x+1; a++) {
int8_t c = a;
if (a < 0) c += 8;
if (a >= 8) c -= 8;
for (b = y-1; b <= y+1; b++) {
int8_t d = b;
if (a == x && b == y) continue;
if (b < 0) d += 16;
if (b >= 16) d -= 16;
neighbours += (world[d] & (1 << c)) ? 1 : 0;
}
}
return neighbours; /* 0 <= neighbours <= 8 */
}
uint8_t conway_next_cell_gen(uint8_t x, uint8_t y, uint8_t *world) {
uint8_t neighbours = conway_cell_neighbours(x, y, world);
uint8_t alive = world[y] & (1 << x);
if (alive) {
if (neighbours > 3 || neighbours < 2) {
return 0; /* died by over-/underpopulation */
}
else {
return 1; /* kept alive */
}
}
else if (neighbours == 3) {
return 1; /* born */
}
else {
return 0; /* still dead */
}
}
void conway_next_gen(uint8_t *world, uint8_t *next_gen) {
uint8_t x, y;
for (y = 0; y < 16; y++) {
next_gen[y] = 0;
for (x = 0; x < 8; x++) {
next_gen[y] |= conway_next_cell_gen(x, y, world) << x;
}
}
}
void conway_start() {
volatile uint8_t worlds[2][16];
display_buffer = memset(worlds, 0, 32);
uint8_t i = 0;
/* populate world */
/* with pattern
worlds[i][1] |= 0b00001100;
worlds[i][2] |= 0b00011000;
worlds[i][3] |= 0b00001000;*/
/* by random */
for (uint8_t q = 0; q < 8; q++) {
uint8_t row = rand() % 16;
uint8_t col = rand() % 8;
display_set(col, row, 1);
}
while ( TRUE ) {
_delay_ms(100);
conway_next_gen(worlds[i], worlds[1 - i]);
display_buffer = worlds[1 - i];
i = 1 - i; // switch world
if (~PINB & KEY_Y) {
_delay_ms(10);
return; // exit
}
}
}

4
conway.h Normal file
View file

@ -0,0 +1,4 @@
uint8_t conway_cell_neighbours(uint8_t x, uint8_t y, uint8_t *world);
uint8_t conway_next_cell_gen(uint8_t x, uint8_t y, uint8_t *world);
void conway_next_gen(uint8_t *world, uint8_t *next_gen);
void conway_start();

104
display.c Normal file
View file

@ -0,0 +1,104 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include "display.h"
#include "tetris.h"
volatile uint8_t *volatile display_buffer; /* Buffer für Display */
extern volatile board_t brd;
extern volatile stone_t stn;
/**
* Initialisiere Display im Multiplexing Modus
*/
void display_init() {
DDRC = 0xff; /* Ausgang: Zeilen 0-7 */
DDRA = 0xff; /* Ausgang: Zeilen 8-15 */
DDRD = 0xff; /* Ausgang: Spalten 0-7 (gespiegelt) */
/* Initalisiere Timer */
/**
* "NES Tetris operates at 60 frames per second. At level 0, a piece falls one step every 48 frames, and at level 19, a piece falls one step every 2 frames"
* (http://en.wikipedia.org/wiki/Tetris)
*/
TCCR0 |= (1 << CS01) | (1 << CS00) | (1 << WGM01);
OCR0 = 240;
TIMSK |= (1 << OCIE0);
TIFR |= (1 << OCF0);
sei();
}
uint8_t * display_print(char *text, uint8_t *buffer) {
uint16_t len = strlen(text);
strupr(text); /* Nur Großbuchstaben sind verfügbar */
for (uint16_t c = 0; c < len; c++) {
char chr = text[len-c-1];
uint8_t pattern;
if (chr >= ' ' && chr <= '_')
pattern = chr - ' ';
else
pattern = 0; /* space */
for (uint8_t p = 0; p < 3; p++) {
buffer[p+c*4+16] = font[pattern][p];
}
//buffer[c*4+16] = 0; /* padding */
}
return buffer;
}
void display_laufschrift(uint8_t *buffer, uint16_t bytes, uint8_t speed, uint8_t rounds) {
display_buffer = buffer;
while(1) {
if (display_buffer == buffer) {
display_buffer = buffer+bytes-16;
if (rounds-- == 0) {
return;
}
}
display_buffer--;
_delay_ms(speed);
}
}
/**
* Multiplexing Routine
*/
ISR(TIMER0_COMP_vect) {
static uint8_t column;
static uint8_t counter;
uint8_t row_mask = (1 << column);
uint16_t column_mask = 0;
for (uint8_t i = 4; i < NUM_LINES; i++) {
if (row_mask & brd[i]) { /* fixed pixels, dimmed */
column_mask |= (1 << (i-4));
}
if (tetris) { /* in tetris mode ? */
if (i >= tetris->stn.pos_y && i < tetris->stn.pos_y+4) { /* in clipping of falling stone ? */
if (row_mask & tetris->stn.clipping[i-stn.pos_y]) {
column_mask |= (1 << (i-4));
}
}
}
}
PORTC = (uint8_t) column_mask;
PORTA = (uint8_t) (column_mask >> 8);
PORTD = row_mask;
column++;
if (column == 8) {
column = 0;
counter++;
}
}

8
display.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _DISPLAY_H_
#define _DISPLAY_H_
void display_init( void );
void display_laufschrift(uint8_t *buffer, uint16_t bytes, uint8_t speed, uint8_t rounds);
uint8_t * display_print(char *text, uint8_t *buffer);
#endif /* _DISPLAY_H_ */

324
font.h Normal file
View file

@ -0,0 +1,324 @@
/* 3x7 dot matrix font */
const uint8_t font[][3] = {
{ // ' '
0b00000000,
0b00000000,
0b00000000
},
{ // !
0b01111101,
0b00000000,
0b00000000
},
{ // "
0b01110000,
0b00000000,
0b01110000
},
{ // # => ?
0b00110011,
0b00101101,
0b00110011
},
{ // $ => ?
0b00000000,
0b00001000,
0b00000000
},
{ // %
0b00110010,
0b00001100,
0b00010011
},
{ // & => ?
0b00011100,
0b00001111,
0b00011100
},
{ // '
0b01110000,
0b00000000,
0b00000000
},
{ // (
0b01000001,
0b00111110,
0b00000000
},
{ // )
0b00000000,
0b00111110,
0b01000001
},
{ // *
0b00010100,
0b00001000,
0b00010100
},
{ // +
0b00001000,
0b00011100,
0b00001000
},
{ // ,
0b00000110,
0b00000001,
0b00000000
},
{ // -
0b00001000,
0b00001000,
0b00001000
},
{ // .
0b00000011,
0b00000011,
0b00000000
},
{ // /
0b01100000,
0b00011100,
0b00000011
},
{ // 0
0b00111110,
0b01000001,
0b00111110
},
{ // 1
0b01111111,
0b00100000,
0b00000000
},
{ // 2
0b00110001,
0b01001001,
0b00100111
},
{ // 3
0b00110110,
0b01001001,
0b01000001
},
{ // 4
0b01111111,
0b00001000,
0b01111000
},
{ // 5
0b01001110,
0b01001001,
0b01110001
},
{ // 6
0b01000110,
0b01001001,
0b00111110
},
{ // 7
0b00111111,
0b01000000,
0b01000000
},
{ // 8
0b00110110,
0b01001001,
0b00110110
},
{ // 9
0b00111110,
0b01001001,
0b00110010
},
{ // :
0b00100010,
0b00000000,
0b00000000
},
{ // ;
0b00100010,
0b00000001,
0b00000000
},
{ // <
0b00100010,
0b00010100,
0b00001000
},
{ // =
0b00010010,
0b00010010,
0b00010010
},
{ // >
0b00001000,
0b00010100,
0b00100010
},
{ // ?
0b00110000,
0b01001101,
0b00100000
},
{ // @
0b00111100,
0b01001100,
0b00111111
},
{ // A
0b00111111,
0b01001000,
0b00111111
},
{ // B
0b00110110,
0b01001001,
0b01111111
},
{ // C
0b01000001,
0b01000001,
0b00111110
},
{ // D
0b00111110,
0b01000001,
0b01111111
},
{ // E
0b01000001,
0b01001001,
0b01111111
},
{ // F
0b01000000,
0b01001000,
0b01111111
},
{ // G
0b01001110,
0b01001001,
0b00111110
},
{ // H
0b01111111,
0b00001000,
0b01111111
},
{ // I
0b01000001,
0b01111111,
0b01000001
},
{ // J
0b01111110,
0b01000001,
0b00000010
},
{ // K
0b01110111,
0b00001000,
0b01111111
},
{ // L
0b00000001,
0b00000001,
0b01111111
},
{ // M
0b01111111,
0b00100000,
0b01111111
},
{ // N
0b00111111,
0b01000000,
0b01111111
},
{ // O
0b00111110,
0b01000001,
0b00111110
},
{ // P
0b00110000,
0b01001000,
0b01111111
},
{ // Q
0b00111111,
0b01000001,
0b00111110
},
{ // R
0b00110111,
0b01001000,
0b01111111
},
{ // S
0b01000110,
0b01001001,
0b00110001
},
{ // T
0b01000000,
0b01111111,
0b01000000
},
{ // U
0b01111111,
0b00000001,
0b01111111
},
{ // V
0b01111110,
0b00000001,
0b01111110
},
{ // W
0b01111111,
0b00000010,
0b01111111
},
{ // X
0b01110011,
0b00001100,
0b01110011
},
{ // Y
0b01110000,
0b00001111,
0b01110000
},
{ // Z
0b01110001,
0b01001001,
0b01000111
},
{ // [
0b01000001,
0b01111111,
0b00000000
},
{ // \
0b00000011,
0b00011100,
0b01100000
},
{ // ]
0b00000000,
0b01111111,
0b01000001
},
{ // ^
0b01000000,
0b10000000,
0b01000000
},
{ // _
0b00000001,
0b00000001,
0b00000001
}
};

242
main.c
View file

@ -1,200 +1,60 @@
#include <string.h>
#include <avr/io.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <curses.h>
#include "main.h"
#include "display.h"
#include "tetris.h"
static stone_t shapes[NUM_SHAPES] = {
{SHAPE_I, {
0b00000000,
0b00000000,
0b00000000,
0b00111100
}, 0},
{SHAPE_J, {
0b00000000,
0b00001000,
0b00001000,
0b00011000
}, 0
},
{SHAPE_L, {
0b00000000,
0b00010000,
0b00010000,
0b00011000
}, 0},
{SHAPE_O, {
0b00000000,
0b00000000,
0b00011000,
0b00011000
}, 0},
{SHAPE_S, {
0b00000000,
0b00000000,
0b00011000,
0b00110000
}, 0},
{SHAPE_T, {
0b00000000,
0b00000000,
0b00010000,
0b00111000
}, 0},
{SHAPE_Z, {
0b00000000,
0b00000000,
0b00011000,
0b00001100
}, 0}
static uint8_t rwth_logo[] = {0x7c, 0x50, 0x2c, 0x00, 0x78, 0x04, 0x18, 0x04, 0x78, 0x40, 0x7c, 0x40, 0x7c, 0x10, 0x7c, 0x00};
};
uint8_t get_seed() {
uint8_t seed = 0;
uint8_t *p = (uint8_t *) (RAMEND+1);
extern uint8_t __heap_start;
while (p >= &__heap_start + 1)
seed ^= * (--p);
return seed;
}
void draw_screen(board_t brd, stone_t stn, uint16_t score, uint8_t level, uint8_t stones) {
/* clear screen */
clear();
int main( void ) {
display_init();
srand(get_seed());
/* setup gamepad */
DDRB = 0x00;
PORTB = 0xff;
while ( TRUE ) {
/* Demo 0: Bitmaps */
display_buffer = rwth_logo;
_delay_ms(1500);
/* Demo 1: Tetris */
tetris_start();
for (uint8_t i = 0; i < NUM_LINES; i++) {
addch('|');
for (uint8_t j = 0; j < 8; j++) {
if (brd[i] & (1 << j)) { /* fixed pixel */
addch('#');
}
else if (i >= stn.pos && i < stn.pos+4 && stn.clipping[i-stn.pos] & (1 << j)) {
addch('0');
}
else {
addch(' ');
}
}
printw("%c\n", (i == 3) ? '-' : '|');
/* Demo 4: Conways Game of Life */
conway_start();
/* Demo 2: Laufschrift */
/*char text[] = "'Nobody will ever need more than 640k RAM!' - Bill Gates, 1981 ;-)";
uint16_t len = 4*strlen(text)+32; // 4 Bytes pro Character + 2 * 16 Bytes Padding
volatile uint8_t text_buffer[len];
display_print(text, text_buffer);
// Starte Laufschrift
display_laufschrift(text_buffer, len, 120, 1);*/
/* Demo 3: Zufall */
/*volatile uint8_t random_buffer[16];
display_buffer = memset(random_buffer, 0, 16);
srand(get_seed());
for (uint16_t i = 0; i < 450; i++) {
display_toggle(rand()%8, rand()%16);
display_buffer[0] = i;
_delay_ms(20);
}*/
}
printw("----------");
printw("\nScore: %i\tLevel: %i\tStones: %i\n", score, level, stones);
}
uint8_t flush_lines(board_t brd) {
uint8_t lines = 0;
for (uint8_t i = NUM_LINES-1; i >= 0; i--) {
if (brd[i] == 0xFF) {
lines++;
//for () { /* restock with remaining lines */
//}
}
else if (brd[i] == 0x00) { /* empty line, no following line can be non-empty => aborting */
break;
}
}
return lines; /* required to calculate score */
}
bool_t turn_stone(stone_t s) {
}
bool_t shift_stone(stone_t stn, direction_t dir) {
if (dir == DIR_DOWN) {
stn.pos++;
}
else {
for (uint8_t i = 0; i < 4; i++) {
if (dir == DIR_LEFT) {
stn.clipping[i] <<= 1;
}
else if (dir == DIR_RIGHT) {
stn.clipping[i] >>= 1;
}
}
}
stn.clipping[3] = 0;
}
bool_t detect_collision(board_t brd, stone_t stn) {
return FALSE; // TODO implement
}
WINDOW * init_screen() {
WINDOW * win = initscr();
noecho();
cbreak();
timeout(0);
keypad(win, 1);
curs_set(0);
return win;
}
void main() {
board_t brd;
stone_t falling;
uint8_t level = 0;
uint16_t score = 0;
uint16_t stones = 0;
uint16_t steps = 0;
memset(&brd, 0, NUM_LINES); /* init board */
WINDOW * win = init_screen(); /* init terminal */
/* init prng */
// TODO init with adc value & eeprom seed
srand(129);
while (TRUE) {
/* add new stone on the top of our board */
falling = shapes[rand() % NUM_SHAPES];
if (stones > level * 10) {
level++; /* next level */
}
while (detect_collision(brd, falling) == FALSE) {
/* lets fall stone */
falling.pos++;
draw_screen(brd, falling, score, level, stones);
printw("Steps: %i\n", steps++);
/* poll for user interaction */
switch (getch()) {
case KEY_UP:
turn_stone(falling);
break;
case KEY_LEFT:
shift_stone(falling, DIR_LEFT);
break;
case KEY_RIGHT:
shift_stone(falling, DIR_RIGHT);
break;
case KEY_DOWN:
shift_stone(falling, DIR_DOWN);
break;
}
flushinp();
refresh();
usleep(800000);
}
/* check for completed lines and calculate score */
uint8_t lines = flush_lines(brd);
score += lines * lines;
if (brd[3] > 0) {
break; /* game over */
}
}
// TODO handle highscore
endwin(); /* reset terminal */
}

45
main.h
View file

@ -1,47 +1,20 @@
#ifndef _MAIN_H_
#define _MAIN_H_
#include <stdint.h>
#define TRUE 1
#define FALSE 0
#define NUM_SHAPES 7
#define NUM_LINES 20 /* 16 visible + 4 to spawn new stones */
#define KEY_DOWN (1 << 0)
#define KEY_UP (1 << 1)
#define KEY_A (1 << 2)
#define KEY_RIGHT (1 << 3)
#define KEY_LEFT (1 << 4)
#define KEY_X (1 << 5)
#define KEY_B (1 << 6)
#define KEY_Y (1 << 7)
typedef uint8_t board_t[NUM_LINES];
typedef uint8_t bool_t;
/* Named according to: http://de.wikipedia.org/wiki/Tetris */
typedef enum {
SHAPE_I, /* line of four dots */
SHAPE_J, /* inverted L */
SHAPE_L,
SHAPE_O, /* cubeoid */
SHAPE_S,
SHAPE_T,
SHAPE_Z /* inverted S */
} shape_t;
typedef enum {
DIR_LEFT,
DIR_RIGHT,
DIR_DOWN
} direction_t;
typedef struct {
shape_t shape;
uint8_t clipping[4];
uint8_t pos;
} stone_t;
uint8_t flush_lines(board_t brd);
bool_t turn_stone(stone_t stn);
bool_t shift_stone(stone_t stn, direction_t dir);
bool_t detect_collision(board_t brd, stone_t stn);
/* platform dependent */
void draw_screen(board_t brd, stone_t stn, uint16_t score, uint8_t level, uint8_t stones);
uint8_t get_seed( void );
#endif /* _MAIN_H_ */

267
tetris.c Normal file
View file

@ -0,0 +1,267 @@
#include <util/delay.h>
#include <stdlib.h>
#include <avr/io.h>
#include "tetris.h"
volatile tetris_t *volatile tetris = NULL;
//static int16_t scoring[] = {0, 40, 100, 300, 1200}; /* scoring multiplicator for 0-4 flushed lines */
static clipping_t shapes[][4] = { /* including 4 ccw rotations */
{ // SHAPE_I
{0x08, 0x08, 0x08, 0x08},
{0x00, 0x1e, 0x00, 0x00},
{0x08, 0x08, 0x08, 0x08},
{0x00, 0x3c, 0x00, 0x00}
},
{ // SHAPE_J
{0x00, 0x08, 0x08, 0x18},
{0x00, 0x00, 0x1c, 0x04},
{0x00, 0x0c, 0x08, 0x08},
{0x00, 0x10, 0x1c, 0x00}
},
{ // SHAPE_L
{0x00, 0x10, 0x10, 0x18},
{0x00, 0x08, 0x38, 0x00},
{0x00, 0x30, 0x10, 0x10},
{0x00, 0x00, 0x38, 0x20}
},
{ // SHAPE_O
{0x00, 0x18, 0x18, 0x00},
{0x00, 0x18, 0x18, 0x00},
{0x00, 0x18, 0x18, 0x00},
{0x00, 0x18, 0x18, 0x00}
},
{ // SHAPE_S
{0x00, 0x10, 0x18, 0x08},
{0x00, 0x0c, 0x18, 0x00},
{0x00, 0x08, 0x0c, 0x04},
{0x00, 0x0c, 0x18, 0x00}
},
{ // SHAPE_Z
{0x00, 0x08, 0x18, 0x10},
{0x00, 0x00, 0x18, 0x0c},
{0x00, 0x04, 0x0c, 0x08},
{0x00, 0x00, 0x18, 0x0c}
},
{ // SHAPE_T
{0x00, 0x10, 0x38, 0x00},
{0x00, 0x10, 0x30, 0x10},
{0x00, 0x38, 0x10, 0x00},
{0x00, 0x10, 0x18, 0x10}
}
};
uint8_t tetris_flush_lines() {
uint8_t lines = 0;
uint8_t i = NUM_LINES - 1;
while (i >= 0) {
if (brd[i] == 0xFF) {
lines++;
for (uint8_t j = i; j > 0; j--) { /* restock with remaining lines */
brd[j] = brd[j-1];
}
}
else if (brd[i] == 0x00) { /* empty line, no following line can be non-empty => aborting */
break;
}
else {
i--;
}
}
return lines; /* required to calculate score */
}
bool_t tetris_turn_stone() {
stn.orientation++;
stn.orientation %= 4;
uint8_t check = 0;
clipping_t tmp;
tetris_copy_clipping(shapes[stn.shape][stn.orientation], tmp);
// shift to origin
for (uint8_t i = 0; i < 4; i++) {
if (stn.pos_x > 0) {
tmp[i] >>= abs(stn.pos_x);
check = tmp[i] << abs(stn.pos_x);
}
else {
tmp[i] <<= abs(stn.pos_x);
check = tmp[i] >> abs(stn.pos_x);
}
if (check != shapes[stn.shape][stn.orientation][i] || brd[i+stn.pos_y] & tmp[i]) {
return FALSE; // detected collision
}
}
copy_clipping(tmp, stn.clipping);
return TRUE;
}
bool_t tetris_shift_stone(direction_t dir, uint8_t times) {
while (times--) {
if(tetris_detect_collision(dir)) {
return FALSE;
}
if (dir == DIR_DOWN) { // nach unten fallen lassen
stn.pos_y++;
}
else {
if (dir == DIR_LEFT) { // nach links schieben
stn.pos_x--;
for (uint8_t i = 0; i < 4; i++) {
stn.clipping[i] <<= 1;
}
}
else if (dir == DIR_RIGHT) { // nach rechts schieben
stn.pos_x++;
for (uint8_t i = 0; i < 4; i++) {
stn.clipping[i] >>= 1;
}
}
}
}
return TRUE;
}
bool_t tetris_detect_collision(direction_t dir) {
// Kollision beim Fallen?
if (dir == DIR_ALL || dir == DIR_DOWN) {
for (int8_t i = 3; i >= 0; i--) {
if (stn.clipping[i]) {
if (stn.pos_y+i+1 >= NUM_LINES) { // Kollision mit Boden
return TRUE;
}
else if (stn.clipping[i] & brd[stn.pos_y+i+1]) { // Kollision mit liegenden Steinen
return TRUE;
}
}
}
}
// Kollision beim Shiften?
if (dir == DIR_ALL || dir == DIR_LEFT || dir == DIR_RIGHT) {
for (uint8_t i = 0; i < 4; i++) {
if (stn.clipping[i] & ((dir == DIR_LEFT) ? 0x80 : 0x01)) { // Kollision mit Waenden
return TRUE;
}
// Kollision mit Steinen rechts oder links?
else if (dir == DIR_LEFT && (stn.clipping[i] << 1) & brd[stn.pos_y+i]) { // links
return TRUE;
}
else if (dir == DIR_RIGHT && (stn.clipping[i] >> 1) & brd[stn.pos_y+i]) { // rechts
return TRUE;
}
}
}
return FALSE; /* no collision */
}
void tetris_start() {
tetris_t state;
uint8_t frames = 48;
uint8_t level = 0;
//uint16_t score = 0;
//uint16_t stones = 0;
uint16_t lines = 0;
uint8_t debounce;
tetris = &state;
display_buffer = state.brd+4; /* skipping "virtual" lines */
/* starting with empty board */
for (uint8_t i = 0; i < NUM_LINES; i++) {
brd[i] = 0;
}
while (TRUE) { /* main loop */
/* add new stone on the top of our board */
uint8_t shape = rand() % NUM_SHAPES;
uint8_t orientation = rand() % 4;
stn.shape = shape;
stn.orientation = orientation;
stn.pos_x = 0;
stn.pos_y = 0;
copy_clipping(shapes[shape][orientation], stn.clipping);
if (lines > (level + 1) * 10) {
level++; /* next level */
frames = 48 - level * (46/19.0); /* update frames per step */
}
do {
/* lets fall stone */
tetris_shift_stone(DIR_DOWN, 1);
/* poll for user interaction */
for (uint8_t i = 0; i < frames; i++) {
if (debounce) {
debounce--;
}
else {
uint8_t keys = ~PINB;
if (keys & KEY_A)
tetris_turn_stone();
if (keys & KEY_LEFT)
tetris_shift_stone(DIR_LEFT, 1);
if (keys & KEY_RIGHT)
tetris_shift_stone(DIR_RIGHT, 1);
if (keys & KEY_DOWN)
tetris_shift_stone(DIR_DOWN, 1);
if (keys & KEY_B) /* free fall */
tetris_shift_stone(DIR_DOWN, NUM_LINES);
if (keys & KEY_LEFT && keys & KEY_RIGHT) {
uint8_t *p = brd[NUM_LINES-1];
while(*p) {
*p = 0;
p--;
}
}
if (keys & KEY_Y) {
_delay_ms(10);
return; // exit tetris
}
if (keys) {
debounce = 6
}
}
_delay_ms(900 / FRAMES); // sleep for 1 frame (10% for calculations)
}
} while (tetris_detect_collision(DIR_DOWN) == FALSE);
for (uint8_t i = 0; i < 4; i++) {
brd[stn.pos_y+i] |= stn.clipping[i];
}
/* check for completed lines and calculate score */
uint8_t flushed = flush_lines();
//score += (level + 1) * scoring[flushed];
//stones++;
lines += flushed;
if (brd[3] > 0) {
//return; /* game over */
for (uint8_t i = 0; i < NUM_LINES; i++) { /* restart with empty board */
brd[i] = 0; /* starting with empty board */
}
_delay_ms(1000);
}
}
}

57
tetris.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef _TETRIS_H_
#define _TETRIS_H_
#include <stdint.h>
#include "main.h"
#define NUM_SHAPES 7
#define NUM_LINES 20 /* 16 visible + 4 to spawn new stones */
#define FRAMES 60
typedef uint8_t board_t[NUM_LINES];
typedef uint8_t clipping_t[4];
typedef struct {
stone_t stn;
board_t brd;
} tetris_t;
/* Named according to: http://de.wikipedia.org/wiki/Tetris */
typedef enum {
SHAPE_I, /* line of four dots */
SHAPE_J, /* inverted L */
SHAPE_L,
SHAPE_O, /* cubeoid */
SHAPE_S,
SHAPE_Z, /* inverted S */
SHAPE_T
} shape_t;
typedef enum {
DIR_LEFT,
DIR_RIGHT,
DIR_DOWN,
DIR_ALL
} direction_t;
typedef struct {
shape_t shape;
uint8_t clipping[4];
int8_t pos_x, pos_y, orientation;
} stone_t;
inline static void tetris_copy_clipping(clipping_t src, clipping_t dest) {
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = src[3];
}
void tetris_start( void );
uint8_t tetris_flush_lines( void );
bool_t tetris_turn_stone( void );
bool_t tetris_shift_stone(direction_t dir, uint8_t times);
bool_t tetris_detect_collision(direction_t dir);
#endif /* _TETRIS_H_ */