Statistics
| Branch: | Revision:

root / main.c @ f8259022

History | View | Annotate | Download (15.2 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
// note that both controllers are on the same port, the reason is
33
// controllerOffset for faster input handling
34
#define CONTROLLER0_PORT   D
35
#define CONTROLLER0_LEFT   PD7
36
#define CONTROLLER0_RIGHT  PD6
37
#define CONTROLLER0_DOWN   PD5
38
#define CONTROLLER0_ROTATE PD4
39
#define CONTROLLER1_PORT   D
40
#define CONTROLLER1_LEFT   PD3
41
#define CONTROLLER1_RIGHT  PD2
42
#define CONTROLLER1_DOWN   PD1
43
#define CONTROLLER1_ROTATE PD0
44

    
45
// control mappings for atari joystick
46
#define LEFT   0b0001
47
#define DOWN   0b0100
48
#define RIGHT  0b0010
49
#define ROTATE 0b1000
50

    
51
// note NOGAMEAREA needs has to be one pixel bigger
52
// because we are lazy in moveSpriteHorizontal (forward check)
53
#define NOGAMEAREA 0b111111111
54
#define TICKS 40
55
#define NUMBER_OF_PLAYERS 2
56

    
57
struct player;
58

    
59
typedef struct sprite {
60
        volatile int8_t offset;
61
        volatile int8_t yLowerPos;
62
        volatile uint16_t block[4];
63
        volatile uint16_t raw;
64
        volatile struct player *owner;
65
        } sprite_t;
66

    
67
typedef struct player {
68
        volatile uint16_t score;
69
        volatile int8_t gameAreaStart;
70
        volatile int8_t gameAreaEnd;
71
        volatile sprite_t sprite;
72
        volatile uint8_t mirrored;
73
        volatile uint8_t controllerOffset;
74
        volatile uint8_t controllerInputMask;
75
        } player_t;
76

    
77
int main(void);
78
inline void redDot(void);
79
inline void greenDot(void);
80
inline void yellowDot(void);
81
inline void noDot(void);
82
inline void OE(void);
83
inline void ODE(void);
84
void loopLines(void);
85
void rotateLeft(sprite_t* sprite);
86
void moveFlyingRowsDown(player_t* player);
87
void moveSpriteDown(sprite_t* sprite);
88
void clearCompleteLines(player_t* player);
89
void rawToBlock(uint16_t block[4], uint16_t raw, int8_t offset);
90
void moveSpriteToLines(sprite_t* sprite);
91
uint8_t checkPlayerSpriteForCollision(player_t* player, uint16_t block[4], int8_t lookahead); // lookahead -1 -> look down
92
void moveSpriteHorizontal(volatile sprite_t *sprite, int8_t dir); // -1 if left, 1 if right
93
void handleGamepadInput(player_t* player, uint8_t input);
94
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite);
95
static inline void drawGameArea(player_t player[NUMBER_OF_PLAYERS]);
96
void gameOver(player_t* player);
97
void setNewSprite(sprite_t* sprite);
98
void drawScore(uint16_t* score, uint8_t round);
99

    
100
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 };
101
volatile player_t player[NUMBER_OF_PLAYERS];
102

    
103
volatile uint8_t keypressCounter = 0;
104

    
105
int main(){
106
        // hardware init
107
        // clock, data and output enable
108
        DDR(WALLBOARD_PORT) |= (1<<CLOCK_PIN) | (1<<DATA_RED_PIN) | (1<<DATA_GREEN_PIN) | (1<<OUTPUT_ENABLE_PIN);
109
        // gamepad (PD7 is for bootloadstart):
110
        // 7...4 left down right rotate
111
        //
112
        // move > rotate > down
113
        DDR(CONTROLLER0_PORT) &= ~((1<<CONTROLLER0_LEFT) | (1<<CONTROLLER0_RIGHT) | (1<<CONTROLLER0_DOWN) | (1<<CONTROLLER0_ROTATE));
114
        PORT(CONTROLLER0_PORT) |= (1<<CONTROLLER0_LEFT) | (1<<CONTROLLER0_RIGHT) | (1<<CONTROLLER0_DOWN) | (1<<CONTROLLER0_ROTATE);
115
        DDR(CONTROLLER1_PORT) &= ~((1<<CONTROLLER1_LEFT) | (1<<CONTROLLER1_RIGHT) | (1<<CONTROLLER1_DOWN) | (1<<CONTROLLER1_ROTATE));
116
        PORT(CONTROLLER1_PORT) |= (1<<CONTROLLER1_LEFT) | (1<<CONTROLLER1_RIGHT) | (1<<CONTROLLER1_DOWN) | (1<<CONTROLLER1_ROTATE);
117
        // address lines:
118
        DDR(ADDRESS_PORT) |= (1<<A3) | (1<<A2) | (1<<A1) | (1<<A0);
119

    
120
        // init player
121
        player[0].score = 0;
122
        player[0].gameAreaStart = 0;
123
        player[0].gameAreaEnd = 17;
124
        player[0].sprite.owner = (player_t*)&player[0];
125
        setNewSprite((sprite_t*)&(player[0].sprite));
126
        player[0].mirrored = 0;
127
        player[0].controllerInputMask = (1<<CONTROLLER0_LEFT) | (1<<CONTROLLER0_DOWN) | (1<<CONTROLLER0_RIGHT) | (1<<CONTROLLER0_ROTATE);
128
        player[0].controllerOffset = 4;
129
        // next one
130
        player[1].score = 0;
131
        player[1].gameAreaStart = 26;
132
        player[1].gameAreaEnd = 44;
133
        player[1].sprite.owner = (player_t*)&player[1];
134
        setNewSprite((sprite_t*)&(player[1].sprite));
135
        player[1].mirrored = 1;
136
        player[1].controllerInputMask = (1<<CONTROLLER1_LEFT) | (1<<CONTROLLER1_DOWN) | (1<<CONTROLLER1_RIGHT) | (1<<CONTROLLER1_ROTATE);
137
        player[1].controllerOffset = 0;
138
        while(1){
139
                for(uint8_t delay = 0; delay < TICKS; delay++){
140
                        drawGameArea((player_t*)player);
141
                        if(delay%(TICKS/(NUMBER_OF_PLAYERS)) == 0){
142
                                for(uint8_t playerNumber = 0; playerNumber < NUMBER_OF_PLAYERS; playerNumber++){ //fair handing of input
143
                                        handleGamepadInput((player_t*)&player[playerNumber],
144
                                                                ((~PIN(CONTROLLER0_PORT))& player[playerNumber].controllerInputMask)
145
                                                                        >>player[playerNumber].controllerOffset);
146
                                }
147
                        }
148
                }
149
                for(uint8_t playerNumber = 0; playerNumber < NUMBER_OF_PLAYERS; playerNumber++){
150
                        moveSpriteDown((sprite_t*)&(player[playerNumber].sprite));
151
                        clearCompleteLines((player_t*)&player[playerNumber]);
152
                        moveFlyingRowsDown((player_t*)&player[playerNumber]);
153
                }
154
        }
155
}
156

    
157
void setNewSprite(sprite_t* sprite){
158
        (*sprite).yLowerPos = (*((*sprite).owner)).gameAreaEnd-2;
159
        (*sprite).raw = pgm_read_word(bricks+(keypressCounter%(sizeof(bricks)/sizeof(uint16_t))));
160
        (*sprite).offset = 10;
161
        rawToBlock((uint16_t*)(*sprite).block, (*sprite).raw, (*sprite).offset);
162
        keypressCounter++;
163
}
164

    
165
void gameOver(player_t* player){
166
        for(int16_t line = (*player).gameAreaEnd; line >= (*player).gameAreaStart; line--){
167
                lines[line] = 0x0000;
168
        }
169
        (*player).score = 0;
170
        setNewSprite((sprite_t*)&((*player).sprite));
171
}
172

    
173
void drawScore(uint16_t* score, uint8_t round){
174
        if((*score) & (1<<round)){
175
                redDot();
176
        }
177
        else{
178
                noDot();
179
        }
180
}
181

    
182
inline void printYellowBar(void){
183
        yellowDot();
184
}
185

    
186
inline void printGreenBar(void){
187
        greenDot();
188
}
189

    
190
inline void printRedBar(void){
191
        redDot();
192
}
193

    
194
inline void printEmptyBar(void){
195
        noDot();
196
}
197

    
198
static inline void drawGameArea(player_t player[NUMBER_OF_PLAYERS]){
199
        PORT(ADDRESS_PORT) = 0;
200
        for(uint8_t round = 0; round < 16; /*round++*/){
201
                for(int16_t line = player[1].gameAreaStart; line <= player[1].gameAreaEnd; line++){
202
                        printLine(line, 15-round/2, (sprite_t*)&(player[1].sprite));
203
                }
204
                printGreenBar();
205
                for(int16_t line = 0; line < 7 ; line++){ //player[1].gameAreaEnd; line < player[0].gameAreaStart; line--){
206
                        //printLine(line, 15-round, (sprite_t*)&(player[1].sprite));
207
                        if(round == 5){
208
                                printYellowBar();
209
                        }
210
                        else{
211
                                printEmptyBar();
212
                        }
213
                }
214
                printYellowBar();
215
                for(int16_t line = 0; line < 7 ; line++){
216
                        if(round == 10){
217
                                printYellowBar();
218
                        }
219
                        else{
220
                                printEmptyBar();
221
                        }
222
                }
223
                printRedBar();
224
                for(int16_t line = player[0].gameAreaEnd; line >= player[0].gameAreaStart; line--){
225
                        printLine(line, 15-round/2, (sprite_t*)&(player[0].sprite));
226
                }
227

    
228
                OE();
229
                _delay_us(80);
230
                ODE();
231
                round++;
232
                PORT(ADDRESS_PORT) = round;
233
                _delay_us(10); // avoid ghosting
234
        }
235
}
236

    
237
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite){
238
        if((1<<round) & lines[line]){
239
                yellowDot();
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
                                greenDot();
247
                        }
248
                        else{
249
                                noDot();
250
                                noDot();
251
                        }
252
                }
253
                else{
254
                        noDot();
255
                        noDot();
256
                }
257
        }
258
}
259

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

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

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

    
351
void clearCompleteLines(player_t* player){
352
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
353
                if((lines[line] & (0xffff & ~(NOGAMEAREA>>1))) == (0xffff & ~(NOGAMEAREA>>1))){
354
                        lines[line] ^= (0xffff & ~(NOGAMEAREA>>1));
355
                        (*player).score++;
356
                }
357
        }
358
}
359

    
360
void moveFlyingRowsDown(player_t* player){ // TODO needs fix becase we don't want to touch nogamearea
361
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd-1; line++){
362
                if(lines[line] == 0x0000){ // TODO maybe add clear effect with green line?
363
                        lines[line] = lines[line+1];
364
                        // blank line to propagate movement
365
                        lines[line+1] = 0x0000;
366
                }
367
        }
368
}
369

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

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

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

    
410

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

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

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

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

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

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

    
482
        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) );
483

    
484
        rawToBlock(block, tmp, (*sprite).offset);
485

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

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