Statistics
| Branch: | Revision:

root / main.c @ 5b98becf

History | View | Annotate | Download (13.4 KB)

1
#define F_CPU 16000000L
2

    
3
#include <inttypes.h>
4
#include <avr/io.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
#include <avr/pgmspace.h>
8
#include "bricks.h"
9

    
10
//let's make our lives easier :)
11
#define PORT_(port) PORT ## port
12
#define DDR_(port)  DDR  ## port
13
#define PIN_(port)  PIN  ## port
14
#define PORT(port) PORT_(port)
15
#define DDR(port)  DDR_(port)
16
#define PIN(port)  PIN_(port)
17

    
18
// wallboard pins:
19
#define WALLBOARD_PORT         B
20
#define CLOCK_PIN         PB0
21
#define DATA_GREEN_PIN         PB3
22
#define DATA_RED_PIN         PB1
23
#define OUTPUT_ENABLE_PIN PB2
24
// address lines - please note that the address lines have to be
25
// on the same port and in the current implementation are on 0-3
26
#define ADDRESS_PORT         C
27
#define A0                 PC0
28
#define A1                 PC1
29
#define A2                 PC2
30
#define A3                 PC3
31

    
32
#define CONTROLLER0_PORT   D
33
#define CONTROLLER0_LEFT   PD7
34
#define CONTROLLER0_RIGHT  PD6
35
#define CONTROLLER0_DOWN   PD5
36
#define CONTROLLER0_ROTATE PD4
37

    
38
// control mappings for atari joystick
39
#define LEFT   0b0001
40
#define DOWN   0b1000
41
#define RIGHT  0b0010
42
#define ROTATE 0b0100
43

    
44
#define TICKS 40
45
#define NUMBER_OF_PLAYERS 1
46

    
47
struct player;
48

    
49
typedef struct sprite {
50
        volatile int8_t offset;
51
        volatile int8_t yLowerPos;
52
        volatile uint16_t block[4];
53
        volatile uint16_t raw;
54
        volatile struct player *owner;
55
        } sprite_t;
56

    
57
typedef struct player {
58
        volatile uint16_t score;
59
        volatile int8_t gameAreaStart;
60
        volatile int8_t gameAreaEnd;
61
        volatile sprite_t sprite;
62
        volatile uint8_t mirrored;
63
        } player_t;
64

    
65
int main(void);
66
inline void redDot(void);
67
inline void greenDot(void);
68
inline void yellowDot(void);
69
inline void noDot(void);
70
inline void OE(void);
71
inline void ODE(void);
72
void loopLines(void);
73
void rotateLeft(sprite_t* sprite);
74
void moveFlyingRowsDown(player_t* player);
75
void moveSpriteDown(sprite_t* sprite);
76
void clearCompleteLines(player_t* player);
77
void rawToBlock(uint16_t block[4], uint16_t raw, int8_t offset);
78
void moveSpriteToLines(sprite_t* sprite);
79
uint8_t checkPlayerSpriteForCollision(player_t* player, uint16_t block[4], int8_t lookahead); // lookahead -1 -> look down
80
void moveSpriteHorizontal(volatile sprite_t *sprite, int8_t dir); // -1 if left, 1 if right
81
void handleGamepadInput(player_t* player, uint8_t input);
82
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite);
83
static inline void drawGameArea(player_t* player);
84
void gameOver(player_t* player);
85
void setNewSprite(sprite_t* sprite);
86
void drawScore(uint16_t* score, uint8_t round);
87

    
88
volatile uint16_t lines[90] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
89
volatile player_t player[NUMBER_OF_PLAYERS];
90

    
91
volatile uint8_t keypressCounter = 0;
92

    
93
int main(){
94
        // hardware init
95
        // clock, data and output enable
96
        DDR(WALLBOARD_PORT) |= (1<<CLOCK_PIN) | (1<<DATA_RED_PIN) | (1<<DATA_GREEN_PIN) | (1<<OUTPUT_ENABLE_PIN);
97
        // gamepad (PD7 is for bootloadstart):
98
        // 7...4 left down right rotate
99
        //
100
        // move > rotate > down
101
        DDR(CONTROLLER0_PORT) &= ~((1<<CONTROLLER0_LEFT) | (1<<CONTROLLER0_RIGHT) | (1<<CONTROLLER0_DOWN) | (1<<CONTROLLER0_ROTATE));
102
        PORT(CONTROLLER0_PORT) |= (1<<CONTROLLER0_LEFT) | (1<<CONTROLLER0_RIGHT) | (1<<CONTROLLER0_DOWN) | (1<<CONTROLLER0_ROTATE);
103
        // address lines:
104
        DDR(ADDRESS_PORT) |= (1<<A3) | (1<<A2) | (1<<A1) | (1<<A0);
105

    
106
        // init player
107
        player[0].score = 0;
108
        player[0].gameAreaStart = 0;
109
        player[0].gameAreaEnd = 43;
110
        player[0].sprite.owner = (player_t*)&player[0];
111
        player[0].mirrored = 0;
112
        setNewSprite((sprite_t*)&(player[0].sprite));
113

    
114
        while(1){
115
                for(uint8_t playerNumber = 0; playerNumber < NUMBER_OF_PLAYERS; playerNumber++){
116
                        for(uint8_t delay = 0; delay < TICKS; delay++){
117
                                drawGameArea((player_t*)&player[playerNumber]);
118
                                if(delay%20 == 0){
119
                                        handleGamepadInput((player_t*)&player[playerNumber],
120
                                                                ((~PIN(CONTROLLER0_PORT))&
121
                                                                        ( (1<<CONTROLLER0_LEFT)
122
                                                                        | (1<<CONTROLLER0_DOWN)
123
                                                                        | (1<<CONTROLLER0_RIGHT)
124
                                                                        | (1<<CONTROLLER0_ROTATE))
125
                                                                )>>4);
126
                                }
127
                        }
128
                        moveSpriteDown((sprite_t*)&(player[playerNumber].sprite));
129
                        clearCompleteLines((player_t*)&player[playerNumber]);
130
                        moveFlyingRowsDown((player_t*)&player[playerNumber]);
131
                }
132
        }
133
}
134

    
135
void setNewSprite(sprite_t* sprite){
136
        (*sprite).yLowerPos = (*((*sprite).owner)).gameAreaEnd-2;
137
        (*sprite).raw = pgm_read_word(bricks+(keypressCounter%(sizeof(bricks)/sizeof(uint16_t))));
138
        (*sprite).offset = 6;
139
        rawToBlock((uint16_t*)(*sprite).block, (*sprite).raw, (*sprite).offset);
140
        keypressCounter++;
141
}
142

    
143
void gameOver(player_t* player){ // TODO FIXME
144
        for(int16_t line = (*player).gameAreaStart; line < (*player).gameAreaEnd; line++){
145
                lines[line] = 0xffff;
146
                drawGameArea(player);
147
        }
148
        for(int16_t line = (*player).gameAreaEnd-1; line >= (*player).gameAreaStart; line--){
149
                lines[line] = 0x0000;
150
                drawGameArea(player);
151
        }
152
        (*player).score = 0;
153
        setNewSprite((sprite_t*)&((*player).sprite));
154
}
155

    
156
void drawScore(uint16_t* score, uint8_t round){
157
        if((*score) & (1<<round)){
158
                redDot();
159
        }
160
        else{
161
                noDot();
162
        }
163
}
164

    
165
static inline void drawGameArea(player_t* player){ // TODO needs changes for real multiplayer
166
        for(uint8_t round = 0; round < 16; round++){
167
                for(int16_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
168
                        printLine(line, round, (sprite_t*)&((*player).sprite));
169
                }
170
                drawScore((uint16_t*)&((*player).score), round);
171
                drawScore((uint16_t*)&((*player).score), round); // don't mirror the score, so it's easier to compare
172
                for(int16_t line = (*player).gameAreaEnd; line >= (*player).gameAreaStart; line--){
173
                        printLine(line, 15-round, (sprite_t*)&((*player).sprite));
174
                }
175
                OE();
176
                _delay_us(80);
177
                ODE();
178
                _delay_us(1); // avoid ghosting
179
                PORT(ADDRESS_PORT) = round+1;
180
        }
181
}
182

    
183
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite){
184
        if((1<<round) & lines[line]){
185
                yellowDot();
186
        }
187
        else{
188
                if((*sprite).yLowerPos <= line && (*sprite).yLowerPos+4 > line){
189
                        if((1<<round) & (*sprite).block[line-(*sprite).yLowerPos]){
190
                                greenDot();
191
                        }
192
                        else{
193
                                noDot();
194
                        }
195
                }
196
                else{
197
                        noDot();
198
                }
199
        }
200
}
201

    
202
void handleGamepadInput(player_t* player, uint8_t input){
203
        switch(input){
204
                // move > rotate > down
205
                case (LEFT | DOWN | RIGHT | ROTATE):
206
                        keypressCounter+=2;
207
                case (DOWN | ROTATE):
208
                        rotateLeft((sprite_t*)&((*player).sprite));
209
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
210
                        moveSpriteDown((sprite_t*)&((*player).sprite));
211
                        keypressCounter+=2;
212
                        break;
213
                case (LEFT | DOWN | RIGHT):
214
                        keypressCounter+=2;
215
                case (DOWN):
216
                        moveSpriteDown((sprite_t*)&((*player).sprite));
217
                        keypressCounter+=1;
218
                        break;
219
                case (RIGHT):
220
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
221
                        keypressCounter+=1;
222
                        break;
223
                case (LEFT | RIGHT | ROTATE):
224
                        keypressCounter+=1;
225
                case (RIGHT | ROTATE):
226
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
227
                        keypressCounter+=1;
228
                case (ROTATE):
229
                        rotateLeft((sprite_t*)&((*player).sprite));
230
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
231
                        keypressCounter+=1;
232
                        break;
233
                case (DOWN | RIGHT):
234
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
235
                        moveSpriteDown((sprite_t*)&((*player).sprite));
236
                        keypressCounter+=2;
237
                        break;
238
                case (DOWN | RIGHT | ROTATE):
239
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
240
                        rotateLeft((sprite_t*)&((*player).sprite));
241
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
242
                        moveSpriteDown((sprite_t*)&((*player).sprite));
243
                        keypressCounter+=3;
244
                        break;
245
                case (LEFT):
246
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
247
                        keypressCounter+=1;
248
                        break;
249
                case (LEFT | ROTATE):
250
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
251
                        rotateLeft((sprite_t*)&((*player).sprite));
252
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
253
                        keypressCounter+=2;
254
                        break;
255
                case (LEFT | DOWN):
256
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
257
                        moveSpriteDown((sprite_t*)&((*player).sprite));
258
                        keypressCounter+=2;
259
                        break;
260
                case (LEFT | DOWN | ROTATE):
261
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
262
                        rotateLeft((sprite_t*)&((*player).sprite));
263
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
264
                        moveSpriteDown((sprite_t*)&((*player).sprite));
265
                        keypressCounter+=3;
266
                        break;
267
                // "nops"
268
                case (LEFT | RIGHT): // left+right = no move
269
                        keypressCounter+=2;
270
                case (0b0000): // nothing pressed
271
                        break;
272
        }
273
}
274

    
275
void rawToBlock(uint16_t block[4], uint16_t raw, int8_t offset){
276
        block[0] = raw & 0xf;
277
        block[1] = (raw>>4) & 0xf;
278
        block[2] = (raw>>8) & 0xf;
279
        block[3] = (raw>>12) & 0xf;
280

    
281
        if(offset > 0){
282
                for(uint8_t i = 0; i < 4; i++){
283
                        block[i] = block[i]<<offset;
284
                }
285
        }
286
        else if(offset < 0){
287
                for(uint8_t i = 0; i < 4; i++){
288
                        block[i] = block[i]>>(offset*-1);
289
                }
290
        }
291
}
292

    
293
void clearCompleteLines(player_t* player){
294
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
295
                if(lines[line] == 0xffff){
296
                        lines[line] = 0x00;
297
                        (*player).score++;
298
                }
299
        }
300
}
301

    
302
void moveFlyingRowsDown(player_t* player){
303
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
304
                if(lines[line] == 0x0000){ // TODO maybe add clear effect with green line?
305
                        lines[line] = lines[line+1];
306
                        // blank line to propagate movement
307
                        lines[line+1] = 0x0000;
308
                }
309
        }
310
}
311

    
312
void moveSpriteToLines(sprite_t* sprite){
313
        for(uint8_t j = 0; j < 4; j++){ // copy sprite to lines
314
                lines[(*sprite).yLowerPos+j] |= (*sprite).block[j];
315
        }
316
}
317

    
318
uint8_t checkPlayerSpriteForCollision(player_t* player, uint16_t block[4], int8_t lookahead){
319
        for(int8_t i = 0; i < 4; i++){
320
                if((*player).sprite.yLowerPos+lookahead+i < (*player).gameAreaStart){
321
                        if((*player).sprite.block[i] == 0x0000){
322
                                continue;
323
                        }
324
                        else{
325
                                return 1;
326
                        }
327
                }
328
                else if((lines[(*player).sprite.yLowerPos+lookahead+i] & block[i]) != 0){ // collision detected
329
                        return 1;
330
                }
331
        }
332
        return 0;
333
}
334

    
335
void moveSpriteDown(sprite_t* sprite){
336
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, (uint16_t*)(*sprite).block, -1)){
337
                if((*sprite).yLowerPos >= (*(*sprite).owner).gameAreaEnd-4){
338
                        gameOver((player_t*)(*sprite).owner);
339
                        return;
340
                }
341
                moveSpriteToLines(sprite);
342
                setNewSprite(sprite);
343
                return;
344
        }
345
        (*sprite).yLowerPos--;
346
        if((*sprite).yLowerPos <= 0 && ((*sprite).block[-1*(*sprite).yLowerPos] != 0x00)){
347
                moveSpriteToLines(sprite);
348
                setNewSprite(sprite);
349
        }
350
}
351

    
352

    
353
void moveSpriteHorizontal(volatile sprite_t *sprite, int8_t dir){ // -1 if left, 1 if right
354
        uint16_t block[4];
355
        if(dir == 1){
356
                // check if block[*] >= 0x8000, so we cant move left
357
                for(uint8_t i = 0; i < 4; i++){
358
                        if((*sprite).block[i] >= (1<<15)){
359
                                return;
360
                        }
361
                }
362
                for(uint8_t i = 0; i < 4; i++){
363
                        block[i] = (*sprite).block[i] << 1;
364
                }
365
        }
366
        else{
367
                // check if block[*] is uneven, so we cant move right
368
                for(uint8_t i = 0; i < 4; i++){
369
                        if((*sprite).block[i] % 2){
370
                                return;
371
                        }
372
                }
373
                for(uint8_t i = 0; i < 4; i++){
374
                        block[i] = (*sprite).block[i] >> 1;
375
                }
376
        }
377
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
378
                return;
379
        }
380
        else{
381
                for(uint8_t i = 0; i < 4; i++){
382
                        (*sprite).block[i] = block[i];
383
                }
384
                (*sprite).offset = (*sprite).offset+dir;
385
        }
386
}
387

    
388
void loopLines(){
389
        uint8_t tmp = lines[0];
390
        for(uint8_t line = 0; line < 89 ; line++){
391
                lines[line] = lines[line+1];
392
        }
393
        lines[89] = tmp;
394
}
395

    
396
inline void greenDot(){
397
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN);
398
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN));
399
}
400
inline void redDot(){
401
        PORT(WALLBOARD_PORT) |= (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
402
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
403
}
404
inline void yellowDot(){
405
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
406
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
407
}
408
inline void noDot(){
409
        PORT(WALLBOARD_PORT) |= (1<<CLOCK_PIN);
410
        PORT(WALLBOARD_PORT) &= ~(1<<CLOCK_PIN);
411
}
412

    
413
inline void OE(){
414
        PORT(WALLBOARD_PORT) |= (1<<OUTPUT_ENABLE_PIN);
415
}
416

    
417
inline void ODE(){
418
        PORT(WALLBOARD_PORT) &= ~(1<<OUTPUT_ENABLE_PIN);
419
}
420

    
421
void rotateLeft(sprite_t* sprite){
422
        uint16_t block[4];
423
        uint16_t tmp = (*sprite).raw;
424

    
425
        tmp =((tmp&(1<<15))>>(15-3) | (tmp&(1<<14))>>(14-7) | (tmp&(1<<13))>>(13-11) | (tmp&(1<<12))<<(15-12) | (tmp&(1<<11))>>(11-2) | (tmp&(1<<10))>>(10-6) | (tmp&(1<<9))<<(10-9) | (tmp&(1<<8))<<(14-8) | (tmp&(1<<7))>>(7-1) | (tmp&(1<<6))>>(6-5) | (tmp&(1<<5))<<(9-5) | (tmp&(1<<4))<<(13-4) | (tmp&(1<<3))>>(3-0) | (tmp&(1<<2))<<(4-2) | (tmp&(1<<1))<<(8-1) | (tmp&(1<<0))<<(12-0) );
426

    
427
        rawToBlock(block, tmp, (*sprite).offset);
428

    
429
        // check if sprite violates borders // TODO needs to be updated if game area doesn't span the whole line
430
        for(uint8_t i = 0; i < 4; i++){
431
                if((*sprite).offset < 0){
432
                        if(((block[i]<<((*sprite).offset*-1)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
433
                                return;
434
                        }
435
                }
436
                else if((*sprite).offset > 0){
437
                        if(((block[i]>>((*sprite).offset)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
438
                                return;
439
                        }
440
                }
441
                else{
442
                        break;
443
                }
444
        }
445

    
446
        // check first if rotated sprite crashes, if, drop to game
447
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
448
                return;
449
        }
450
        else{
451
                (*sprite).raw = tmp;
452
                for(uint8_t i = 0; i < 4; i++){
453
                        (*sprite).block[i] = block[i];
454
                }
455
        }
456
        // TODO rotate with ifs
457
//        l = ((u&(1<<7)) ? (1<<7)>>4 : 0) | ((u&(1<<6)) ? ((1<<6)>>1) : 0) | ((u&(1<<3)) ? ((1<<3)>>1) : 0) | ((u&(1<<2)) ? ((1<<2)<<4) : 0) | ((l&(1<<6)) ? (1<<6)>>1 : 0) | ((l&(1<<2)) ? (1<<2)<<2 : 0) | ((l&(1<<3)) ? (1<<3)>>3 : 0) | ((l&(1<<7)) ? (1<<7)>>6 : 0);
458
}