Statistics
| Branch: | Revision:

root / main.c @ 855dd978

History | View | Annotate | Download (13.7 KB)

1
#define F_CPU 16000000L                                                                                                                                                  
2
#include <inttypes.h>
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
#include <avr/pgmspace.h>
7

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

    
16
// wallboard pins:
17
#define WALLBOARD_PORT         B
18
#define CLOCK_PIN         PB0
19
#define DATA_GREEN_PIN         PB3
20
#define DATA_RED_PIN         PB1
21
#define OUTPUT_ENABLE_PIN PB2
22

    
23
#define CONTROLLER_PORT   D
24
#define CONTROLLER_LEFT   PD7
25
#define CONTROLLER_RIGHT  PD6
26
#define CONTROLLER_DOWN   PD5
27
#define CONTROLLER_ROTATE PD4
28

    
29
// control mappings for atari joystick
30
#define LEFT   0b0001
31
#define DOWN   0b1000
32
#define RIGHT  0b0010
33
#define ROTATE 0b0100
34

    
35
#define TICKS 40
36
#define NUMBER_OF_PLAYERS 1
37

    
38
struct player;
39

    
40
typedef struct sprite {
41
        volatile int8_t offset;
42
        volatile int8_t yLowerPos;
43
        volatile uint16_t block[4];
44
        volatile uint16_t raw;
45
        volatile struct player *owner;
46
        } sprite_t;
47

    
48
typedef struct player {
49
        volatile uint16_t score;
50
        volatile int8_t gameAreaStart;
51
        volatile int8_t gameAreaEnd;
52
        volatile sprite_t sprite;
53
        volatile uint8_t mirrored;
54
        } player_t;
55

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

    
79
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 };
80
volatile player_t player[NUMBER_OF_PLAYERS];
81

    
82
const uint16_t bricks[] PROGMEM = { 
83
        /* +----+
84
         * |    |
85
         * | ## |
86
         * | ## |
87
         * |    |
88
         * +----+ */
89
        0b0000011001100000, 
90

    
91
        /* +----+
92
         * |    |
93
         * | #  |
94
         * | ## |
95
         * |  # |
96
         * +----+ */
97
        0b0000010001100010,
98

    
99
        /* +----+
100
         * |    |
101
         * |  # |
102
         * | ## |
103
         * | #  |
104
         * +----+ */
105
        0b0000001001100100, 
106

    
107
        /* +----+
108
         * |    |
109
         * | #  |
110
         * | ## |
111
         * | #  |
112
         * +----+ */
113
        0b0000010001100100, 
114

    
115
        /* +----+
116
         * | #  |
117
         * | #  |
118
         * | #  |
119
         * | #  |
120
         * +----+ */
121
        0b0100010001000100, 
122

    
123
        /* +----+
124
         * |    |
125
         * | #  |
126
         * | #  |
127
         * | ## |
128
         * +----+ */
129
        0b0000010001000110, 
130

    
131
        /* +----+
132
         * |    |
133
         * |  # |
134
         * |  # |
135
         * | ## |
136
         * +----+ */
137
        0b0000001000100110
138
};
139
volatile uint8_t keypressCounter = 0;
140

    
141
int main(){
142
        // hardware init
143
        // clock, data and output enable
144
        DDR(WALLBOARD_PORT) = (1<<CLOCK_PIN) | (1<<DATA_RED_PIN) | (1<<DATA_GREEN_PIN) | (1<<OUTPUT_ENABLE_PIN);
145
        // gamepad (PD7 is for bootloadstart):
146
        // 7...4 left down right rotate
147
        //
148
        // move > rotate > down
149
        DDR(CONTROLLER_PORT) &= ~((1<<CONTROLLER_LEFT) | (1<<CONTROLLER_RIGHT) | (1<<CONTROLLER_DOWN) | (1<<CONTROLLER_ROTATE));
150
        PORT(CONTROLLER_PORT) |= (1<<CONTROLLER_LEFT) | (1<<CONTROLLER_RIGHT) | (1<<CONTROLLER_DOWN) | (1<<CONTROLLER_ROTATE);
151
        // address lines:
152
        DDR(C) = 0xff;
153

    
154
        // init player
155
        player[0].score = 0;
156
        player[0].gameAreaStart = 0;
157
        player[0].gameAreaEnd = 43;
158
        player[0].sprite.owner = (player_t*)&player[0];
159
        player[0].mirrored = 0;
160
        setNewSprite((sprite_t*)&(player[0].sprite));
161

    
162
        while(1){
163
                for(uint8_t playerNumber = 0; playerNumber < NUMBER_OF_PLAYERS; playerNumber++){
164
                        for(uint8_t delay = 0; delay < TICKS; delay++){
165
                                drawGameArea((player_t*)&player[playerNumber]);
166
                                if(delay%20 == 0){
167
                                        handleGamepadInput((player_t*)&player[playerNumber], ((~PIN(D))&(0b11110000))>>4);
168
                                }
169
                        }
170
                        moveSpriteDown((sprite_t*)&(player[playerNumber].sprite));
171
                        clearCompleteLines((player_t*)&player[playerNumber]);
172
                        moveFlyingRowsDown((player_t*)&player[playerNumber]);
173
                }
174
        }
175
}
176

    
177
void setNewSprite(sprite_t* sprite){
178
        (*sprite).yLowerPos = (*((*sprite).owner)).gameAreaEnd-2;
179
        (*sprite).raw = pgm_read_word(bricks+(keypressCounter%(sizeof(bricks)/sizeof(uint16_t))));
180
        (*sprite).offset = 6;
181
        rawToBlock((uint16_t*)(*sprite).block, (*sprite).raw, (*sprite).offset);
182
        keypressCounter++;
183
}
184

    
185
void gameOver(player_t* player){ // TODO FIXME
186
        for(int16_t line = (*player).gameAreaStart; line < (*player).gameAreaEnd; line++){
187
                lines[line] = 0xffff;
188
                drawGameArea(player);
189
        }
190
        for(int16_t line = (*player).gameAreaEnd-1; line >= (*player).gameAreaStart; line--){
191
                lines[line] = 0x0000;
192
                drawGameArea(player);
193
        }
194
        (*player).score = 0;
195
        setNewSprite((sprite_t*)&((*player).sprite));
196
}
197

    
198
void drawScore(uint16_t* score, uint8_t round){
199
        if((*score) & (1<<round)){
200
                redDot();
201
        }
202
        else{
203
                noDot();
204
        }
205
}
206

    
207
static inline void drawGameArea(player_t* player){ // TODO needs changes for real multiplayer
208
        for(uint8_t round = 0; round < 16; round++){
209
                for(int16_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
210
                        printLine(line, round, (sprite_t*)&((*player).sprite));
211
                }
212
                drawScore((uint16_t*)&((*player).score), round);
213
                drawScore((uint16_t*)&((*player).score), round); // don't mirror the score, so it's easier to compare
214
                for(int16_t line = (*player).gameAreaEnd; line >= (*player).gameAreaStart; line--){
215
                        printLine(line, 15-round, (sprite_t*)&((*player).sprite));
216
                }
217
                OE();
218
                _delay_us(80);
219
                ODE();
220
                _delay_us(1); // avoid ghosting
221
                PORT(C) = round+1;
222
        }
223
}
224

    
225
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite){
226
        if((1<<round) & lines[line]){
227
                yellowDot();
228
        }
229
        else{
230
                if((*sprite).yLowerPos <= line && (*sprite).yLowerPos+4 > line){
231
                        if((1<<round) & (*sprite).block[line-(*sprite).yLowerPos]){
232
                                greenDot();
233
                        }
234
                        else{
235
                                noDot();
236
                        }
237
                }
238
                else{
239
                        noDot();
240
                }
241
        }
242
}
243

    
244
void handleGamepadInput(player_t* player, uint8_t input){
245
        switch(input){
246
                // move > rotate > down
247
                case (LEFT | DOWN | RIGHT | ROTATE):
248
                case (DOWN | ROTATE):
249
                        rotateLeft((sprite_t*)&((*player).sprite));
250
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
251
                        moveSpriteDown((sprite_t*)&((*player).sprite));
252
                        keypressCounter+=2;
253
                        break;
254
                case (DOWN):
255
                case (LEFT | DOWN | RIGHT):
256
                        moveSpriteDown((sprite_t*)&((*player).sprite));
257
                        keypressCounter+=1;
258
                        break;
259
                case (RIGHT):
260
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
261
                        keypressCounter+=1;
262
                        break;
263
                case (RIGHT | ROTATE):
264
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
265
                        keypressCounter+=1;
266
                case (ROTATE):
267
                case (LEFT | RIGHT | ROTATE):
268
                        rotateLeft((sprite_t*)&((*player).sprite));
269
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
270
                        keypressCounter+=1;
271
                        break;
272
                case (DOWN | RIGHT):
273
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
274
                        moveSpriteDown((sprite_t*)&((*player).sprite));
275
                        keypressCounter+=2;
276
                        break;
277
                case (DOWN | RIGHT | ROTATE):
278
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
279
                        rotateLeft((sprite_t*)&((*player).sprite));
280
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
281
                        moveSpriteDown((sprite_t*)&((*player).sprite));
282
                        keypressCounter+=3;
283
                        break;
284
                case (LEFT):
285
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
286
                        keypressCounter+=1;
287
                        break;
288
                case (LEFT | ROTATE):
289
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
290
                        rotateLeft((sprite_t*)&((*player).sprite));
291
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
292
                        keypressCounter+=2;
293
                        break;
294
                case (LEFT | DOWN):
295
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
296
                        moveSpriteDown((sprite_t*)&((*player).sprite));
297
                        keypressCounter+=2;
298
                        break;
299
                case (LEFT | DOWN | ROTATE):
300
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
301
                        rotateLeft((sprite_t*)&((*player).sprite));
302
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
303
                        moveSpriteDown((sprite_t*)&((*player).sprite));
304
                        keypressCounter+=3;
305
                        break;
306
                // "nops"
307
                case (LEFT | RIGHT): // left+right = no move
308
                        keypressCounter+=2;
309
                case (0b0000): // nothing pressed
310
                        break;
311
        }
312
}
313

    
314
void rawToBlock(uint16_t block[4], uint16_t raw, int8_t offset){
315
        block[0] = raw & 0xf;
316
        block[1] = (raw>>4) & 0xf;
317
        block[2] = (raw>>8) & 0xf;
318
        block[3] = (raw>>12) & 0xf;
319

    
320
        if(offset > 0){
321
                for(uint8_t i = 0; i < 4; i++){
322
                        block[i] = block[i]<<offset;
323
                }
324
        }
325
        else if(offset < 0){
326
                for(uint8_t i = 0; i < 4; i++){
327
                        block[i] = block[i]>>(offset*-1);
328
                }
329
        }
330
}
331

    
332
void clearCompleteLines(player_t* player){
333
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
334
                if(lines[line] == 0xffff){
335
                        lines[line] = 0x00;
336
                        (*player).score++;
337
                }
338
        }
339
}
340

    
341
void moveFlyingRowsDown(player_t* player){
342
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
343
                if(lines[line] == 0x0000){ // TODO maybe add clear effect with green line?
344
                        lines[line] = lines[line+1];
345
                        // blank line to propagate movement
346
                        lines[line+1] = 0x0000;
347
                }
348
        }
349
}
350

    
351
void moveSpriteToLines(sprite_t* sprite){
352
        for(uint8_t j = 0; j < 4; j++){ // copy sprite to lines
353
                lines[(*sprite).yLowerPos+j] |= (*sprite).block[j];
354
        }
355
}
356

    
357
uint8_t checkPlayerSpriteForCollision(player_t* player, uint16_t block[4], int8_t lookahead){
358
        for(int8_t i = 0; i < 4; i++){
359
                if((*player).sprite.yLowerPos+lookahead+i < (*player).gameAreaStart){
360
                        if((*player).sprite.block[i] == 0x0000){
361
                                continue;
362
                        }
363
                        else{
364
                                return 1;
365
                        }
366
                }
367
                else if((lines[(*player).sprite.yLowerPos+lookahead+i] & block[i]) != 0){ // collision detected
368
                        return 1;
369
                }
370
        }
371
        return 0;
372
}
373

    
374
void moveSpriteDown(sprite_t* sprite){
375
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, (uint16_t*)(*sprite).block, -1)){
376
                if((*sprite).yLowerPos >= (*(*sprite).owner).gameAreaEnd-4){
377
                        gameOver((player_t*)(*sprite).owner);
378
                        return;
379
                }
380
                moveSpriteToLines(sprite);
381
                setNewSprite(sprite);
382
                return;
383
        }
384
        (*sprite).yLowerPos--;
385
        if((*sprite).yLowerPos <= 0 && ((*sprite).block[-1*(*sprite).yLowerPos] != 0x00)){
386
                moveSpriteToLines(sprite);
387
                setNewSprite(sprite);
388
        }
389
}
390

    
391

    
392
void moveSpriteHorizontal(volatile sprite_t *sprite, int8_t dir){ // -1 if left, 1 if right
393
        uint16_t block[4];
394
        if(dir == 1){
395
                // check if block[*] >= 0x8000, so we cant move left
396
                for(uint8_t i = 0; i < 4; i++){
397
                        if((*sprite).block[i] >= (1<<15)){
398
                                return;
399
                        }
400
                }
401
                for(uint8_t i = 0; i < 4; i++){
402
                        block[i] = (*sprite).block[i] << 1;
403
                }
404
        }
405
        else{
406
                // check if block[*] is uneven, so we cant move right
407
                for(uint8_t i = 0; i < 4; i++){
408
                        if((*sprite).block[i] % 2){
409
                                return;
410
                        }
411
                }
412
                for(uint8_t i = 0; i < 4; i++){
413
                        block[i] = (*sprite).block[i] >> 1;
414
                }
415
        }
416
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
417
                return;
418
        }
419
        else{
420
                for(uint8_t i = 0; i < 4; i++){
421
                        (*sprite).block[i] = block[i];
422
                }
423
                (*sprite).offset = (*sprite).offset+dir;
424
        }
425
}
426

    
427
void loopLines(){
428
        uint8_t tmp = lines[0];
429
        for(uint8_t line = 0; line < 89 ; line++){
430
                lines[line] = lines[line+1];
431
        }
432
        lines[89] = tmp;
433
}
434

    
435
inline void greenDot(){
436
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN);
437
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN));
438
}
439
inline void redDot(){
440
        PORT(WALLBOARD_PORT) |= (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
441
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
442
}
443
inline void yellowDot(){
444
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
445
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
446
}
447
inline void noDot(){
448
        PORT(WALLBOARD_PORT) |= (1<<CLOCK_PIN);
449
        PORT(WALLBOARD_PORT) &= ~(1<<CLOCK_PIN);
450
}
451

    
452
inline void OE(){
453
        PORT(WALLBOARD_PORT) |= (1<<OUTPUT_ENABLE_PIN);
454
}
455

    
456
inline void ODE(){
457
        PORT(WALLBOARD_PORT) &= ~(1<<OUTPUT_ENABLE_PIN);
458
}
459

    
460
void rotateLeft(sprite_t* sprite){
461
        uint16_t block[4];
462
        uint16_t tmp = (*sprite).raw;
463

    
464
        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) );
465

    
466
        rawToBlock(block, tmp, (*sprite).offset);
467

    
468
        // check if sprite violates borders // TODO needs to be updated if game area doesn't span the whole line
469
        for(uint8_t i = 0; i < 4; i++){
470
                if((*sprite).offset < 0){
471
                        if(((block[i]<<((*sprite).offset*-1)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
472
                                return;
473
                        }
474
                }
475
                else if((*sprite).offset > 0){
476
                        if(((block[i]>>((*sprite).offset)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
477
                                return;
478
                        }
479
                }
480
                else{
481
                        break;
482
                }
483
        }
484

    
485
        // check first if rotated sprite crashes, if, drop to game
486
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
487
                return;
488
        }
489
        else{
490
                (*sprite).raw = tmp;
491
                for(uint8_t i = 0; i < 4; i++){
492
                        (*sprite).block[i] = block[i];
493
                }
494
        }
495
        // TODO rotate with ifs
496
//        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);
497
}