Statistics
| Branch: | Revision:

root / main.c @ 517b1a45

History | View | Annotate | Download (14.6 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
#define TICKS 40
52
#define NUMBER_OF_PLAYERS 2
53

    
54
struct player;
55

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

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

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

    
97
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 };
98
volatile player_t player[NUMBER_OF_PLAYERS];
99

    
100
volatile uint8_t keypressCounter = 0;
101

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

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

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

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

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

    
179
static inline void drawGameArea(player_t player[NUMBER_OF_PLAYERS]){
180
        for(uint8_t round = 0; round < 16; round++){
181
                for(uint8_t playerNumber = 0; playerNumber < NUMBER_OF_PLAYERS; playerNumber++){
182
                        if(player[playerNumber].mirrored){
183
                                drawScore((uint16_t*)&(player[playerNumber].score), round); // don't mirror the score, so it's easier to compare
184
                                for(int16_t line = player[playerNumber].gameAreaEnd; line >= player[playerNumber].gameAreaStart; line--){
185
                                        printLine(line, 15-round, (sprite_t*)&(player[playerNumber].sprite));
186
                                }
187
                        }
188
                        else{
189
                                for(int16_t line = player[playerNumber].gameAreaStart; line <= player[playerNumber].gameAreaEnd; line++){
190
                                        printLine(line, round, (sprite_t*)&(player[playerNumber].sprite));
191
                                }
192
                                drawScore((uint16_t*)&(player[playerNumber].score), round);
193
                        }
194
                }
195
                OE();
196
                _delay_us(80);
197
                ODE();
198
                _delay_us(1); // avoid ghosting
199
                PORT(ADDRESS_PORT) = round+1;
200
        }
201
}
202

    
203
inline void printLine(int16_t line, uint8_t round, sprite_t* sprite){
204
        if((1<<round) & lines[line]){
205
                yellowDot();
206
        }
207
        else{
208
                if((*sprite).yLowerPos <= line && (*sprite).yLowerPos+4 > line){
209
                        if((1<<round) & (*sprite).block[line-(*sprite).yLowerPos]){
210
                                greenDot();
211
                        }
212
                        else{
213
                                noDot();
214
                        }
215
                }
216
                else{
217
                        noDot();
218
                }
219
        }
220
}
221

    
222
void handleGamepadInput(player_t* player, uint8_t input){
223
        switch(input){
224
                // move > rotate > down
225
                case (LEFT | DOWN | RIGHT | ROTATE):
226
                        keypressCounter+=2;
227
                case (DOWN | ROTATE):
228
                        rotateLeft((sprite_t*)&((*player).sprite));
229
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
230
                        moveSpriteDown((sprite_t*)&((*player).sprite));
231
                        keypressCounter+=2;
232
                        break;
233
                case (LEFT | DOWN | RIGHT):
234
                        keypressCounter+=2;
235
                case (DOWN):
236
                        moveSpriteDown((sprite_t*)&((*player).sprite));
237
                        keypressCounter+=1;
238
                        break;
239
                case (RIGHT):
240
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
241
                        keypressCounter+=1;
242
                        break;
243
                case (LEFT | RIGHT | ROTATE):
244
                        keypressCounter+=1;
245
                case (RIGHT | ROTATE):
246
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
247
                        keypressCounter+=1;
248
                case (ROTATE):
249
                        rotateLeft((sprite_t*)&((*player).sprite));
250
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
251
                        keypressCounter+=1;
252
                        break;
253
                case (DOWN | RIGHT):
254
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
255
                        moveSpriteDown((sprite_t*)&((*player).sprite));
256
                        keypressCounter+=2;
257
                        break;
258
                case (DOWN | RIGHT | ROTATE):
259
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), -1);
260
                        rotateLeft((sprite_t*)&((*player).sprite));
261
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
262
                        moveSpriteDown((sprite_t*)&((*player).sprite));
263
                        keypressCounter+=3;
264
                        break;
265
                case (LEFT):
266
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
267
                        keypressCounter+=1;
268
                        break;
269
                case (LEFT | ROTATE):
270
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
271
                        rotateLeft((sprite_t*)&((*player).sprite));
272
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
273
                        keypressCounter+=2;
274
                        break;
275
                case (LEFT | DOWN):
276
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
277
                        moveSpriteDown((sprite_t*)&((*player).sprite));
278
                        keypressCounter+=2;
279
                        break;
280
                case (LEFT | DOWN | ROTATE):
281
                        moveSpriteHorizontal((sprite_t*)&((*player).sprite), 1);
282
                        rotateLeft((sprite_t*)&((*player).sprite));
283
                        rawToBlock((uint16_t*)(*player).sprite.block, (*player).sprite.raw, (*player).sprite.offset);
284
                        moveSpriteDown((sprite_t*)&((*player).sprite));
285
                        keypressCounter+=3;
286
                        break;
287
                // "nops"
288
                case (LEFT | RIGHT): // left+right = no move
289
                        keypressCounter+=2;
290
                case (0b0000): // nothing pressed
291
                        break;
292
        }
293
}
294

    
295
void rawToBlock(uint16_t block[4], uint16_t raw, int8_t offset){
296
        block[0] = raw & 0xf;
297
        block[1] = (raw>>4) & 0xf;
298
        block[2] = (raw>>8) & 0xf;
299
        block[3] = (raw>>12) & 0xf;
300

    
301
        if(offset > 0){
302
                for(uint8_t i = 0; i < 4; i++){
303
                        block[i] = block[i]<<offset;
304
                }
305
        }
306
        else if(offset < 0){
307
                for(uint8_t i = 0; i < 4; i++){
308
                        block[i] = block[i]>>(offset*-1);
309
                }
310
        }
311
}
312

    
313
void clearCompleteLines(player_t* player){
314
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd; line++){
315
                if(lines[line] == 0xffff){
316
                        lines[line] = 0x00;
317
                        (*player).score++;
318
                }
319
        }
320
}
321

    
322
void moveFlyingRowsDown(player_t* player){
323
        for(uint8_t line = (*player).gameAreaStart; line <= (*player).gameAreaEnd-1; line++){
324
                if(lines[line] == 0x0000){ // TODO maybe add clear effect with green line?
325
                        lines[line] = lines[line+1];
326
                        // blank line to propagate movement
327
                        lines[line+1] = 0x0000;
328
                }
329
        }
330
}
331

    
332
void moveSpriteToLines(sprite_t* sprite){
333
        for(uint8_t j = 0; j < 4; j++){ // copy sprite to lines
334
                lines[(*sprite).yLowerPos+j] |= (*sprite).block[j];
335
        }
336
}
337

    
338
uint8_t checkPlayerSpriteForCollision(player_t* player, uint16_t block[4], int8_t lookahead){
339
        for(int8_t i = 0; i < 4; i++){
340
                if((*player).sprite.yLowerPos+lookahead+i < (*player).gameAreaStart){
341
                        if((*player).sprite.block[i] == 0x0000){
342
                                continue;
343
                        }
344
                        else{
345
                                return 1;
346
                        }
347
                }
348
                else if((lines[(*player).sprite.yLowerPos+lookahead+i] & block[i]) != 0){ // collision detected
349
                        return 1;
350
                }
351
        }
352
        return 0;
353
}
354

    
355
void moveSpriteDown(sprite_t* sprite){
356
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, (uint16_t*)(*sprite).block, -1)){
357
                if((*sprite).yLowerPos >= (*(*sprite).owner).gameAreaEnd-4){
358
                        gameOver((player_t*)(*sprite).owner);
359
                        return;
360
                }
361
                moveSpriteToLines(sprite);
362
                setNewSprite(sprite);
363
                return;
364
        }
365
        (*sprite).yLowerPos--;
366
        if((*sprite).yLowerPos <= 0 && ((*sprite).block[-1*(*sprite).yLowerPos] != 0x00)){
367
                moveSpriteToLines(sprite);
368
                setNewSprite(sprite);
369
        }
370
}
371

    
372

    
373
void moveSpriteHorizontal(volatile sprite_t *sprite, int8_t dir){ // -1 if left, 1 if right
374
        uint16_t block[4];
375
        if(dir == 1){
376
                // check if block[*] >= 0x8000, so we cant move left
377
                for(uint8_t i = 0; i < 4; i++){
378
                        if((*sprite).block[i] >= (1<<15)){
379
                                return;
380
                        }
381
                }
382
                for(uint8_t i = 0; i < 4; i++){
383
                        block[i] = (*sprite).block[i] << 1;
384
                }
385
        }
386
        else{
387
                // check if block[*] is uneven, so we cant move right
388
                for(uint8_t i = 0; i < 4; i++){
389
                        if((*sprite).block[i] % 2){
390
                                return;
391
                        }
392
                }
393
                for(uint8_t i = 0; i < 4; i++){
394
                        block[i] = (*sprite).block[i] >> 1;
395
                }
396
        }
397
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
398
                return;
399
        }
400
        else{
401
                for(uint8_t i = 0; i < 4; i++){
402
                        (*sprite).block[i] = block[i];
403
                }
404
                (*sprite).offset = (*sprite).offset+dir;
405
        }
406
}
407

    
408
void loopLines(){
409
        uint8_t tmp = lines[0];
410
        for(uint8_t line = 0; line < 89 ; line++){
411
                lines[line] = lines[line+1];
412
        }
413
        lines[89] = tmp;
414
}
415

    
416
inline void greenDot(){
417
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN);
418
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<CLOCK_PIN));
419
}
420
inline void redDot(){
421
        PORT(WALLBOARD_PORT) |= (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
422
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
423
}
424
inline void yellowDot(){
425
        PORT(WALLBOARD_PORT) |= (1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN);
426
        PORT(WALLBOARD_PORT) &= ~((1<<DATA_GREEN_PIN) | (1<<DATA_RED_PIN) | (1<<CLOCK_PIN));
427
}
428
inline void noDot(){
429
        PORT(WALLBOARD_PORT) |= (1<<CLOCK_PIN);
430
        PORT(WALLBOARD_PORT) &= ~(1<<CLOCK_PIN);
431
}
432

    
433
inline void OE(){
434
        PORT(WALLBOARD_PORT) |= (1<<OUTPUT_ENABLE_PIN);
435
}
436

    
437
inline void ODE(){
438
        PORT(WALLBOARD_PORT) &= ~(1<<OUTPUT_ENABLE_PIN);
439
}
440

    
441
void rotateLeft(sprite_t* sprite){
442
        uint16_t block[4];
443
        uint16_t tmp = (*sprite).raw;
444

    
445
        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) );
446

    
447
        rawToBlock(block, tmp, (*sprite).offset);
448

    
449
        // check if sprite violates borders // TODO needs to be updated if game area doesn't span the whole line
450
        for(uint8_t i = 0; i < 4; i++){
451
                if((*sprite).offset < 0){
452
                        if(((block[i]<<((*sprite).offset*-1)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
453
                                return;
454
                        }
455
                }
456
                else if((*sprite).offset > 0){
457
                        if(((block[i]>>((*sprite).offset)) ^ ((tmp&((0xf)<<(i*4)))>>(i*4))) != 0){
458
                                return;
459
                        }
460
                }
461
                else{
462
                        break;
463
                }
464
        }
465

    
466
        // check first if rotated sprite crashes, if, drop to game
467
        if(checkPlayerSpriteForCollision((player_t*)(*sprite).owner, block, 0)){
468
                return;
469
        }
470
        else{
471
                (*sprite).raw = tmp;
472
                for(uint8_t i = 0; i < 4; i++){
473
                        (*sprite).block[i] = block[i];
474
                }
475
        }
476
        // TODO rotate with ifs
477
//        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);
478
}