pacman from dindinx
[soft/lpc122x/tigp] / pacman / main.c
1 /****************************************************************************
2  *   apps/.*./main.c
3  *
4  * LPC1224/SSD1306 implementation of the famous pacman game
5  *
6  * Copyright 2017 Nathael Pajani <nathael.pajani@ed3l.fr>
7  *
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  *************************************************************************** */
24 #define ALLOW_SCREENSHOT
26 #include "core/pio.h"
27 #include "core/system.h"
28 #include "core/systick.h"
29 #include "lib/string.h"
30 #include "drivers/gpio.h"
31 #include "drivers/ssp.h"
32 #ifdef ALLOW_SCREENSHOT
33 #include "lib/stdio.h"
34 #include "drivers/serial.h"
35 #endif
37 #include "extdrv/status_led.h"
38 #include "extdrv/ssd130x_oled_driver.h"
39 #include "extdrv/ssd130x_oled_buffer.h"
41 #include "data.h"
43 #define MODULE_VERSION    0x01
44 #define MODULE_NAME "Pacman"
46 #define SELECTED_FREQ  FREQ_SEL_48MHz
48 /***************************************************************************** */
49 /* Pins configuration */
50 /* pins blocks are passed to set_pins() for pins configuration.
51  * Unused pin blocks can be removed safely with the corresponding set_pins() call
52  * All pins blocks may be safelly merged in a single block for single set_pins() call..
53  */
54 const struct pio_config common_pins[] = {
55 #ifdef ALLOW_SCREENSHOT
56         /* UART 0 : Config / Debug / USB */
57         { LPC_UART0_RX_PIO_0_1,  LPC_IO_DIGITAL },
58         { LPC_UART0_TX_PIO_0_2,  LPC_IO_DIGITAL },
59 #endif
60         /* SPI : Display */
61         { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
62         { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
63         { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
64         /* buttons */
65         { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
67         { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* User button B4 */
68         { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* User button B3 */
69         { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* User button B2 */
70         { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* User button B1 */
71         ARRAY_LAST_PIO,
72 };
74 const struct pio status_led_green = LPC_GPIO_0_27;
75 const struct pio status_led_red = LPC_GPIO_0_28;
77 /* Buttons */
78 const struct pio button_up    = LPC_GPIO_0_3; // B1
79 const struct pio button_down  = LPC_GPIO_0_6; // B4
80 const struct pio button_left  = LPC_GPIO_0_4; // B2
81 const struct pio button_right = LPC_GPIO_0_5; // B3
82 const struct pio button_ok    = LPC_GPIO_0_12; // BOK
84 /***************************************************************************** */
85 /* Basic system init and configuration */
87 void system_init()
88 {
89         /* Stop the Watchdog */
90         startup_watchdog_disable(); /* Do it right now, before it gets a chance to break in */
91         system_set_default_power_state();
92         clock_config(SELECTED_FREQ);
93         set_pins(common_pins);
94         gpio_on();
95         status_led_config(&status_led_green, &status_led_red);
96         /* System tick timer MUST be configured and running in order to use the sleeping
97          * functions */
98         systick_timer_on(1); /* 1ms */
99         systick_start();
102 /****************************************************************************/
103 /* Oled Display */
104 #define DISPLAY_ADDR   0x78
105 static uint8_t gddram[ 4 + GDDRAM_SIZE ];
106 struct oled_display display = {
107         .bus_type = SSD130x_BUS_SPI,
108         .address = DISPLAY_ADDR,
109         .bus_num = SSP_BUS_0,
110         .charge_pump = SSD130x_INTERNAL_PUMP,
111         .video_mode = SSD130x_DISP_NORMAL,
112         .contrast = 128,
113         .scan_dir = SSD130x_SCAN_BOTTOM_TOP,
114         .read_dir = SSD130x_RIGHT_TO_LEFT,
115         .display_offset_dir = SSD130x_MOVE_TOP,
116         .display_offset = 4,
117         .gddram = gddram,
118         .gpio_cs = LPC_GPIO_1_2,
119         .gpio_dc = LPC_GPIO_1_1,
120         .gpio_rst = LPC_GPIO_1_3,
122 };
124 #ifdef ALLOW_SCREENSHOT
125 static void data_rx(uint8_t __attribute((unused))c)
129 volatile int take_screenshot = 0;
130 static void set_screenshot(uint32_t __attribute__((unused))gpio)
132   take_screenshot = 1;
135 static void send_screenshot(void)
137   int y, x, xx;
138   uprintf(UART0, "P4\n128 64\n");
139   for (y = 0; y < 64; y++)
140     for (x = 0; x < 128; x += 8)
141     {
142       uint32_t pattern = 0;
143       for (xx = 0; xx < 8; xx++)
144       {
145         pattern <<= 1;
146         if ((gddram[4 + (y/8)*128 + x + xx] >> (y & 7)) & 1)
147         {
148           pattern |= 1;
149         }
150       }
151       uprintf(UART0, "%c", pattern);
152     }
153   uprintf(UART0, "\n");
155 #endif
157 /****************************************************************************/
159 enum GhostNames
161   GN_BLINKY,
162   GN_PINKY,
163   GN_INKY,
164   GN_CLYDE,
165   GN_NB_GHOSTS
166 };
168 enum GhostModes
170   GM_CORNERS,
171   GM_FRIGHTENED,
172   GM_CHASE,
173   GM_EATEN,
174   GM_WAITING,
175   GM_IN_PEN
176 };
178 static uint32_t current_pills[31];
180 struct Vec2
182   int x, y;
183 };
185 struct Ghost
187   struct Vec2 position;  /* */
188   struct Vec2 direction; /* */
189   struct Vec2 offset;    /* */
190   struct Vec2 target;    /* */
191   enum GhostModes mode;  /* */
192   int stopped;
193 };
195 struct Ghost ghosts[4] = 
197   {
198     { 16, 11 }, { 1, 0 }, { 0, 0 }, { 28, -2 }, GM_CORNERS, 0
199   },
200   {
201     { 13, 14 }, { 1, 0 }, { 0, 0 }, { 5, -2 }, GM_IN_PEN, 0
203   },
204   {
205     { 15, 14 }, { 1, 0 }, { 0, 0 }, { 5, 33 }, GM_IN_PEN, 0
207   },
208   {
209     { 17, 14 }, { 1, 0 }, { 0, 0 }, { 28, 33 }, GM_IN_PEN, 0
210   }
211 };
213 struct Pacman
215   struct Vec2 position;
216   struct Vec2 direction;
217   struct Vec2 sub_step;
218   struct Vec2 next_dir;
219   int shape, anim;
220 };
222 struct Pacman pacman =
224   { 15, 23 },
225   { 0, 0 },
226   { 0, 0 },
227   { 0, 0 },
228   F_PACMAN_LEFT_0, 1
229 };
231 static int screen_offset = 0;
232 static int current_frame = 0;
233 static uint8_t score[5];
234 static uint8_t ghost_score = 0;
236 static volatile uint8_t button_up_pressed = 0;
237 static volatile uint8_t button_down_pressed = 0;
238 static volatile uint8_t button_left_pressed = 0;
239 static volatile uint8_t button_right_pressed = 0;
241 static void handle_buttons(uint32_t gpio)
242 { // FIXME: ugly
243   if (gpio == button_up.pin)
244   {
245     if (gpio_read(button_up) == 0)
246       button_up_pressed = 1;
247     else
248       button_up_pressed = 0;
249   }
250   if (gpio == button_left.pin)
251   {
252     if (gpio_read(button_left) == 0)
253       button_left_pressed = 1;
254     else
255       button_left_pressed = 0;
256   }
257   if (gpio == button_down.pin)
258   {
259     if (gpio_read(button_down) == 0)
260       button_down_pressed = 1;
261     else
262       button_down_pressed = 0;
263   }
264   if (gpio == button_right.pin)
265   {
266     if (gpio_read(button_right) == 0)
267       button_right_pressed = 1;
268     else
269       button_right_pressed = 0;
270   }
273 /*==========================================================================*\
274  * my_rand                                                                  *
275 \*==========================================================================*/
276 static uint32_t my_rand()
277
278   static uint32_t x = 123456789;
279   static uint32_t y = 234567891;
280   static uint32_t z = 345678912;
281   static uint32_t w = 456789123;
282   static uint32_t c = 0;
283   int t;
285   y ^= (y << 5);
286   y ^= (y >> 7);
287   y ^= (y << 22); 
288   t = z + w + c;
289   z = w;
290   c = t < 0;
291   w = t & 2147483647; 
292   x += 1411392427; 
294   return x + y + w; 
297 /*==========================================================================*\
298  * draw_glyph                                                               *
299 \*==========================================================================*/
300 static void draw_glyph(int x, int y, uint8_t glyph)
302   int i;
303   for (i = 0; i < 8; i++)
304     gddram[4 + y * 128 + x + i] = font[glyph * 8 + i];
307 /*==========================================================================*\
308  * score_add_10000                                                          *
309 \*==========================================================================*/
310 static void score_add_10000(int digit)
312   score[1] += digit;
313   if (score[1] >9)
314   {
315     score[1] -= 10;
316     score[0]++;
317   }
320 /*==========================================================================*\
321  * score_add_1000                                                           *
322 \*==========================================================================*/
323 static void score_add_1000(int digit)
325   score[2] += digit;
326   if (score[2] > 9)
327   {
328     score[2] -= 10;
329     score_add_10000(1);
330   }
332 /*==========================================================================*\
333  * score_add_100                                                            *
334 \*==========================================================================*/
335 static void score_add_100(int digit)
337   score[3] += digit;
338   if (score[3] > 9)
339   {
340     score[3] -= 10;
341     score_add_1000(1);
342   }
345 /*==========================================================================*\
346  * score_add_10                                                             *
347 \*==========================================================================*/
348 static void score_add_10(int digit)
350   score[4] += digit;
351   if (score[4] > 9)
352   {
353     score[4] -= 10;
354     score_add_100(1);
355   }
358 /*==========================================================================*\
359  * draw_score                                                               *
360 \*==========================================================================*/
361 static void draw_score(void)
363   // Note this should be called last, so this can hide a ghost in the tunnel
364   draw_glyph(0, 0, F_BLANK);
365   draw_glyph(0, 1, F_DIGIT_0 + score[0]);
366   draw_glyph(0, 2, F_DIGIT_0 + score[1]);
367   draw_glyph(0, 3, F_DIGIT_0 + score[2]);
368   draw_glyph(0, 4, F_DIGIT_0 + score[3]);
369   draw_glyph(0, 5, F_DIGIT_0 + score[4]);
370   draw_glyph(0, 6, F_DIGIT_0);
371   draw_glyph(0, 7, F_BLANK);
373   // lifes left
374   draw_glyph(120, 0, F_BLANK);
375   draw_glyph(120, 1, F_PACMAN_RIGHT_1);
376   draw_glyph(120, 2, F_PACMAN_RIGHT_1);
377   draw_glyph(120, 3, F_PACMAN_RIGHT_1);
378   // fruits
379   draw_glyph(120, 4, F_BLANK);
380   draw_glyph(120, 5, F_CHERRY);
381   draw_glyph(120, 6, F_BLANK);
382   draw_glyph(120, 7, F_BLANK);
385 /*==========================================================================*\
386  * draw_sprite8x8                                                           *
387 \*==========================================================================*/
388 static void draw_sprite8x8(int x, int y, const uint8_t *sprite)
390   int i, offset = 4 + 128 * (y / 8) + x;
392   if (y >= 0 && y < 64)
393     for (i = 0; i < 8; i++)
394       gddram[offset + i] |= sprite[i] << (y & 7);
395   if (y >= -8 && y < 56)
396   {
397     offset += 128;
398     for (i = 0; i < 8; i++)
399       gddram[offset + i] |= sprite[i] >> (8 - (y & 7));
400   }
403 /*==========================================================================*\
404  * draw_sprite8x8_with_mask                                                 *
405 \*==========================================================================*/
406 static void draw_sprite8x8_with_mask(int x, int y,
407                                      const uint8_t *sprite,
408                                      const uint8_t *mask)
410   int i, offset = 4 + 128 * (y / 8) + x;
412   if (y >= 0 && y < 64)
413     for (i = 0; i < 8; i++)
414     {
415       gddram[offset + i] &= ~(mask[i] << (y & 7));
416       gddram[offset + i] |= sprite[i] << (y & 7);
417     }
418   if (y >= -8 && y < 56)
419   {
420     offset += 128; // on the next line...
421     for (i = 0; i < 8; i++)
422     {
423       gddram[offset + i] &= ~(mask[i] >> (8 - (y % 8)));
424       gddram[offset + i] |= sprite[i] >> (8 - (y % 8));
425     }
426   }
429 /*==========================================================================*\
430  * draw_pills                                                               *
431 \*==========================================================================*/
432 static void draw_pills(void)
433 { // FIXME: rewrite this mess!
434   int x, y;
435   for (y = 1; y < 31; y++) {
436     uint32_t line = current_pills[y];
437     if (line) {
438       for (x = 0; x < 32; x++)
439       if (line >> x & 1) {
440         int yp = 4 * y - screen_offset;
441         if ((x == 3 || x == 28) && (y == 3 || y == 23)) {
442           if (current_frame & 8) {
443             if (yp == -3) {
444               gddram[4 + x * 4 + 1] |= 0x01;
445               gddram[4 + x * 4 + 2] |= 0x01;
446             } else if (yp == -2) {
447               gddram[4 + x * 4 + 0] |= 0x01;
448               gddram[4 + x * 4 + 1] |= 0x03;
449               gddram[4 + x * 4 + 2] |= 0x03;
450               gddram[4 + x * 4 + 3] |= 0x01;
451             } else if (yp == -1) {
452               gddram[4 + x * 4 + 0] |= 0x03;
453               gddram[4 + x * 4 + 1] |= 0x07;
454               gddram[4 + x * 4 + 2] |= 0x07;
455               gddram[4 + x * 4 + 3] |= 0x03;
456             }
457             else if (yp >= 0 && yp < 64) {
458               int offset = 4 + 128 * (yp >> 3) + x * 4;
459               switch (yp & 7) {
460                 case 0: case 1: case 2: case 3: case 4:
461                   gddram[offset + 0] |= (0x06 << (yp & 7));
462                   gddram[offset + 1] |= (0x0F << (yp & 7));
463                   gddram[offset + 2] |= (0x0F << (yp & 7));
464                   gddram[offset + 3] |= (0x06 << (yp & 7));
465                   break;
466                 case 5:
467                   gddram[offset + 0] |= 0xC0;
468                   gddram[offset + 1] |= 0xE0;
469                   gddram[offset + 2] |= 0xE0;
470                   gddram[offset + 3] |= 0xC0;
471                   if (yp < 56) {
472                     gddram[offset + 128 + 1] |= 0x01;
473                     gddram[offset + 128 + 2] |= 0x01;
474                   }
475                   break;
476                 case 6:
477                   gddram[offset + 0] |= 0x80;
478                   gddram[offset + 1] |= 0xC0;
479                   gddram[offset + 2] |= 0xC0;
480                   gddram[offset + 3] |= 0x80;
481                   if (yp < 56) {
482                     gddram[offset + 128 + 0] |= 0x01;
483                     gddram[offset + 128 + 1] |= 0x03;
484                     gddram[offset + 128 + 2] |= 0x03;
485                     gddram[offset + 128 + 3] |= 0x01;
486                   }
487                   break;
488                 case 7:
489                   gddram[offset + 1] |= 0x80;
490                   gddram[offset + 2] |= 0x80;
491                   if (yp < 56) {
492                     gddram[offset + 128 + 0] |= 0x03;
493                     gddram[offset + 128 + 1] |= 0x07;
494                     gddram[offset + 128 + 2] |= 0x07;
495                     gddram[offset + 128 + 3] |= 0x03;
496                   }
497                   break;
498               }
499             }
500           }
501         }
502         else {
503           if (yp == -1) {
504             gddram[4 + x * 4 + 1] |= 0x03;
505             gddram[4 + x * 4 + 2] |= 0x03;
506           } else if (yp == -2) {
507             gddram[4 + x * 4 + 1] |= 0x01;
508             gddram[4 + x * 4 + 2] |= 0x01;
509           }
510           else if (yp >= 0 && yp < 64) {
511             int offset = 4 + 128 * (yp / 8) + x * 4;
512             switch (yp & 7)
513             {
514               case 0: case 1: case 2: case 3: case 4: case 5:
515                 gddram[offset + 1] |= (0x06 << (yp & 7));
516                 gddram[offset + 2] |= (0x06 << (yp & 7));
517                 break;
518               case 6:
519                 gddram[offset + 1] |= 0x80;
520                 gddram[offset + 2] |= 0x80;
521                 if (yp < 56)
522                 {
523                   gddram[offset + 128 + 1] |= 0x01;
524                   gddram[offset + 128 + 2] |= 0x01;
525                 }
526                 break;
527               case 7:
528                 if (yp < 56) {
529                   gddram[offset + 128 + 1] |= 0x03;
530                   gddram[offset + 128 + 2] |= 0x03;
531                 }
532                 break;
533             }
534           }
535         }
536       }
537     }
538   }
541 /*==========================================================================*\
542  * move_ghosts                                                              *
543 \*==========================================================================*/
544 static void move_ghosts(void)
546   enum Direction { LEFT, RIGHT, UP, DOWN };
547   int checkxdir[4] = { -1,1,0,0 };
548   int checkydir[4] = { 0,0,-1,1 };
550   int i;
552   for (i = 0; i < 4; i++)
553   {
554     if (ghosts[i].mode == GM_EATEN)
555     {
556       ghosts[i].offset.x = ghosts[i].offset.y = 0;
557       ghosts[i].position.x += ghosts[i].direction.x;
558       ghosts[i].position.y += ghosts[i].direction.y;
559     } else
560     {
561       if (ghosts[i].mode == GM_FRIGHTENED)
562       { // pause every 4 moves
563         if (ghosts[i].stopped == 4)
564         {
565           ghosts[i].stopped = 0;
566           continue;
567         } else ghosts[i].stopped++;
568       }
569       ghosts[i].offset.x += ghosts[i].direction.x;
570       ghosts[i].offset.y += ghosts[i].direction.y;
571     }
573     if (ghosts[i].offset.x >= 4 || ghosts[i].offset.x <= -4) {
574       ghosts[i].offset.x = 0;
575       ghosts[i].position.x += ghosts[i].direction.x;
576     }
577     if (ghosts[i].offset.y >= 4 || ghosts[i].offset.y <= -4) {
578       ghosts[i].offset.y = 0;
579       ghosts[i].position.y += ghosts[i].direction.y;
580     }
582     if (ghosts[i].offset.y == 0 && ghosts[i].offset.x == 0)
583     {
584       int u, a = 9999, b = -1;
586       for (u = 0; u < 4; u++)
587       {
588         int forbidden = walls_mask[ghosts[i].position.y+checkydir[u]] >> (ghosts[i].position.x+checkxdir[u]) & 1;
589         // U turns are forbidden by game rules
590         if (ghosts[i].direction.x == -checkxdir[u] &&
591             ghosts[i].direction.y == -checkydir[u])
592           forbidden = 1;
593         // Ghosts are not allowed reenter the pen (unless eaten)
594         if (u == 3 &&
595             (ghosts[i].position.x == 15 || ghosts[i].position.x == 16) &&
596             ghosts[i].position.y == 11 &&
597             ghosts[i].mode != GM_EATEN)
598           forbidden = 1;
599         // compute (squared) distance to target
600         int dx = ghosts[i].position.x + checkxdir[u];
601         dx = ghosts[i].target.x - dx;
602         int dy = ghosts[i].position.y + checkydir[u];
603         dy = ghosts[i].target.y - dy;
604         int distance = dx * dx + dy * dy;
605         // Store the minimal distance
606         if (!forbidden && distance <= a)
607         {
608           a = distance;
609           b = u;
610         }
611       }
612       ghosts[i].direction.x = checkxdir[b];
613       ghosts[i].direction.y = checkydir[b];
614     }
615     // Resurect!
616     if (ghosts[i].position.x == 15 && ghosts[i].position.y == 13)
617       ghosts[i].mode = GM_IN_PEN;
618     // out of pen
619     if (ghosts[i].mode == GM_IN_PEN && ghosts[i].position.y == 11)
620       ghosts[i].mode = GM_CORNERS;
621     // handle tunnel for ghosts
622     if (ghosts[i].position.x == 0 && ghosts[i].direction.x == -1)
623       ghosts[i].position.x = 31;
624     if (ghosts[i].position.x == 31 && ghosts[i].direction.x == 1)
625       ghosts[i].position.x = 0;
626   }
629 /*==========================================================================*\
630  * draw_ghosts                                                              *
631 \*==========================================================================*/
632 static void draw_ghosts(void)
634   int i;
635   for (i = 0; i < 4; i++)
636   {
637     enum FontEntries ghost_sprite = F_DIGIT_0;
638     switch (ghosts[i].mode)
639     {
640       case GM_EATEN:
641         ghost_sprite = F_GHOST_EYES;
642         break;
643       case GM_FRIGHTENED:
644         ghost_sprite = F_GHOST_FRIGHTENED;
645         break;
646       case GM_CORNERS:
647       case GM_CHASE:
648       case GM_IN_PEN:
649         if (ghosts[i].direction.x == -1) ghost_sprite = F_GHOST_LEFT;
650         if (ghosts[i].direction.x == 1) ghost_sprite = F_GHOST_RIGHT;
651         if (ghosts[i].direction.y == -1) ghost_sprite = F_GHOST_UP;
652         if (ghosts[i].direction.y == 1) ghost_sprite = F_GHOST_DOWN;
653         break;
654       case GM_WAITING:
655         ghost_sprite = F_GHOST_STILL;
656         break;
658     }
659     draw_sprite8x8_with_mask(ghosts[i].position.x * 4 + ghosts[i].offset.x - 1,
660                              ghosts[i].position.y * 4 + ghosts[i].offset.y - 1 - screen_offset,
661                              &font[ghost_sprite * 8],
662                              &font[F_GHOST_MASK * 8]);
663   }
666 /*==========================================================================*\
667  * move_pacman                                                              *
668 \*==========================================================================*/
669 static void move_pacman(void)
671   if (pacman.direction.x || pacman.direction.y)
672   {
673     // animation
674     if (current_frame & 1)
675       pacman.anim = (pacman.anim + 1) & 3;
676     if (pacman.direction.x == -1) pacman.shape = F_PACMAN_LEFT_0;
677     if (pacman.direction.x ==  1) pacman.shape = F_PACMAN_RIGHT_0;
678     if (pacman.direction.y == -1) pacman.shape = F_PACMAN_UP_0;
679     if (pacman.direction.y ==  1) pacman.shape = F_PACMAN_DOWN_0;
680     
681     // sub steps moves
682     pacman.sub_step.x += pacman.direction.x;
683     pacman.sub_step.y += pacman.direction.y;
684   }
685   if (pacman.sub_step.x >= 4 || pacman.sub_step.x <= -4)
686   {
687     pacman.sub_step.x = 0;
688     pacman.position.x += pacman.direction.x;
689   }
690   if (pacman.sub_step.y >= 4 || pacman.sub_step.y <= -4)
691   {
692     pacman.sub_step.y = 0;
693     pacman.position.y += pacman.direction.y;
694   }
695   // if in full tile
696   if (pacman.sub_step.x == 0 && pacman.sub_step.y == 0)
697   {
698     int x = pacman.position.x, y = pacman.position.y;
699     if (current_pills[y] >> x & 1) // hum, this should be 31-x (or not)
700     {
701       current_pills[y] -= 1 << x; // ditto
702       // remaining_dots--;
703       if ((x == 3 || x == 28) && (y == 3 || y == 23))
704       {
705         ghosts[GN_BLINKY].mode = GM_FRIGHTENED;
706         ghosts[GN_BLINKY].stopped = 0;
707         ghosts[GN_PINKY].mode = GM_FRIGHTENED;
708         ghosts[GN_PINKY].stopped = 0;
709         ghosts[GN_INKY].mode = GM_FRIGHTENED;
710         ghosts[GN_INKY].stopped = 0;
711         ghosts[GN_CLYDE].mode = GM_FRIGHTENED;
712         ghosts[GN_CLYDE].stopped = 0;
713         score_add_10(5); // energizers worth 50 points
714         ghost_score = 1;
715       }
716       else score_add_10(1); // normal pills are worth 10 points
717     }
719     // Where can we go next ?
720     x = pacman.position.x + pacman.next_dir.x;
721     y = pacman.position.y + pacman.next_dir.y;
723     if (!(walls_mask[y] >> x & 1))
724     {
725       pacman.direction.x = pacman.next_dir.x;
726       pacman.direction.y = pacman.next_dir.y;
727     }
728     x = pacman.position.x + pacman.direction.x;
729     y = pacman.position.y + pacman.direction.y;
730     if (walls_mask[y] >> x & 1)
731     {
732       pacman.direction.x = 0;
733       pacman.direction.y = 0;
734     }
735     // handle tunnel for pacman (ghosts are way slower in this)
736     if (pacman.position.x == 1 && pacman.direction.x == -1)
737       pacman.position.x = 30;
738     if (pacman.position.x == 30 && pacman.direction.x == 1)
739       pacman.position.x = 1;
740   }
743 /*==========================================================================*\
744  * draw_pacman                                                              *
745 \*==========================================================================*/
746 static void draw_pacman(void)
748   draw_sprite8x8(pacman.position.x * 4 + pacman.sub_step.x - 2,
749                  pacman.position.y * 4 + pacman.sub_step.y - 2 - screen_offset,
750                  font + 8 * (pacman.shape + pacman.anim));
753 /*==========================================================================*\
754  * draw_walls                                                               *
755 \*==========================================================================*/
756 static void draw_walls(int screen_offset)
758 #include "image.h0"
759 #include "image.h1"
760 #include "image.h2"
761 #include "image.h3"
762 #include "image.h4"
763 #include "image.h5"
764 #include "image.h6"
765 #include "image.h7"
766   const uint8_t *backgrounds[8] =
767   {
768     image0, image1, image2, image3, image4, image5, image6, image7
769   };
770   const uint16_t *offsets[8] =
771   {
772     image_offsets0, image_offsets1, image_offsets2, image_offsets3,
773     image_offsets4, image_offsets5, image_offsets6, image_offsets7
774   };
775   uncompress_image(backgrounds[screen_offset & 7] +
776                    offsets[screen_offset & 7][screen_offset >> 3],
777                    gddram + 4);
780 /*==========================================================================*\
781  * draw_sprite_16x16                                                        *
782 \*==========================================================================*/
783 static void draw_sprite_16x16(int x, int y, const uint8_t *sprite)
785   int i, offset = 4 + 128 * y + x;
786   int start = 0;
787   int end = 16;
788   if (x < 0) start = -x;
789   if (x > 112) end = 128 - x;
790   for (i = start; i < end; i++)
791     gddram[offset + i] |= sprite[i];
792   for (i = start; i < end; i++)
793     gddram[offset + i + 128] |= sprite[i + 16];
796 /*==========================================================================*\
797  * draw_sprite_16x16_reverse                                                *
798 \*==========================================================================*/
799 static void draw_sprite_16x16_reverse(int x, int y, const uint8_t *sprite)
801   int i, offset = 4 + 128 * y + x;
802   int start = 0;
803   int end = 16;
804   if (x < 0) start = -x;
805   if (x > 112) end = 128 - x;
806   for (i = start; i < end; i++)
807     gddram[offset + i] |= sprite[15 - i];
808   for (i = start; i < end; i++)
809     gddram[offset + i + 128] |= sprite[31 - i];
812 /*==========================================================================*\
813  * update_screen                                                            *
814 \*==========================================================================*/
815 static void update_screen(void)
817 #ifdef ALLOW_SCREENSHOT
818   if (take_screenshot)
819   {
820     send_screenshot();
821     //      take_screenshot = 0;
822   }
823 #endif
824   ssd130x_display_full_screen(&display);
827 /*==========================================================================*\
828  * draw_sprite_16x16_with_mask                                              *
829 \*==========================================================================*/
830 static void draw_sprite_16x16_with_mask(int x, int y,
831                                         const uint8_t *sprite,
832                                         const uint8_t *mask)
834   int i, offset = 4 + 128 * y + x;
836   for (i = 0; i < 16; i++)
837   {
838     gddram[offset + i] &= ~mask[i];
839     gddram[offset + i] |= sprite[i];
840   }
841   offset = 4 + 128 * (y + 1) + x;
842   for (i = 0; i < 16; i++)
843   {
844     gddram[offset + i] &= ~mask[16 + i];
845     gddram[offset + i] |= sprite[16 + i];
846   }
849 /*==========================================================================*\
850  * show_intermission2                                                       *
851 \*==========================================================================*/
852 static void show_intermission2(void)
854   int frame = 0;
855   while (1)
856   {
857     ssd130x_buffer_set(gddram, 0xff);
858     int anim = (frame / 2) % 8;
859     switch (anim)
860     {
861       case 0: // 1
862         draw_sprite_16x16_with_mask(50, 4,
863                                     pacmania_se_01, pacmania_se_01_mask);
864         break;
865       case 1: case 7: // 2
866         draw_sprite_16x16_with_mask(50, 4,
867                                     pacmania_se_02, pacmania_se_02_mask);
868         break;
869       case 2: case 6: // 3
870         draw_sprite_16x16_with_mask(50, 4,
871                                     pacmania_se_03, pacmania_se_03_mask);
872         break;
873       case 3: case 4: // 4
874         draw_sprite_16x16_with_mask(50, 4,
875                                     pacmania_se_04, pacmania_se_04_mask);
876         break;
877       case 5:
878         draw_sprite_16x16_with_mask(50, 4,
879                                     pacmania_se_05, pacmania_se_05_mask);
880         break;
881     }
882     frame++;
883     //
884     update_screen();
885     //
886     if (button_right_pressed || button_left_pressed ||
887         button_up_pressed || button_down_pressed)
888       return;
889   }
892 /*==========================================================================*\
893  * show_intermission                                                        *
894 \*==========================================================================*/
895 static void show_intermission(void)
897 #include "intermission-background.h"
898   int frame = 0, stop = 0;
899   int pac_x = -10;
900   int ghost1 = pac_x - 26;
901   int ghost2 = ghost1 - 18;
902   int ghost3 = ghost2 - 18;
903   int ghost4 = ghost3 - 18;
904   int sequence = 0;
905   int ghost_eaten = 0;
906   while (1)
907   {
908     const uint8_t *ghost_sprite;
909     //const uint8_t *pacman_sprite;
910     uncompress_image(background_intermission, gddram + 4);
912     switch (sequence)
913     {
914       case 0: // pacman is chased by the ghost
915         ghost_sprite = frame & 2 ? big_ghost_0 : big_ghost_1;
916         switch ((frame / 2) & 3)
917         {
918           case 0: draw_sprite_16x16(pac_x, 4, big_pacman_0); break;
919           case 1: draw_sprite_16x16(pac_x, 4, big_pacman_1); break;
920           case 2: draw_sprite_16x16(pac_x, 4, big_pacman_2); break;
921           case 3: draw_sprite_16x16(pac_x, 4, big_pacman_1); break;
922         }
923         draw_sprite_16x16(ghost1, 4, ghost_sprite);
924         draw_sprite_16x16(ghost2, 4, ghost_sprite);
925         draw_sprite_16x16(ghost3, 4, ghost_sprite);
926         draw_sprite_16x16(ghost4, 4, ghost_sprite);
927         frame++;
928         if (frame & 15) pac_x++;
929         ghost1++;
930         ghost2 = ghost1 - 18;
931         ghost3 = ghost2 - 18;
932         ghost4 = ghost3 - 18;
933         if (pac_x == 256) {
934           sequence++;
935           frame = 0;
936           //pac_x = -10;
937           //ghost1 = pac_x - 26;
938         }
939         break;
940       case 1: // Pacman eats an energizer : screen is flashing
941         ssd130x_buffer_set(gddram, 0xFF);
942         frame++;
943         if (frame == 3) {
944           sequence++;
945           frame = 0;
946           ghost1 = 130;
947           ghost2 = ghost1 + 18;
948           ghost3 = ghost2 + 18;
949           ghost4 = ghost3 + 18;
950           pac_x = ghost4 + 80;
951         }
952         break;
953       case 2: // The ghost are going back left, pacman eating them
954         ghost_sprite = frame & 2 ? big_frightened_ghost_0 : big_frightened_ghost_1;
955         if (stop >= 0)
956         switch ((frame / 2) & 3)
957         {
958           case 0: draw_sprite_16x16_reverse(pac_x, 4, big_pacman_0); break;
959           case 1: draw_sprite_16x16_reverse(pac_x, 4, big_pacman_1); break;
960           case 2: draw_sprite_16x16_reverse(pac_x, 4, big_pacman_2); break;
961           case 3: draw_sprite_16x16_reverse(pac_x, 4, big_pacman_1); break;
962         }
963         draw_sprite_16x16(ghost1, 4, ghost_sprite);
964         draw_sprite_16x16(ghost2, 4, ghost_sprite);
965         draw_sprite_16x16(ghost3, 4, ghost_sprite);
966         draw_sprite_16x16(ghost4, 4, ghost_sprite);
967         if (stop >= 0)
968         {
969           frame++;
970           ghost1--;
971           ghost2--;
972           ghost3--;
973           ghost4--;
974           pac_x -= 2;
975         }
976         else
977         {
978           int i = 0;
979           switch (ghost_eaten)
980           {
981             case 1:
982               gddram[4 + 128 * 4 + pac_x + i++] = 0xf2;
983               gddram[4 + 128 * 4 + pac_x + i++] = 0x92;
984               gddram[4 + 128 * 4 + pac_x + i++] = 0x92;
985               gddram[4 + 128 * 4 + pac_x + i++] = 0x9e;
986               break;
987             case 2:
988               gddram[4 + 128 * 4 + pac_x + i++] = 0x1e;
989               gddram[4 + 128 * 4 + pac_x + i++] = 0x10;
990               gddram[4 + 128 * 4 + pac_x + i++] = 0x10;
991               gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
992               break;
993             case 3:
994               gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
995               gddram[4 + 128 * 4 + pac_x + i++] = 0x92;
996               gddram[4 + 128 * 4 + pac_x + i++] = 0x92;
997               gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
998               break;
999             case 4:
1000               gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
1001               i++;
1002               gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
1003               gddram[4 + 128 * 4 + pac_x + i++] = 0x92;
1004               gddram[4 + 128 * 4 + pac_x + i++] = 0x92;
1005               gddram[4 + 128 * 4 + pac_x + i++] = 0xf2;
1006               break;
1007           }
1008           i++;
1009           gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
1010           gddram[4 + 128 * 4 + pac_x + i++] = 0x82;
1011           gddram[4 + 128 * 4 + pac_x + i++] = 0x82;
1012           gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
1013           i++;
1014           gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
1015           gddram[4 + 128 * 4 + pac_x + i++] = 0x82;
1016           gddram[4 + 128 * 4 + pac_x + i++] = 0x82;
1017           gddram[4 + 128 * 4 + pac_x + i++] = 0xfe;
1019         }
1020         stop ++;
1021         if (pac_x - ghost4 < 6) { stop = -10; ghost4 = -100; ghost_eaten++; }
1022         if (pac_x - ghost3 < 6) { stop = -10; ghost3 = -100; ghost_eaten++; }
1023         if (pac_x - ghost2 < 6) { stop = -10; ghost2 = -100; ghost_eaten++; }
1024         if (pac_x - ghost1 < 6) { stop = -10; ghost1 = -100; ghost_eaten++; }
1025         if (pac_x < 0)
1026         {
1027           sequence = 0;
1028           frame = 0; stop = 0;
1029           pac_x = -64;
1030           ghost1 = pac_x - 26;
1031           ghost2 = ghost1 - 18;
1032           ghost3 = ghost2 - 18;
1033           ghost4 = ghost3 - 18;
1034           ghost_eaten = 0;
1035         }
1036         break;
1037     }
1038     //
1039     update_screen();
1040     //
1041     if (button_right_pressed || button_left_pressed ||
1042         button_up_pressed || button_down_pressed)
1043       return;
1044   }
1047 /****************************************************************************/
1048 int main(void)
1050         system_init();
1051 #ifdef ALLOW_SCREENSHOT
1052         uart_on(UART0, 1152000, data_rx);
1053 #endif
1054         ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
1056   set_gpio_callback(handle_buttons, &button_up, EDGES_BOTH);
1057   set_gpio_callback(handle_buttons, &button_down, EDGES_BOTH);
1058   set_gpio_callback(handle_buttons, &button_left, EDGES_BOTH);
1059   set_gpio_callback(handle_buttons, &button_right, EDGES_BOTH);
1061 #ifdef ALLOW_SCREENSHOT
1062   set_gpio_callback(set_screenshot, &button_ok, EDGE_FALLING);
1063 #endif
1065         status_led(green_only);
1067         /* Configure and start display */
1068         ssd130x_display_on(&display);
1070   show_intermission2();
1072   memcpy(current_pills, all_pills, sizeof all_pills);
1074         while (1) {
1075     screen_offset = 4 * (pacman.position.y - 8) + pacman.sub_step.y;
1076     if (screen_offset < 0) screen_offset = 0;
1077     if (screen_offset > 64) screen_offset = 64;
1079     draw_walls(screen_offset);
1080     draw_pills();
1082     if (button_up_pressed)
1083     {
1084       pacman.next_dir.x = 0;
1085       pacman.next_dir.y = -1;
1086     }
1087     if (button_down_pressed)
1088     {
1089       pacman.next_dir.x = 0;
1090       pacman.next_dir.y = 1;
1091     }
1092     if (button_left_pressed)
1093     {
1094       pacman.next_dir.x = -1;
1095       pacman.next_dir.y = 0;
1096     }
1097     if (button_right_pressed)
1098     {
1099       pacman.next_dir.x = 1;
1100       pacman.next_dir.y = 0;
1101     }
1102     move_ghosts();
1103     draw_ghosts();
1104     move_pacman();
1105     draw_pacman();
1106     draw_score();
1107 #ifdef ALLOW_SCREENSHOT
1108     if (take_screenshot)
1109     {
1110       send_screenshot();
1111 //      take_screenshot = 0;
1112     }
1113 #endif
1114     ssd130x_display_full_screen(&display);
1115 //    msleep(100);
1117     int i;
1118     int px = pacman.position.x * 4 + pacman.sub_step.x;
1119     int py = pacman.position.y * 4 + pacman.sub_step.y;
1120     for (i = 0; i < 4; i++)
1121     {
1122       int gx = ghosts[i].position.x * 4 + ghosts[i].offset.y;
1123       int gy = ghosts[i].position.y * 4 + ghosts[i].offset.y;
1124       if (px <= gx + 2 && px >= gx - 2 &&
1125           py <= gy + 2 && py >= gy - 2)
1126       {
1127         if (ghosts[i].mode == GM_FRIGHTENED)
1128         {
1129           int s;
1130           for (s = 0; s < ghost_score; s++)
1131             score_add_100(2);
1132           ghost_score *= 2;
1133           ghosts[i].mode = GM_EATEN;
1134         }
1135       }
1136     }
1138     for (i = 0; i < GN_NB_GHOSTS; i++)
1139       switch (ghosts[i].mode)
1140       {
1141         case GM_EATEN:
1142           ghosts[i].target.x = 15;
1143           ghosts[i].target.y = 13;
1144           break;
1145         case GM_CORNERS: // FIXME! ugly
1146           if (i == GN_BLINKY)
1147           {
1148             ghosts[0].target.x = 28; ghosts[0].target.y = -2;
1149           }
1150           else if (i == GN_PINKY)
1151           {
1152             ghosts[1].target.x = 5; ghosts[1].target.y = -2;
1153           }
1154           else if (i == GN_INKY)
1155           {
1156             ghosts[2].target.x = 0; ghosts[2].target.y = 31;
1157           }
1158           else
1159           {
1160             ghosts[3].target.x = 31; ghosts[3].target.y = 31;
1161           }
1162           break;
1163         case GM_FRIGHTENED:
1164           ghosts[i].target.x = my_rand() & 31;
1165           ghosts[i].target.y = my_rand() & 31;
1166           break;
1167         case GM_CHASE:
1168           break;
1169         case GM_IN_PEN:
1170           ghosts[i].target.x = 16;
1171           ghosts[i].target.y = 11;
1172           break;
1173         case GM_WAITING:
1174           break;
1175       }
1176     current_frame++;
1177     
1178     static unsigned tt; 
1179     unsigned t = systick_get_tick_count();
1180     static unsigned fps = 0;
1181     ++fps;
1182     if (t > tt) {
1183 //      uprintf(UART0, "%u ", fps);
1184       fps = 0;
1185       tt += 1000;
1186     }
1187         }
1188         return 0;