diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de1b976 --- /dev/null +++ b/Makefile @@ -0,0 +1,445 @@ +# Hey Emacs, this is a -*- makefile -*- +#---------------------------------------------------------------------------- +# avr-gcc Makefile +# +# by Steffen Vogel +# +# 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 diff --git a/avr-mem.sh b/avr-mem.sh new file mode 100755 index 0000000..2e89d0d --- /dev/null +++ b/avr-mem.sh @@ -0,0 +1,26 @@ +#!/bin/bash +MAXPERCENT=40 +IMAGESIZE=$(ls -l $1.bin | awk '{ print $5 }') +FLASHSIZE=$(perl -e "print 0x$(echo -e "#include \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 diff --git a/conway.c b/conway.c new file mode 100644 index 0000000..dac35e0 --- /dev/null +++ b/conway.c @@ -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 + } + } +} diff --git a/conway.h b/conway.h new file mode 100644 index 0000000..f5b15b4 --- /dev/null +++ b/conway.h @@ -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(); diff --git a/display.c b/display.c new file mode 100644 index 0000000..9d95a69 --- /dev/null +++ b/display.c @@ -0,0 +1,104 @@ +#include +#include + +#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++; + } +} diff --git a/display.h b/display.h new file mode 100644 index 0000000..6e3cd0e --- /dev/null +++ b/display.h @@ -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_ */ diff --git a/font.h b/font.h new file mode 100644 index 0000000..b760e6c --- /dev/null +++ b/font.h @@ -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 + } +}; diff --git a/main.c b/main.c index 8479bf6..ff3169b 100644 --- a/main.c +++ b/main.c @@ -1,200 +1,60 @@ -#include +#include #include -#include -#include -#include #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 */ } diff --git a/main.h b/main.h index 7029086..01dbe35 100644 --- a/main.h +++ b/main.h @@ -1,47 +1,20 @@ #ifndef _MAIN_H_ #define _MAIN_H_ -#include - #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_ */ diff --git a/tetris.c b/tetris.c new file mode 100644 index 0000000..a64c54d --- /dev/null +++ b/tetris.c @@ -0,0 +1,267 @@ +#include +#include +#include + +#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); + } + } +} diff --git a/tetris.h b/tetris.h new file mode 100644 index 0000000..6fc215d --- /dev/null +++ b/tetris.h @@ -0,0 +1,57 @@ +#ifndef _TETRIS_H_ +#define _TETRIS_H_ + +#include + +#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_ */