-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpictris.c
1585 lines (1426 loc) · 45.8 KB
/
pictris.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// v1.0
// - release of the source code. Tetris without any bells + whistles.
//
// v1.1
// - Added scorekeeping & highscores. The game will display the last high score at the start.
// (If your score beats the highscore, it will be saved to the EEPROM as the new high score.)
//
// v1.2
// - Sander says: Don't point your PIC at another variable: MPLAB® XC8 C Compiler User’s Guide 5.2.1
// ARRAY_SIZE removed because of this. This FIXED the randomness of the shape selection
// - Rerouted the displays to entire RA (rather than RA0-3 + RB4-7)
//
#ifdef SDCC // Include register vars
# include <pic18fregs.h>
#elif defined __XC8
# include <xc.h>
#else
# error make the proper include for the regs-defs
#endif
#include <stddef.h>
#include <stdint.h> // - for uint8_t/uint16_t/int16_t
#include <stdbool.h> // - for bool/true/false
#include <limits.h> // - for SHRT_MAX
#include "randgen.h" //for randomnumber
#include "memcpy.h" //for memcpy() and memcpyvol())
/// Software configuration
#undef ENABLE_SWDT
#define ENABLE_DEBOUNCE
#define ENABLE_MUXDISPLAY
#define ENABLE_INTWAIT
#define ENABLE_PICTRIS
#undef ENABLE_MARTIJN
#undef ENABLE_DOBBELSTEEN
#undef ENABLE_TEST_SHAPE
#define ENABLE_SANDER_ICD
#undef customRandom
#define snake
#define highscore
#define RANDOM2
typedef uint8_t byte;
//#define ARRAY_SIZE(a) ((sizeof a) * (sizeof a[0])) // Do not use: see MPLAB® XC8 C Compiler User’s Guide 5.2.1
// Bit manipulation
#define SBIT(reg,bit) reg |= (1<<bit) // Macro defined for Setting a bit of any register.
#define CBIT(reg,bit) reg &= ~(1<<bit) // Macro defined for Clearing a bit of any register.
#define GBIT(reg,bit) reg & (1<<bit) // Macro defined for Getting a bit of any register.
#define TBIT(reg,bit) reg ^= (1<<bit) // Macro defined for Toggling a bit of any register.
#define VBIT(reg,bit,v) reg = (reg & ~(1<<bit)) | ((v&1)<<bit) // Macro defined for setting a bit of any register to `v'
#include "shapes.h"
#include "numbers.h"
#include "EEPROM.h"
#ifdef SDCC // Configuration bits
#warning TODO: config here
#elif defined __XC8
// CONFIG1H
# pragma config OSC = INTIO67 // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7) == max. 8 MHz
# pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
# pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)
// CONFIG2L
# pragma config PWRT = OFF // Power-up Timer Enable bit (PWRT disabled)
# pragma config BOREN = SBORDIS // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
# pragma config BORV = 3 // Brown Out Reset Voltage bits (Minimum setting)
// CONFIG2H
# pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled)
# pragma config WDTPS = 32768 // Watchdog Timer Postscale Select bits (1:32768)
// CONFIG3H
# pragma config CCP2MX = PORTC // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
# pragma config PBADEN = OFF // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
# pragma config LPT1OSC = OFF // Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)
# pragma config MCLRE = ON // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)
// CONFIG4L
# pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
# pragma config LVP = OFF // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled) --> RB5 is I/O
# pragma config XINST = OFF // Extended Instruction Clear Enable bit (Instruction set extension and Indexed Addressing mode disabled)
// CONFIG5L
# pragma config CP0 = OFF // Code Protection bit (Block 0 (000800-001FFFh) not code-protected)
# pragma config CP1 = OFF // Code Protection bit (Block 1 (002000-003FFFh) not code-protected)
# pragma config CP2 = OFF // Code Protection bit (Block 2 (004000-005FFFh) not code-protected)
# pragma config CP3 = OFF // Code Protection bit (Block 3 (006000-007FFFh) not code-protected)
// CONFIG5H
# pragma config CPB = OFF // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
# pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected)
// CONFIG6L
# pragma config WRT0 = OFF // Write Protection bit (Block 0 (000800-001FFFh) not write-protected)
# pragma config WRT1 = OFF // Write Protection bit (Block 1 (002000-003FFFh) not write-protected)
# pragma config WRT2 = OFF // Write Protection bit (Block 2 (004000-005FFFh) not write-protected)
# pragma config WRT3 = OFF // Write Protection bit (Block 3 (006000-007FFFh) not write-protected)
// CONFIG6H
# pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
# pragma config WRTB = OFF // Boot Block Write Protection bit (Boot block (000000-0007FFh) not write-protected)
# pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected)
// CONFIG7L
# pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks)
# pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)
# pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)
# pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)
// CONFIG7H
# pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) not protected from table reads executed in other blocks)
#else
# error Make proper configuration here
#endif
// Used for timing
#define _XTAL_FREQ 8000000
// PROGRAM DEFINES
#ifdef ENABLE_MUXDISPLAY
volatile uint16_t ObjectData[8];
volatile uint16_t BackgroundData[8];
uint16_t tmpObjectData[8];
#endif
// button definitions
#ifdef ENABLE_MARTIJN
# define Rotate PORTBbits.RB0
# define Left PORTBbits.RB1
# define Right PORTBbits.RB2
# define Down PORTBbits.RB3
#else
# define Left PORTBbits.RB0
# define Right PORTBbits.RB1
# define Rotate PORTBbits.RB2
# define Down PORTBbits.RB3
#endif
// Should be there, but are not in SDCC-3.4.0 or in XC8
#define TMR0IF INTCONbits.TMR0IF
#define T0PS T0CONbits.T0PS
#define TMR2IF PIR1bits.TMR2IF
#define TMR2ON T2CONbits.TMR2ON
#define TMR2IE PIE1bits.TMR2IE
#define IDLEN OSCCONbits.IDLEN
#define SCS OSCCONbits.SCS
#define IRCF OSCCONbits.IRCF
#define RBPU INTCON2bits.RBPU
#define PCFG ADCON1bits.PCFG
#ifdef ENABLE_DEBOUNCE
volatile bool mtxButtons;
volatile
int16_t Left_Delay, Right_Delay, Rotate_Delay, Down_Delay;
volatile
bool Left_Debounced, Right_Debounced, Rotate_Debounced, Down_Debounced;
# define DEBOUNCE_DELAY 20 // 20 ms
# define REPETITION_DELAY 500 // 500 ms
#endif
#ifdef ENABLE_PICTRIS
byte LimitedRotation;
byte NumberOfLines;
byte LastHighScore;
bool IsRotated;
bool EndOfGame;
bool CheckForNewLines;
uint8_t countblocks;
volatile bool DropObject;
#endif
byte OriginX, OriginY;
// used for random graphic selection
#define RND_WINDOW (55/NUMBER_OF_SHAPES)
// TMR0 variables
volatile uint16_t mS; // mS register
#ifdef ENABLE_PICTRIS
// TEXT
const uint16_t TETRIS[] =
{
0x8841, // * * * *,
0xFBDF, // ***** **** *****,
0x8841, // * * * *,
0x0000, // ,
0x9BDF, // * ** **** *****,
0xA955, // * * * * * * * *,
0xCA91, // ** * * * * *,
0x0000, //
};
const uint16_t SNAKE[] =
{
0xFB97, // ***** *** * ***,
0xA955, // * * * * * * * *,
0xAB9D, // * * * *** *** *,
0x0000, // ,
0x03DF, // **** *****,
0xB881, // * *** * *,
0x035F, // ** * *****,
0x0000, //
};
typedef enum {
CCW,
CW
} rotation_t;
#endif
#ifdef snake
const uint16_t choose_screen[] =
{
0xFFFF, // ****************,
0x8181, // * ** *,
0xBD81, // * **** ** *,
0x858D, // * * ** ** *,
0x8599, // * * ** ** *,
0xAD81, // * * ** ** *,
0x8181, // * ** *,
0xFFFF, // ****************
};
#endif
#ifdef RANDOM2
uint8_t randomobjects[8];
#endif
typedef enum {
DOWN,
UP,
LEFT,
RIGHT
} direction_t;
typedef enum {
OVERRIDE,
MERGE,
INVERT
} mode_t;
#ifdef snake
// for every bit 1 turned on rest off i.e. 1000000000000000 = SnakeYtext[0]
const uint16_t SnakeYtext[] = {0x001,0x0002 ,0x0004 ,0x0008 ,0x0010 ,0x0020 ,0x0040 ,0x0080 ,0x0100 ,0x0200 ,0x0400 ,0x0800 ,0x1000 ,0x2000 ,0x4000 ,0x8000 };
//for array positions for snake for each piece of the snake an x and y value.
struct position{
uint8_t x;
uint8_t y;
};
uint8_t direction; //for direction of the snake 0=down 1=right 2=up 3=left
volatile bool moveSnake; //trigger to move the snake
struct position positions[100]; //array for every snake length (max length equals 100)
uint8_t snakeLength; //Length of snake;
//position of berry on screen
uint8_t berryX;
uint8_t berryY;
uint8_t previous_direction; //previous direction so as to not go back the same way
#endif
//game choosing variable
bool tetris;
#ifdef customRandom
int buttonPressed __at() ;
#endif
#define TMR0_RELOAD (~125)
#ifdef SDCC
__sfr16 __at (0x0F820F83) port_display;
#elif defined __XC8
extern volatile unsigned short port_display __at(0xF82);
asm("port_display equ 0F82h");
#else
# warning Make port_display happen on PORTC_ADDR & PORTD_ADDR
#endif
/// Should be there, but are not
// Taken from: http://www.geeksforgeeks.org/implement-itoa/
/* A utility function to swap two bytes */
void swap(char* a, char* b)
{
char t = *a;
*a = *b;
*b = t;
}
/* A utility function to reverse a string */
void reverse(char str[], int length)
{
int start = 0;
int end = length -1;
while (start < end)
{
swap(str+start, str+end);
start++;
end--;
}
}
char * itoa(int value, char * str, int bas)
{
int i = 0;
bool isNegative = false;
/* Handle 0 explicitely, otherwise empty string is printed for 0 */
if (value == 0)
{
str[i++] = '0';
str[i] = '\0';
return str;
}
// In standard itoa(), negative numbers are handled only with
// base 10. Otherwise numbers are considered unsigned.
if (value < 0 && bas == 10)
{
isNegative = true;
value = -value;
}
// Process individual digits
while (value != 0)
{
int rem = value % bas;
str[i++] = (rem > 9)? (rem-10) + 'a' : rem + '0';
value = value/bas;
}
// If number is negative, append '-'
if (isNegative)
str[i++] = '-';
str[i] = '\0'; // Append string terminator
// Reverse the string
reverse(str, i);
return str;
}
#ifdef ENABLE_DEBOUNCE
//clears trigger for checking buttons in interrupt
void pauseButtons()
{
mtxButtons = false;
}
//fills trigger for checking buttons in interrupt
void resumeButtons()
{
mtxButtons = true;
}
//makes button unuseable for DEBOUNCE_DELAY in ms
void debounceButton(volatile bool button, volatile int16_t *delay, volatile bool *debounced)
{
if (button == 0) // Button pressed
{
if (*delay < 0)
*delay = DEBOUNCE_DELAY;
else if ((*delay)-- == 0)
*debounced = true;
}
else
{
*delay = -1;
*debounced = false;
}
}
#endif
#ifdef ENABLE_SWDT
volatile byte sWDT; // Software Watch Dog Timer for entering SLEEP mode
void goSleep(void)
{
sWDT++;
if (sWDT == 30) // Gets reset in the mainGameLoop();
{
IDLEN = 1; // Put the PIC in RC_IDLE mode on SLEEP
SCS = 0x02; // Internal oscillator
DDRA = 0xFF; // Make all I/Os high impedance inputs
DDRB = 0xFF;
DDRC = 0xFF;
DDRD = 0xFF;
//DDRE?
TMR2ON = 0; // Disable TMR2
// Configure oscillator for 31Khz
IRCF = 0;
Sleep();
}
}
#endif // ENABLE_SWDT
#ifdef ENABLE_INTWAIT
volatile unsigned time;
// wait for time in ms
void waitms(unsigned t)
{
time = t;
while(time)
continue; // time is decremented in timer0interrupt
}
#endif
#ifdef ENABLE_MUXDISPLAY
volatile bool UpdateScreen;
volatile bool InterruptComplete;
// Loop until an interrupt occurs.
// Useful for semaphores - allows shared resources to be used with little (no) impact on timing
void waitForInterrupt(void)
{
InterruptComplete = false;
while (!InterruptComplete)
continue;
}
// This is the primary semaphore routine. If the program uses shared resources, this routine is called.
void pauseMultiplexing(void)
{
if (UpdateScreen)
{
waitForInterrupt();
UpdateScreen = false;
//port_display = 0; // blank the screen
}
}
// Enables multiplexing to resume
void resumeMultiplexing(void)
{
UpdateScreen = true;
}
#endif
#ifdef SDCC
void isr(void) __interrupt
#else
void __interrupt() isr(void)
#endif
{
if (TMR0IF)
{
TMR0IF = 0; // reset flag
TMR0 = TMR0_RELOAD; // set reload value to 1 ms (with internal 1MHz crystal)
#ifdef ENABLE_INTWAIT
time--; // variable for waitms()
#endif
if (++mS >= 1600)
{
mS = 0;
#ifdef ENABLE_SWDT
goSleep();
#endif
}
#ifdef ENABLE_DEBOUNCE
// Button Debounces
// Ensures the button has been activated before performing a debounce.
// Interrupt driven, the user has X mS between button presses.
// Prevents double presses and and minimize the chance of switch chatter.
// The sub routine 'CheckButtons' controls the state of various flags here.
// check if buttons require debounce
if (mtxButtons) // Is already claimed by the mainGameLoop()
{
debounceButton(Left, &Left_Delay, &Left_Debounced );
debounceButton(Right, &Right_Delay, &Right_Debounced );
debounceButton(Rotate, &Rotate_Delay, &Rotate_Debounced);
debounceButton(Down, &Down_Delay, &Down_Debounced );
}
#endif
#ifdef ENABLE_PICTRIS
// Event Handler - Object Drop
// Once every 800mS, a flag is set to initiate the next object drop.
// Once every 200mS, a flag is set to initiate the next snake move.
// This is one of few classic features of tetris
if (tetris == true && mS % 800 == 0){
DropObject = true;}
#ifdef snake
else if(mS % 200 == 0 ){
moveSnake = true;
}
#endif
#endif
#ifdef ENABLE_MUXDISPLAY
// Displays content on 64 LEDs via multiplexing
// Ensure multiplexing is enabled.
// This is vital to protect shared variables such as ObjectData and BackgroundData.
// Whenever a write to either is done outside of the interrupt, multiplexing should be disabled.
if (UpdateScreen)
{
static byte CurrentX = 0;
uint8_t xmask = 1 << CurrentX;
uint16_t ymask = ObjectData[CurrentX] | BackgroundData[CurrentX];
port_display = 0;
#ifdef ENABLE_SANDER_ICD
PORTA = xmask;
#else
PORTA = (PORTA & 0xf0) | (xmask & 0x0f);
PORTB = (PORTB & 0x0f) | (xmask & 0xf0);
#endif
port_display = ~ymask;
// increment x axis, ready for the next multiplex
CurrentX++;
CurrentX &= 0x07; // wrap around after 8 states
// a general purpose flag to inidcate the completion of an interrupt
//InterruptComplete = true; // uit de if gehaald op 10-12-2015 na debuggen
}
InterruptComplete = true;
#endif
}
}
// clears the passed array of type Word
void clearArray(volatile uint16_t *pArray, size_t size)
{
byte i;
for (i = 0; i < size; i++)
pArray[i] = 0;
}
#if defined ENABLE_PICTRIS | defined ENABLE_TEST_SHAPE
// select a random object based on the number of objects defined
void selectNextObject(volatile uint16_t *pTarget)
{
#ifdef RANDOM2
byte rndSelection, counter, selection;
struct shape * s;
bool check[NUMBER_OF_SHAPES];
if(countblocks == 7){
countblocks = 0;
// create a random number from 1-255
for(counter=0; counter<NUMBER_OF_SHAPES;counter++){
randomobjects[counter] = 255;
check[counter] = false;
}
for(counter=0; counter<NUMBER_OF_SHAPES;counter++){
bool fill = false;
do {
rndSelection = rnd_get_num();
rndSelection = rndSelection % NUMBER_OF_SHAPES;
if (check[rndSelection] == false) {
randomobjects[counter] = rndSelection;
check[rndSelection] = true;
true;
fill = true;
}
} while (!fill);
}
}
add_counter(7);
s = &shapes[randomobjects[countblocks]];
countblocks++;
#else
#ifdef customRandom
rndSelection = rnd_get_namm(buttonPressed);
#else
rndSelection = rnd_get_num();
add_counter(1);
#endif
// make a selection based on the random number created
selection = 0;
do
{
counter += RND_WINDOW;
selection++;
} while (counter < rndSelection);
//selection = rndSelection;
// initalise object variables
s = &shapes[selection % NUMBER_OF_SHAPES];
#endif
memcpyvol(pTarget, s->graphic, 16); // 16 = ARRAY_SIZE(s->graphic));
OriginX = s->x;
OriginY = s->y;
#ifdef ENABLE_PICTRIS
IsRotated = false;
LimitedRotation = s->limitedRotation;
#endif
mS = 0; // reset the timer
}
#endif
#ifdef snake
uint8_t genBerry(uint8_t size)
{
uint8_t temp;
temp = rnd_get_num();
add_counter(1);
temp = temp % size;
return temp;
}
void CreateBerry()
{
bool randomGood;
uint8_t i;
randomGood = 1;
do
{
randomGood = 0;
berryX = genBerry(8);
berryY = genBerry(16);
for(i=0;i<snakeLength;i++)
{
if (berryX == positions[i].x && berryY == positions[i].y)
{
randomGood = 1;
}
}
} while (randomGood == 1);
}
void SnakeGraph()
{
uint8_t i;
uint8_t j;
clearArray(tmpObjectData, 8);
for(i=0;i<=snakeLength;i++){
j = positions[i].x;
tmpObjectData[j] = (SnakeYtext[positions[i].y] | tmpObjectData[j]);
}
for(i=0;i<8;i++){
if (berryX == i){
tmpObjectData[i] = SnakeYtext[berryY] | tmpObjectData[i];
}
}
pauseMultiplexing();
clearArray(ObjectData, 8);
for(i=0;i<8;i++){
ObjectData[i] = tmpObjectData[i];
}
resumeMultiplexing();
}
void MoveSnakes()
{
uint8_t i;
switch (direction){
case 0:
positions[0].y = positions[0].y - 1;
previous_direction = 0;
if (positions[0].y >= 200){
positions[0].y = 15;
};
break;
case 1:
positions[0].x = positions[0].x + 1;
previous_direction = 1;
if (positions[0].x > 7 & positions[0].x < 200){
positions[0].x = 0;
};
break;
case 2:
positions[0].y = positions[0].y + 1;
previous_direction = 2;
if (positions[0].y > 15 & positions[0].y < 200) {
positions[0].y = 0;
};
break;
case 3:
positions[0].x = positions[0].x - 1;
previous_direction = 3;
if (positions[0].x >= 200){
positions[0].x = 7;
};
break;
};
for (i=0;i<=snakeLength;i++){
positions[(snakeLength - i)+1].y = positions[snakeLength-i].y;
positions[(snakeLength - i)+1].x = positions[snakeLength-i].x;
}
if (positions[0].y == berryY && positions[0].x == berryX){
snakeLength = snakeLength + 1;
CreateBerry();
}
for (i=4;i<=snakeLength;i++){
if (positions[0].y == positions[i].y && positions[0].x == positions[i].x){
EndOfGame = 1;
};
};
SnakeGraph();
}
#endif
// Merge two arrays with the selected mode
void mergeObjects(volatile uint16_t * pSource, volatile uint16_t * pTarget, mode_t mode)
{
byte i;
switch (mode)
{
case OVERRIDE:
for (i = 0; i < 8; i++)
pTarget[i] = pSource[i];
break;
case MERGE:
for (i = 0; i < 8; i++)
pTarget[i] |= pSource[i];
break;
case INVERT:
for (i = 0; i < 8; i++)
pTarget[i] ^= pSource[i];
break;
}
}
// Move an objects x axis in pDirection
void moveObject(volatile uint16_t * pObject, direction_t direction, byte cycles)
{
byte i, c;
switch (direction)
{
case DOWN:
for (c = 0; c < cycles; c++)
{
for (i = 0; i < 8; i++)
pObject[i] <<= 1;
OriginY++;
}
break;
case UP:
for (c = 0; c < cycles; c++)
{
for (i = 0; i < 8; i++)
pObject[i] >>= 1;
OriginY--;
}
break;
case RIGHT:
for (c = 0; c < cycles; c++)
{
for (i = 7; i > 0; i--)
pObject[i] = pObject[i-1];
pObject[0] = 0;
OriginX++;
}
break;
case LEFT:
for (c = 0; c < cycles; c++)
{
for (i = 0; i < 7; i++)
pObject[i] = pObject[i+1];
pObject[7] = 0;
OriginX--;
}
}
}
#ifdef ENABLE_PICTRIS
// check the passed object to see if it has reached the bottom
// return true if so
bool checkForBottom(volatile uint16_t * pObject)
{
for (byte i = 0; i < 8; i++)
if (GBIT(pObject[i], 15))
return true;
return false;
}
// check the passed object to see if it is against the left wall already
// return true if so
bool checkForLeftWall(volatile uint16_t * pObject)
{
return pObject[0] != 0;
}
// check the passed object to see if it is against the right wall already
// return true if so
bool checkForRightWall(volatile uint16_t * pObject)
{
return pObject[7] != 0;
}
// compare the passed source and target for a collision
// return true if so
bool collisionDetect(volatile uint16_t * pSource, volatile uint16_t * pTarget)
{
for (byte i = 0; i < 8; i++)
if (pSource[i] & pTarget[i]) // if two bits overlap, this is true
return true;
return false; // no bits overlap, no collision
}
// An extremely quick way to rotate objects +90/-90 degrees.
//
// Notes:
// Work out the centre of the block (to be used as a pivot point), i.e. the centre of the block shape. Call that (px, py).
// Each brick that makes up the block shape will rotate around that point. For each brick, you can apply the following calculation.
// Where each brick's width and height is q, the brick's current location (of the upper left corner) is (x1, y1) and the new brick location is (x2, y2):
// x2 = (y1 + px - py)
// y2 = (px + py - x1 - q)
// To rotate the opposite direction:
// x2 = (px + py - y1 - q)
// y2 = (x1 + py - px)
//
// Based on a 2D affine matrix transformation.
void newRotation(volatile uint16_t * pSource, uint16_t * pTarget, rotation_t rotation)
{
int8_t x2, y2;
byte x1, y1;
// check to see if rotations are disabled for current object
if (LimitedRotation == 2)
memcpyvol(pTarget, pSource, 16); // 16 = ARRAY_SIZE(pSource) = 8 * sizeof uint16_t
else
{
// clear the target array
clearArray(pTarget, 8);
// if the object is limited by rotation, then reverse
// rotations 1 & 3. This will make the object turn like so
// CW, CCW, CW, CCW
if (LimitedRotation == 1 && IsRotated)
rotation = CCW;
// analyse each pixel (working with 5x5 graphic)
for (x1 = 0; x1 < 8; x1++)
for (y1 = 0; y1 < 16; y1++)
{
if (GBIT(pSource[x1], y1))
{
if (rotation == CW)
{
x2 = OriginX + OriginY - y1;
y2 = x1 + OriginY - OriginX;
}
else
{
x2 = y1 + OriginX - OriginY;
y2 = OriginX + OriginY - x1;
}
if (x2 >= 0 && x2 < 8 &&
y2 >= 0 && y2 < 16)
SBIT(pTarget[x2], y2); //pDestinationObject(GetIndexY(Int(Y2)), GetIndexX(Int(X2))) = 1
}
}
}
}
// Count the number of pixels that equal "1". Standard bit-wise operators did not suit
// this routine as pixels could very well end up ANYWHERE after a rotation.
byte pixelCount(volatile uint16_t * pSource)
{
byte r = 0, x, y;
for (x = 0; x < 8; x++)
for (y = 0; y < 16; y++)
if (GBIT(pSource[x], y))
r++;
return r;
}
// Drop the object by one Y increment. Return FALSE if failed.
bool moveObjectDown(volatile uint16_t * pObject)
{
bool result = true;
#ifdef ENABLE_MUXDISPLAY
// Semaphore, to protect shared variables
pauseMultiplexing();
#endif
// check for a collision with the bottom of the screen
if (checkForBottom(pObject))
{
// ... yes it has - time to save it there
result = false;
// OR the object into the background
mergeObjects(pObject, BackgroundData, MERGE);
// get a new object up-and-running
selectNextObject(pObject);
// check for new lines
CheckForNewLines = true;
}
else
{
// Move the object down
moveObject(pObject, DOWN, 1);
// check for a collision with the background
if (collisionDetect(pObject, BackgroundData))
{
// the object HIT something in the background. The object needs to be
// moved BACK UP and saved to the background.
result = false;
// move the object up one
moveObject(pObject, UP, 1);
// move the object to the background (OR it over)
mergeObjects(pObject, BackgroundData, MERGE);
// get a new object up-and-running
selectNextObject(pObject);
// check if the object has collided with the background already. If yes, then
// the game is over.
EndOfGame = collisionDetect(pObject, BackgroundData);
// it's always possible that new lines may have been made already
CheckForNewLines = true;
}
}
mS = 0; // reset the timer because of the moveDown()
// resume multiplexing, shared resources are done with
#ifdef ENABLE_MUXDISPLAY
resumeMultiplexing();
#endif
return result;
}
// Check the state of each button. Because this routine is called often, the chances of missing a button press
// are very unlikely.
void checkButtons(void)
{
// check if left button pressed
if (Left_Debounced)
{
pauseButtons();
Left_Debounced = false;
Left_Delay = REPETITION_DELAY; // repetition delay
resumeButtons();
// Left_Delay = DEBOUNCE_DELAY;
// ensure the object is not ALREADY against the left wall
if (tetris == true){
if (!checkForLeftWall(ObjectData))
{
// move the object to a temporary buffer
//mergeObjects(ObjectData, tmpObjectData, OVERRIDE);
memcpyvol(tmpObjectData, ObjectData, 16);
// Decrement the x axis
moveObject(tmpObjectData, LEFT, 1);
// ensure no collisions with background
if (!collisionDetect(tmpObjectData, BackgroundData))
{
#ifdef ENABLE_MUXDISPLAY
// semaphore - shared variables are about to be accessed
pauseMultiplexing();
#endif
// all went well, merge the array to the working buffer
mergeObjects(tmpObjectData, ObjectData, OVERRIDE);// TODO: is moveObject(ObjectData) niet sneller
// enable the debounce timer (prevents double presses etc)
#ifdef ENABLE_MUXDISPLAY
resumeMultiplexing();
#endif
// always a chance that a new line was just made
CheckForNewLines = true;
}
}
}
#ifdef snake
else if (previous_direction != 1){ direction = 3 ;}
#endif
}
// check if the right button is pressed
// NOTE: THESE COMMENTS ARE MUCH THE SAME AS ABOVE, excluded for that reason
if (Right_Debounced)
{
pauseButtons();
Right_Debounced = false;
Right_Delay = REPETITION_DELAY;
resumeButtons();
// Right_Delay = DEBOUNCE_DELAY;
// ensure not already on the right wall
if (tetris == true) {
if (!checkForRightWall(ObjectData))
{
mergeObjects(ObjectData, tmpObjectData, OVERRIDE);
moveObject(tmpObjectData, RIGHT, 1);
// ensure no collisions with objects
if (!collisionDetect(tmpObjectData, BackgroundData))
{
#ifdef ENABLE_MUXDISPLAY
pauseMultiplexing();
#endif
mergeObjects(tmpObjectData, ObjectData, OVERRIDE);
#ifdef ENABLE_MUXDISPLAY
resumeMultiplexing();