Statistics
| Branch: | Revision:

root / main.c @ a2d58d3e

History | View | Annotate | Download (14.2 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
// address lines - please note that the address lines have to be
23
// on the same port and in the current implementation are on 0-3
24
#define ADDRESS_PORT         C
25
#define A0                 PC0
26
#define A1                 PC1
27
#define A2                 PC2
28
#define A3                 PC3
29

    
30
#define CONTROLLER_PORT   D
31
#define CONTROLLER_LEFT   PD7
32
#define CONTROLLER_RIGHT  PD6
33
#define CONTROLLER_DOWN   PD5
34
#define CONTROLLER_ROTATE PD4
35

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

    
42
#define TICKS 40
43
#define NUMBER_OF_PLAYERS 1
44

    
45
struct player;
46

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

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

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

    
86
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 };
87
volatile player_t player[NUMBER_OF_PLAYERS];
88

    
89
const uint16_t bricks[] PROGMEM = { 
90
        /* +----+
91
         * |    |
92
         * | ## |
93
         * | ## |
94
         * |    |
95
         * +----+ */
96
        0b0000011001100000, 
97

    
98
        /* +----+
99
         * |    |
100
         * | #  |
101
         * | ## |
102
         * |  # |
103
         * +----+ */
104
        0b0000010001100010,
105

    
106
        /* +----+
107
         * |    |
108
         * |  # |
109
         * | ## |
110
         * | #  |
111
         * +----+ */
112
        0b0000001001100100, 
113

    
114
        /* +----+
115
         * |    |
116
         * | #  |
117
         * | ## |
118
         * | #  |
119
         * +----+ */
120
        0b0000010001100100, 
121

    
122
        /* +----+
123
         * | #  |
124
         * | #  |
125
         * | #  |
126
         * | #  |
127
         * +----+ */
128
        0b0100010001000100, 
129

    
130
        /* +----+
131
         * |    |
132
         * | #  |
133
         * | #  |
134
         * | ## |
135
         * +----+ */
136
        0b0000010001000110, 
137

    
138
        /* +----+
139
         * |    |
140
         * |  # |
141
         * |  # |
142
         * | ## |
143
         * +----+ */
144
        0b0000001000100110
145
};
146
volatile uint8_t keypressCounter = 0;
147

    
148
int main(){
149
        // hardware init
150
        // clock, data and output enable
151
        DDR(WALLBOARD_PORT) |= (1<<CLOCK_PIN) | (1<<DATA_RED_PIN) | (1<<DATA_GREEN_PIN) | (1<<OUTPUT_ENABLE_PIN);
152
        // gamepad (PD7 is for bootloadstart):
153
        // 7...4 left down right rotate
154
        //
155
        // move > rotate > down
156
        DDR(CONTROLLER_PORT) &= ~((1<<CONTROLLER_LEFT) | (1<<CONTROLLER_RIGHT) | (1<<CONTROLLER_DOWN) | (1<<CONTROLLER_ROTATE));
157
        PORT(CONTROLLER_PORT) |= (1<<CONTROLLER_LEFT) | (1<<CONTROLLER_RIGHT) | (1<<CONTROLLER_DOWN) | (1<<CONTROLLER_ROTATE);
158
        // address lines:
159
        DDR(ADDRESS_PORT) |= (1<<A3) | (1<<A2) | (1<<A1) | (1<<A0);
160

    
161
        // init player
162
        player[0].score = 0;
163
        player[0].gameAreaStart = 0;
164
        player[0].gameAreaEnd = 43;
165
        player[0].sprite.owner = (player_t*)&player[0];
166
        player[0].mirrored = 0;
167
        setNewSprite((sprite_t*)&(player[0].sprite));
168

    
169
        while(1){
170
                for(uint8_t playerNumber = 0; playerNumber < NUMBER_OF_PLAYERS; playerNumber++){
171
                        for(uint8_t delay = 0; delay < TICKS; delay++){
172
                                drawGameArea((player_t*)&player[playerNumber]);
173
                                if(delay%20 == 0){
174
                                        handleGamepadInput((player_t*)&player[playerNumber],
175
                                                                ((~PIN(CONTROLLER_PORT))&
176
                                                                        ( (1<<CONTROLLER_LEFT)
177
                                                                        | (1<<CONTROLLER_DOWN)
178
                                                                        | (1<<CONTROLLER_RIGHT)
179
                                                                        | (1<<CONTROLLER_ROTATE))
180
                                                                )>>4);
181
                                }
182
                        }
183
                        moveSpriteDown((sprite_t*)&(player[playerNumber].sprite));
184
                        clearCompleteLines((player_t*)&player[playerNumber]);
185
                        moveFlyingRowsDown((player_t*)&player[playerNumber]);
186
                }
187
        }
188
}
189

    
190
void setNewSprite(sprite_t* sprite){
191
        (*sprite).yLowerPos = (*((*sprite).owner)).gameAreaEnd-2;
192
        (*sprite).raw = pgm_read_word(bricks+(keypressCounter%(sizeof(bricks)/sizeof(uint16_t))));
193
        (*sprite).offset = 6;
194
        rawToBlock((uint16_t*)(*sprite).block, (*sprite).raw, (*sprite).offset);
195
        keypressCounter++;
196
}
197

    
198
void gameOver(player_t* player){ // TODO FIXME
199
        for(int16_t line = (*player).gameAreaStart; line < (*player).gameAreaEnd; line++){
200
                lines[line] = 0xffff;
201
                drawGameArea(player);
202
        }
203
        for(int16_t line = (*player).gameAreaEnd-1; line >= (*player).gameAreaStart; line--){
204
                lines[line] = 0x0000;
205
                drawGameArea(player);
206
        }
207
        (*player).score = 0;
208
        setNewSprite((sprite_t*)&((*player).sprite));
209
}
210

    
211
void drawScore(uint16_t* score, uint8_t round){
212
        if((*score) & (1<<round)){
213
                redDot();
214
        }
215
        else{
216
                noDot();
217
        }
218
}
219

    
220
static inline void drawGameArea(player_t* player){ // TODO needs changes for real multiplayer
221
        for(uint8_t round = 0; round < 16; round++){
222
                for(int16_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
223
                        printLine(line, round, (sprite_t*)&((*player).sprite));
224
                }
225
                drawScore((uint16_t*)&((*player).score), round);
226
                drawScore((uint16_t*)&((*player).score), round); // don't mirror the score, so it's easier to compare
227
                for(int16_t line = (*player).gameAreaEnd; line >= (*player).gameAreaStart; line--){
228
                        printLine(line, 15-round, (sprite_t*)&((*player).sprite));
229
                }
230
                OE();
231
                _delay_us(80);
232
                ODE();
233
                _delay_us(1); // avoid ghosting
234
                PORT(ADDRESS_PORT) = round+1;
235
        }
236
}
237

    
238
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite){
239
        if((1<<round) & lines[line]){
240
                yellowDot();
241
        }
242
        else{
243
                if((*sprite).yLowerPos <= line && (*sprite).yLowerPos+4 > line){
244
                        if((1<<round) & (*sprite).block[line-(*sprite).yLowerPos]){
245
                                greenDot();
246
                        }
247
                        else{
248
                                noDot();
249
                        }
250
                }
251
                else{
252
                        noDot();
253
                }
254
        }
255
}
256

    
257
void handleGamepadInput(player_t* player, uint8_t input){
258
        switch(input){
259
                // move > rotate > down
260
                case (LEFT | DOWN | RIGHT | ROTATE):
261
                        keypressCounter+=2;
262
                case (DOWN | ROTATE):
263
                        rotateLeft((sprite_t*)&((*player).sprite));
264
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
265
                        moveSpriteDown((sprite_t*)&((*player).sprite));
266
                        keypressCounter+=2;
267
                        break;
268
                case (LEFT | DOWN | RIGHT):
269
                        keypressCounter+=2;
270
                case (DOWN):
271
                        moveSpriteDown((sprite_t*)&((*player).sprite));
272
                        keypressCounter+=1;
273
                        break;
274
                case (RIGHT):
275
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
276
                        keypressCounter+=1;
277
                        break;
278
                case (LEFT | RIGHT | ROTATE):
279
                        keypressCounter+=1;
280
                case (RIGHT | ROTATE):
281
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
282
                        keypressCounter+=1;
283
                case (ROTATE):
284
                        rotateLeft((sprite_t*)&((*player).sprite));
285
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
286
                        keypressCounter+=1;
287
                        break;
288
                case (DOWN | RIGHT):
289
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
290
                        moveSpriteDown((sprite_t*)&((*player).sprite));
291
                        keypressCounter+=2;
292
                        break;
293
                case (DOWN | RIGHT | ROTATE):
294
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
295
                        rotateLeft((sprite_t*)&((*player).sprite));
296
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
297
                        moveSpriteDown((sprite_t*)&((*player).sprite));
298
                        keypressCounter+=3;
299
                        break;
300
                case (LEFT):
301
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
302
                        keypressCounter+=1;
303
                        break;
304
                case (LEFT | ROTATE):
305
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
306
                        rotateLeft((sprite_t*)&((*player).sprite));
307
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
308
                        keypressCounter+=2;
309
                        break;
310
                case (LEFT | DOWN):
311
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
312
                        moveSpriteDown((sprite_t*)&((*player).sprite));
313
                        keypressCounter+=2;
314
                        break;
315
                case (LEFT | DOWN | ROTATE):
316
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
317
                        rotateLeft((sprite_t*)&((*player).sprite));
318
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
319
                        moveSpriteDown((sprite_t*)&((*player).sprite));
320
                        keypressCounter+=3;
321
                        break;
322
                // "nops"
323
                case (LEFT | RIGHT): // left+right = no move
324
                        keypressCounter+=2;
325
                case (0b0000): // nothing pressed
326
                        break;
327
        }
328
}
329

    
330
void rawToBlock(uint16_t block[4], uint16_t raw, int8_t offset){
331
        block[0] = raw & 0xf;
332
        block[1] = (raw>>4) & 0xf;
333
        block[2] = (raw>>8) & 0xf;
334
        block[3] = (raw>>12) & 0xf;
335

    
336
        if(offset > 0){
337
                for(uint8_t i = 0; i < 4; i++){
338
                        block[i] = block[i]<<offset;
339
                }
340
        }
341
        else if(offset < 0){
342
                for(uint8_t i = 0; i < 4; i++){
343
                        block[i] = block[i]>>(offset*-1);
344
                }
345
        }
346
}
347

    
348
void clearCompleteLines(player_t* player){
349
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
350
                if(lines[line] == 0xffff){
351
                        lines[line] = 0x00;
352
                        (*player).score++;
353
                }
354
        }
355
}
356

    
357
void moveFlyingRowsDown(player_t* player){
358
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
359
                if(lines[line] == 0x0000){ // TODO maybe add clear effect with green line?
360
                        lines[line] = lines[line+1];
361
                        // blank line to propagate movement
362
                        lines[line+1] = 0x0000;
363
                }
364
        }
365
}
366

    
367
void moveSpriteToLines(sprite_t* sprite){
368
        for(uint8_t j = 0; j < 4; j++){ // copy sprite to lines
369
                lines[(*sprite).yLowerPos+j] |= (*sprite).block[j];
370
        }
371
}
372

    
373
uint8_t checkPlayerSpriteForCollision(player_t* player, uint16_t block[4], int8_t lookahead){
374
        for(int8_t i = 0; i < 4; i++){
375
                if((*player).sprite.yLowerPos+lookahead+i < (*player).gameAreaStart){
376
                        if((*player).sprite.block[i] == 0x0000){
377
                                continue;
378
                        }
379
                        else{
380
                                return 1;
381
                        }
382
                }
383
                else if((lines[(*player).sprite.yLowerPos+lookahead+i] & block[i]) != 0){ // collision detected
384
                        return 1;
385
                }
386
        }
387
        return 0;
388
}
389

    
390
void moveSpriteDown(sprite_t* sprite){
391
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, (uint16_t*)(*sprite).block, -1)){
392
                if((*sprite).yLowerPos >= (*(*sprite).owner).gameAreaEnd-4){
393
                        gameOver((player_t*)(*sprite).owner);
394
                        return;
395
                }
396
                moveSpriteToLines(sprite);
397
                setNewSprite(sprite);
398
                return;
399
        }
400
        (*sprite).yLowerPos--;
401
        if((*sprite).yLowerPos <= 0 && ((*sprite).block[-1*(*sprite).yLowerPos] != 0x00)){
402
                moveSpriteToLines(sprite);
403
                setNewSprite(sprite);
404
        }
405
}
406

    
407

    
408
void moveSpriteHorizontal(volatile sprite_t *sprite, int8_t dir){ // -1 if left, 1 if right
409
        uint16_t block[4];
410
        if(dir == 1){
411
                // check if block[*] >= 0x8000, so we cant move left
412
                for(uint8_t i = 0; i < 4; i++){
413
                        if((*sprite).block[i] >= (1<<15)){
414
                                return;
415
                        }
416
                }
417
                for(uint8_t i = 0; i < 4; i++){
418
                        block[i] = (*sprite).block[i] << 1;
419
                }
420
        }
421
        else{
422
                // check if block[*] is uneven, so we cant move right
423
                for(uint8_t i = 0; i < 4; i++){
424
                        if((*sprite).block[i] % 2){
425
                                return;
426
                        }
427
                }
428
                for(uint8_t i = 0; i < 4; i++){
429
                        block[i] = (*sprite).block[i] >> 1;
430
                }
431
        }
432
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
433
                return;
434
        }
435
        else{
436
                for(uint8_t i = 0; i < 4; i++){
437
                        (*sprite).block[i] = block[i];
438
                }
439
                (*sprite).offset = (*sprite).offset+dir;
440
        }
441
}
442

    
443
void loopLines(){
444
        uint8_t tmp = lines[0];
445
        for(uint8_t line = 0; line < 89 ; line++){
446
                lines[line] = lines[line+1];
447
        }
448
        lines[89] = tmp;
449
}
450

    
451
inline void greenDot(){
452
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN);
453
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN));
454
}
455
inline void redDot(){
456
        PORT(WALLBOARD_PORT) |= (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
457
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
458
}
459
inline void yellowDot(){
460
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
461
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
462
}
463
inline void noDot(){
464
        PORT(WALLBOARD_PORT) |= (1<<CLOCK_PIN);
465
        PORT(WALLBOARD_PORT) &= ~(1<<CLOCK_PIN);
466
}
467

    
468
inline void OE(){
469
        PORT(WALLBOARD_PORT) |= (1<<OUTPUT_ENABLE_PIN);
470
}
471

    
472
inline void ODE(){
473
        PORT(WALLBOARD_PORT) &= ~(1<<OUTPUT_ENABLE_PIN);
474
}
475

    
476
void rotateLeft(sprite_t* sprite){
477
        uint16_t block[4];
478
        uint16_t tmp = (*sprite).raw;
479

    
480
        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) );
481

    
482
        rawToBlock(block, tmp, (*sprite).offset);
483

    
484
        // check if sprite violates borders // TODO needs to be updated if game area doesn't span the whole line
485
        for(uint8_t i = 0; i < 4; i++){
486
                if((*sprite).offset < 0){
487
                        if(((block[i]<<((*sprite).offset*-1)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
488
                                return;
489
                        }
490
                }
491
                else if((*sprite).offset > 0){
492
                        if(((block[i]>>((*sprite).offset)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
493
                                return;
494
                        }
495
                }
496
                else{
497
                        break;
498
                }
499
        }
500

    
501
        // check first if rotated sprite crashes, if, drop to game
502
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
503
                return;
504
        }
505
        else{
506
                (*sprite).raw = tmp;
507
                for(uint8_t i = 0; i < 4; i++){
508
                        (*sprite).block[i] = block[i];
509
                }
510
        }
511
        // TODO rotate with ifs
512
//        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);
513
}