asteroids.ino 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. #include <SPI.h>
  2. #include <GD.h>
  3. // ----------------------------------------------------------------------
  4. // qrand: quick random numbers
  5. // ----------------------------------------------------------------------
  6. static uint16_t lfsr = 1;
  7. static void qrandSeed(int seed)
  8. {
  9. if (seed) {
  10. lfsr = seed;
  11. } else {
  12. lfsr = 0x947;
  13. }
  14. }
  15. static byte qrand1() // a random bit
  16. {
  17. lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & 0xb400);
  18. return lfsr & 1;
  19. }
  20. static byte qrand(byte n) // n random bits
  21. {
  22. byte r = 0;
  23. while (n--)
  24. r = (r << 1) | qrand1();
  25. return r;
  26. }
  27. // ----------------------------------------------------------------------
  28. // controller: buttons on Arduino pins 3,4,5,6
  29. // ----------------------------------------------------------------------
  30. static void controller_init()
  31. {
  32. // Configure input pins with internal pullups
  33. byte i;
  34. for (i = 3; i < 7; i++) {
  35. pinMode(i, INPUT);
  36. digitalWrite(i, HIGH);
  37. }
  38. }
  39. #define CONTROL_LEFT 1
  40. #define CONTROL_RIGHT 2
  41. #define CONTROL_UP 4
  42. #define CONTROL_DOWN 8
  43. static int screenshot_i; // JCB
  44. #define PLAYBACK 0 // read controls from Serial - JCB
  45. #define SCREENSHOT 0 // JCB
  46. static byte controller_sense(uint16_t clock)
  47. {
  48. byte r = 0;
  49. #if PLAYBACK // JCB
  50. Serial.write(0x50); // request key // JCB
  51. Serial.write(lowByte(clock)); // JCB
  52. Serial.write(highByte(clock)); // JCB
  53. while (!Serial.available()) // JCB
  54. ; // JCB
  55. r = Serial.read(); // JCB
  56. return r; // JCB
  57. #endif // JCB
  58. if (!digitalRead(5))
  59. r |= CONTROL_DOWN;
  60. if (!digitalRead(4))
  61. r |= CONTROL_UP;
  62. if (!digitalRead(6))
  63. r |= CONTROL_LEFT;
  64. if (!digitalRead(3))
  65. r |= CONTROL_RIGHT;
  66. #if PLAYBACK // JCB
  67. Serial.write(lowByte(clock)); // JCB
  68. Serial.write(r); // JCB
  69. #endif // JCB
  70. return r;
  71. }
  72. // Swap color's red and blue channels
  73. uint16_t swapRB(uint16_t color)
  74. {
  75. byte r = 31 & (color >> 10);
  76. byte g = 31 & (color >> 5);
  77. byte b = 31 & color;
  78. return (color & 0x8000) | (b << 10) | (g << 5) | r;
  79. }
  80. // Swap color's red and green channels
  81. uint16_t swapRG(uint16_t color)
  82. {
  83. byte r = 31 & (color >> 10);
  84. byte g = 31 & (color >> 5);
  85. byte b = 31 & color;
  86. return (color & 0x8000) | (g << 10) | (r << 5) | b;
  87. }
  88. #include "asteroidgraphics.h"
  89. #include "splitscreen.h"
  90. static void update_score();
  91. // [int(127 * math.sin(math.pi * 2 * i / 16)) for i in range(16)]
  92. static flash_uint8_t charsin[16] = {0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48};
  93. #define qsin(a) (char)pgm_read_byte_near(charsin + ((a) & 15))
  94. #define qcos(a) qsin((a) + 4)
  95. static char spr2obj[256]; // Maps sprites to owning objects
  96. /*
  97. The general idea is that an object table ``objects`` has an entry for
  98. each drawn thing on screen (e.g. player, missile, rock, explosion).
  99. Each class of object has a ``handler`` function that does the necessary
  100. housekeeping and draws the actual sprites.
  101. As part of the behavior, some classes need to know if they have collided
  102. with anything. In particular the rocks need to know if they have collided
  103. with the player or a missile. The `collide` member points to the
  104. colliding sprite.
  105. */
  106. struct object {
  107. int x, y;
  108. byte handler, state;
  109. byte collide;
  110. } objects[128];
  111. #define COORD(c) ((c) << 4)
  112. static char explosions = -1;
  113. static char enemies = -1;
  114. static char missiles = -1;
  115. static void push(char *stk, byte i)
  116. {
  117. objects[i].state = *stk;
  118. *stk = i;
  119. }
  120. static char pop(char *stk)
  121. {
  122. char r = *stk;
  123. if (0 <= r) {
  124. *stk = objects[r].state;
  125. }
  126. return r;
  127. }
  128. byte rr[4] = { 0,3,6,5 };
  129. static struct {
  130. byte boing, boom, pop;
  131. byte thrust;
  132. byte bass;
  133. } sounds;
  134. static int player_vx, player_vy; // Player velocity
  135. static int player_invincible, player_dying;
  136. static byte lives;
  137. static long score;
  138. static byte level;
  139. // Move object po by velocity (vx, vy), optionally keeping in
  140. // player's frame.
  141. // Returns true if the object wrapped screen edge
  142. static bool move(struct object *po, char vx, char vy, byte playerframe = 1)
  143. {
  144. bool r = 0;
  145. if (playerframe) {
  146. po->x += (vx - player_vx);
  147. po->y += (vy - player_vy);
  148. } else {
  149. po->x += vx;
  150. po->y += vy;
  151. }
  152. if (po->x > COORD(416))
  153. r = 1, po->x -= COORD(432);
  154. else if (po->x < COORD(-16))
  155. r = 1, po->x += COORD(432);
  156. if (po->y > COORD(316))
  157. r = 1, po->y -= COORD(332);
  158. else if (po->y < COORD(-16))
  159. r = 1, po->y += COORD(332);
  160. return r;
  161. }
  162. #define HANDLE_NULL 0
  163. #define HANDLE_ROCK0 1
  164. #define HANDLE_ROCK1 2
  165. #define HANDLE_BANG0 3
  166. #define HANDLE_BANG1 4
  167. #define HANDLE_PLAYER 5
  168. #define HANDLE_MISSILE 6
  169. // Expire object i, and return it to the free stk
  170. static void expire(char *stk, byte i)
  171. {
  172. objects[i].handler = HANDLE_NULL;
  173. push(stk, i);
  174. }
  175. static void handle_null(byte i, byte state, uint16_t clock)
  176. {
  177. }
  178. static void handle_player(byte i, byte state, uint16_t clock)
  179. {
  180. struct object *po = &objects[i];
  181. byte angle = (po->state & 15);
  182. byte rot1 = (angle & 3);
  183. byte rot2 = rr[3 & (angle >> 2)];
  184. if (!player_dying && (player_invincible & 1) == 0)
  185. draw_player(200, 150, rot1, rot2);
  186. static byte prev_control;
  187. byte control = controller_sense(clock);
  188. char thrust_x, thrust_y;
  189. if (!player_dying && control & CONTROL_DOWN) { // thrust
  190. byte flame_angle = angle ^ 8;
  191. byte d;
  192. for (d = 9; d > 5; d--) {
  193. int flamex = 201 - (((d + (clock&3)) * qsin(flame_angle)) >> 5);
  194. int flamey = 150 - (((d + (clock&3)) * qcos(flame_angle)) >> 5);
  195. if ((player_invincible & 1) == 0)
  196. draw_sparkr(flamex, flamey, rot1, rot2, 1); // collision class K
  197. }
  198. thrust_x = -qsin(angle);
  199. thrust_y = -qcos(angle);
  200. sounds.thrust = 1;
  201. } else {
  202. thrust_x = thrust_y = 0;
  203. sounds.thrust = 0;
  204. }
  205. player_vx = ((31 * player_vx) + thrust_x) / 32;
  206. player_vy = ((31 * player_vy) + thrust_y) / 32;
  207. po->x += player_vx;
  208. po->y += player_vy;
  209. if (clock & 1) {
  210. char rotate = (512 - analogRead(0)) / 400;
  211. if (control & CONTROL_LEFT)
  212. rotate++;
  213. if (control & CONTROL_RIGHT)
  214. rotate--;
  215. po->state = ((angle + rotate) & 15);
  216. }
  217. if (!player_dying &&
  218. !(prev_control & CONTROL_UP) &&
  219. (control & CONTROL_UP)) { // shoot!
  220. char e = pop(&missiles);
  221. if (0 <= e) {
  222. objects[e].x = COORD(200);
  223. objects[e].y = COORD(150);
  224. objects[e].state = po->state;
  225. objects[e].handler = HANDLE_MISSILE;
  226. }
  227. sounds.boing = 1;
  228. }
  229. prev_control = control;
  230. if (player_invincible)
  231. --player_invincible;
  232. if (player_dying) {
  233. if (--player_dying == 0) {
  234. --lives;
  235. update_score();
  236. if (lives != 0) {
  237. player_invincible = 48;
  238. }
  239. }
  240. }
  241. }
  242. static void handle_missile(byte i, byte state, uint16_t clock)
  243. {
  244. struct object *po = &objects[i];
  245. byte angle = (po->state & 15);
  246. byte rot1 = (angle & 3);
  247. byte rot2 = rr[3 & (angle >> 2)];
  248. draw_sparkr(po->x >> 4, po->y >> 4, rot1, rot2);
  249. char vx = -qsin(po->state), vy = -qcos(po->state);
  250. if (move(po, vx, vy, 0)) {
  251. expire(&missiles, i);
  252. }
  253. }
  254. static void explodehere(struct object *po, byte handler, uint16_t clock)
  255. {
  256. char e = pop(&explosions);
  257. if (0 <= e) {
  258. objects[e].x = po->x;
  259. objects[e].y = po->y;
  260. objects[e].handler = handler;
  261. objects[e].state = clock;
  262. }
  263. }
  264. static void killplayer(uint16_t clock)
  265. {
  266. if (!player_invincible && !player_dying) {
  267. char e = pop(&explosions);
  268. if (0 <= e) {
  269. objects[e].x = COORD(200);
  270. objects[e].y = COORD(150);
  271. objects[e].handler = HANDLE_BANG1;
  272. objects[e].state = clock;
  273. }
  274. player_dying = 2 * 36;
  275. sounds.boom = 1;
  276. sounds.pop = 1;
  277. }
  278. }
  279. static byte commonrock(uint16_t clock, byte i, byte speed, void df(int x, int y, byte anim, byte rot, byte jk))
  280. {
  281. struct object *po = &objects[i];
  282. byte move_angle = po->state >> 4;
  283. char vx = (speed * -qsin(move_angle)) >> 6, vy = (speed * -qcos(move_angle)) >> 6;
  284. move(po, vx, vy);
  285. byte angle = (clock * speed) >> 4;
  286. if (po->state & 1)
  287. angle = ~angle;
  288. byte rot1 = (angle & 3);
  289. byte rot2 = rr[3 & (angle >> 2)];
  290. df(po->x >> 4, po->y >> 4, rot1, rot2, 1);
  291. if (po->collide != 0xff) {
  292. struct object *other = &objects[po->collide];
  293. switch (other->handler) {
  294. case HANDLE_PLAYER:
  295. killplayer(clock);
  296. break;
  297. case HANDLE_MISSILE:
  298. expire(&missiles, po->collide); // missile is dead
  299. expire(&enemies, i);
  300. return 1;
  301. }
  302. }
  303. return 0;
  304. }
  305. static void handle_rock0(byte i, byte state, uint16_t clock)
  306. {
  307. struct object *po = &objects[i];
  308. byte speed = 12 + (po->state & 7);
  309. if (commonrock(clock, i, speed, draw_rock0r)) {
  310. explodehere(po, HANDLE_BANG0, clock);
  311. score += 10;
  312. sounds.pop = 1;
  313. }
  314. }
  315. static void handle_rock1(byte i, byte state, uint16_t clock)
  316. {
  317. struct object *po = &objects[i];
  318. byte speed = 6 + (po->state & 3);
  319. if (commonrock(clock, i, speed, draw_rock1r)) {
  320. int j;
  321. for (j = 0; j < 4; j++) {
  322. char e = pop(&enemies);
  323. if (0 < e) {
  324. objects[e].x = po->x;
  325. objects[e].y = po->y;
  326. objects[e].handler = HANDLE_ROCK0;
  327. objects[e].state = (j << 6) + qrand(6); // spread fragments across 4 quadrants
  328. }
  329. }
  330. explodehere(po, HANDLE_BANG1, clock);
  331. score += 30;
  332. sounds.boom = 1;
  333. }
  334. }
  335. static void handle_bang0(byte i, byte state, uint16_t clock)
  336. {
  337. struct object *po = &objects[i];
  338. move(po, 0, 0);
  339. byte anim = ((0xff & clock) - state) >> 1;
  340. if (anim < EXPLODE16_FRAMES)
  341. draw_explode16(po->x >> 4, po->y >> 4, anim, 0);
  342. else
  343. expire(&explosions, i);
  344. }
  345. static void handle_bang1(byte i, byte state, uint16_t clock)
  346. {
  347. struct object *po = &objects[i];
  348. move(po, 0, 0);
  349. byte anim = ((0xff & clock) - state) >> 1;
  350. byte rot = 7 & i;
  351. if (anim < EXPLODE32_FRAMES)
  352. draw_explode32(po->x >> 4, po->y >> 4, anim, rot);
  353. else
  354. expire(&explosions, i);
  355. }
  356. typedef void (*handler)(byte, byte, uint16_t);
  357. static handler handlers[] = {
  358. handle_null,
  359. handle_rock0,
  360. handle_rock1,
  361. handle_bang0,
  362. handle_bang1,
  363. handle_player,
  364. handle_missile
  365. };
  366. class GDflashbits {
  367. public:
  368. void begin(flash_uint8_t *s) {
  369. src = s;
  370. mask = 0x01;
  371. }
  372. byte get1(void) {
  373. byte r = (pgm_read_byte_near(src) & mask) != 0;
  374. mask <<= 1;
  375. if (!mask) {
  376. mask = 1;
  377. src++;
  378. }
  379. return r;
  380. }
  381. unsigned short getn(byte n) {
  382. unsigned short r = 0;
  383. while (n--) {
  384. r <<= 1;
  385. r |= get1();
  386. }
  387. return r;
  388. }
  389. private:
  390. flash_uint8_t *src;
  391. byte mask;
  392. };
  393. static GDflashbits GDFB;
  394. static void GD_uncompress(unsigned int addr, flash_uint8_t *src)
  395. {
  396. GDFB.begin(src);
  397. byte b_off = GDFB.getn(4);
  398. byte b_len = GDFB.getn(4);
  399. byte minlen = GDFB.getn(2);
  400. unsigned short items = GDFB.getn(16);
  401. while (items--) {
  402. if (GDFB.get1() == 0) {
  403. GD.wr(addr++, GDFB.getn(8));
  404. } else {
  405. int offset = -GDFB.getn(b_off) - 1;
  406. int l = GDFB.getn(b_len) + minlen;
  407. while (l--) {
  408. GD.wr(addr, GD.rd(addr + offset));
  409. addr++;
  410. }
  411. }
  412. }
  413. }
  414. // uncompress one line of the title banner into buffer dst
  415. // title banner lines are run-length encoded
  416. static void titlepaint(char *dst, byte src, byte mask)
  417. {
  418. if (src != 0xff) {
  419. flash_uint8_t *psrc = title_runs + 2 * src;
  420. byte a, b;
  421. do {
  422. a = pgm_read_byte_near(psrc++);
  423. b = pgm_read_byte_near(psrc++);
  424. while (a < (b & 0x7f))
  425. dst[a++] |= mask;
  426. } while (!(b & 0x80));
  427. }
  428. }
  429. // draw a title banner column src (0-511) to screen column dst (0-63).
  430. static void column(byte dst, byte src)
  431. {
  432. static char scratch[76];
  433. memset(scratch, 0, sizeof(scratch));
  434. uint8_t line = pgm_read_byte_near(title + 2 * src);
  435. titlepaint(scratch, line, 1);
  436. line = pgm_read_byte_near(title + 2 * src + 1);
  437. titlepaint(scratch, line, 2);
  438. byte j;
  439. for (j = 0; j < 38; j++) {
  440. GD.wr(dst + (j << 6),
  441. (((dst + j) & 15) << 4) +
  442. scratch[2 * j] +
  443. (scratch[2 * j + 1] << 2));
  444. }
  445. }
  446. static void setup_sprites()
  447. {
  448. GD.copy(PALETTE16A, palette16a, sizeof(palette16a));
  449. GD.copy(PALETTE4A, palette4a, sizeof(palette4a));
  450. GD.copy(PALETTE16B, palette16b, sizeof(palette16b));
  451. // Use the first two 256-color palettes as pseudo-16 color palettes
  452. int i;
  453. for (i = 0; i < 256; i++) {
  454. // palette 0 decodes low nibble, hence (i & 15)
  455. uint16_t rgb = pgm_read_word_near(palette256a + ((i & 15) << 1));
  456. GD.wr16(RAM_SPRPAL + (i << 1), rgb);
  457. // palette 1 decodes nigh nibble, hence (i >> 4)
  458. rgb = pgm_read_word_near(palette256a + ((i >> 4) << 1));
  459. GD.wr16(RAM_SPRPAL + 512 + (i << 1), rgb);
  460. }
  461. GD_uncompress(RAM_SPRIMG, asteroid_images_compressed);
  462. GD.wr(JK_MODE, 1);
  463. }
  464. // Run the object handlers, keeping track of sprite ownership in spr2obj
  465. static void runobjects(uint16_t r)
  466. {
  467. int i;
  468. // JCB-SPR_PAGEW-A
  469. GD.__wstartspr((r & 1) ? 256 : 0); // write sprites to other frame
  470. // JCB-SPR_PAGEW-B
  471. for (i = 0; i < 128; i++) {
  472. struct object *po = &objects[i];
  473. handler h = (handler)handlers[po->handler];
  474. byte loSpr = GD.spr;
  475. (*h)(i, po->state, r);
  476. while (loSpr < GD.spr) {
  477. spr2obj[loSpr++] = i;
  478. }
  479. }
  480. // Hide all the remaining sprites
  481. do
  482. GD.xhide();
  483. while (GD.spr);
  484. GD.__end();
  485. }
  486. // ----------------------------------------------------------------------
  487. // map
  488. // ----------------------------------------------------------------------
  489. static byte loaded[8];
  490. static byte scrap;
  491. // copy a (w,h) rectangle from the source image (x,y) into picture RAM
  492. static void rect(unsigned int dst, byte x, byte y, byte w, byte h)
  493. {
  494. flash_uint8_t *src = bg_pic + (16 * y) + x;
  495. while (h--) {
  496. GD.copy(dst, src, w);
  497. dst += 64;
  498. src += 16;
  499. }
  500. }
  501. static void map_draw(byte strip)
  502. {
  503. strip &= 63; // Universe is finite but unbounded: 64 strips
  504. byte s8 = (strip & 7); // Destination slot for this strip (0-7)
  505. if (loaded[s8] != strip) {
  506. qrandSeed(level ^ (strip * 77)); // strip number is the hash...
  507. // Random star pattern is made from characters 1-15
  508. GD.__wstart(s8 * (8 * 64));
  509. int i;
  510. for (i = 0; i < (8 * 64); i++) {
  511. byte r;
  512. if (qrand(3) == 0)
  513. r = qrand(4);
  514. else
  515. r = 0;
  516. SPI.transfer(r);
  517. }
  518. GD.__end();
  519. // Occasional planet, copied from the background char map
  520. if (qrand(2) == 0) {
  521. uint16_t dst = (qrand(3) * 8) + (s8 * (8 * 64));
  522. switch (qrand(2)) {
  523. case 0:
  524. rect(dst, 0, 1, 6, 6);
  525. break;
  526. case 1:
  527. rect(dst, 7, 1, 6, 6);
  528. break;
  529. case 2:
  530. rect(dst, 0, 7, 8, 4);
  531. break;
  532. case 3:
  533. rect(dst, 8, 7, 5, 5);
  534. break;
  535. }
  536. }
  537. loaded[s8] = strip;
  538. }
  539. }
  540. static void map_coldstart()
  541. {
  542. memset(loaded, 0xff, sizeof(loaded));
  543. scrap = 0xff;
  544. byte i;
  545. for (i = 0; i < 8; i++)
  546. map_draw(i);
  547. }
  548. static int atxy(int x, int y)
  549. {
  550. return (y << 6) + x;
  551. }
  552. static void update_score()
  553. {
  554. flash_uint8_t* digitcodes = bg_pic + (16 * 30);
  555. unsigned long s = score;
  556. uint16_t a = atxy(49, scrap << 3);
  557. byte i;
  558. for (i = 0; i < 6; i++) {
  559. GD.wr(a--, pgm_read_byte_near(digitcodes + (s % 10)));
  560. s /= 10;
  561. }
  562. GD.wr(atxy(0, scrap << 3), pgm_read_byte_near(digitcodes + lives));
  563. }
  564. static void map_refresh(byte strip)
  565. {
  566. byte i;
  567. byte newscrap = 7 & (strip + 7);
  568. if (scrap != newscrap) {
  569. scrap = newscrap;
  570. uint16_t scrapline = scrap << 6;
  571. GD.wr16(COMM+2, 0x8000 | scrapline); // show scrapline at line 0
  572. GD.wr16(COMM+14, 0x8000 | (0x1ff & ((scrapline + 8) - 291))); // show scrapline+8 at line 291
  573. GD.fill(atxy(0, scrap << 3), 0, 50);
  574. update_score();
  575. GD.fill(atxy(0, 1 + (scrap << 3)), 0, 64);
  576. rect(atxy(0, 1 + (scrap << 3)), 0, 31, 16, 1);
  577. rect(atxy(32, 1 + (scrap << 3)), 0, 31, 16, 1);
  578. loaded[scrap] = 0xff;
  579. }
  580. delay(1); // wait for raster to pass the top line before overwriting it
  581. for (i = 0; i < 6; i++)
  582. map_draw(strip + i);
  583. }
  584. static void start_level()
  585. {
  586. int i;
  587. for (i = 0; i < 128; i++)
  588. objects[i].handler = 0;
  589. player_vx = 0;
  590. player_vy = 0;
  591. player_invincible = 0;
  592. player_dying = 0;
  593. objects[0].x = 0;
  594. objects[0].y = 0;
  595. objects[0].state = 0;
  596. objects[0].handler = HANDLE_PLAYER;
  597. // Set up the pools of objects for missiles, enemies, explosions
  598. missiles = 0;
  599. enemies = 0;
  600. explosions = 0;
  601. for (i = 1; i < 16; i++)
  602. push(&missiles, i);
  603. for (i = 16; i < 80; i++)
  604. push(&enemies, i);
  605. for (i = 80; i < 128; i++)
  606. push(&explosions, i);
  607. // Place asteroids in a ring around the edges of the screen
  608. for (i = 0; i < min(32, 3 + level); i++) {
  609. char e = pop(&enemies);
  610. if (random(2) == 0) {
  611. objects[e].x = random(2) ? COORD(32) : COORD(400-32);
  612. objects[e].y = random(COORD(300));
  613. } else {
  614. objects[e].x = random(COORD(400));
  615. objects[e].y = random(2) ? COORD(32) : COORD(300-32);
  616. }
  617. objects[e].handler = HANDLE_ROCK1;
  618. objects[e].state = qrand(8);
  619. }
  620. GD.copy(PALETTE16B, palette16b, sizeof(palette16b));
  621. for (i = 0; i < 16; i++) {
  622. uint16_t a = PALETTE16B + 2 * i;
  623. uint16_t c = GD.rd16(a);
  624. if (level & 1)
  625. c = swapRB(c);
  626. if (level & 2)
  627. c = swapRG(c);
  628. if (level & 4)
  629. c = swapRB(c);
  630. GD.wr16(a, c);
  631. }
  632. map_coldstart();
  633. }
  634. void setup()
  635. {
  636. Serial.begin(1000000); // JCB
  637. GD.begin();
  638. controller_init();
  639. }
  640. static void title_banner()
  641. {
  642. GD.fill(VOICES, 0, 64 * 4);
  643. GD.wr(J1_RESET, 1);
  644. GD.wr(SPR_DISABLE, 1);
  645. GD.wr16(SCROLL_X, 0);
  646. GD.wr16(SCROLL_Y, 0);
  647. GD.fill(RAM_PIC, 0, 4096);
  648. setup_sprites();
  649. uint16_t i;
  650. uint16_t j;
  651. GD.__wstart(RAM_CHR);
  652. for (i = 0; i < 256; i++) {
  653. // bits control lit segments like this:
  654. // 0 1
  655. // 2 3
  656. byte a = (i & 1) ? 0x3f : 0;
  657. byte b = (i & 2) ? 0x3f : 0;
  658. byte c = (i & 4) ? 0x3f : 0;
  659. byte d = (i & 8) ? 0x3f : 0;
  660. for (j = 0; j < 3; j++) {
  661. SPI.transfer(a);
  662. SPI.transfer(b);
  663. }
  664. SPI.transfer(0);
  665. SPI.transfer(0);
  666. for (j = 0; j < 3; j++) {
  667. SPI.transfer(c);
  668. SPI.transfer(d);
  669. }
  670. SPI.transfer(0);
  671. SPI.transfer(0);
  672. }
  673. GD.__end();
  674. for (i = 0; i < 256; i++) {
  675. GD.setpal(4 * i + 0, RGB(0,0,0));
  676. uint16_t color = pgm_read_word_near(title_ramp + 2 * (i >> 4));
  677. GD.setpal(4 * i + 3, color);
  678. }
  679. for (i = 0; i < 64; i++) {
  680. column(i, i);
  681. }
  682. for (i = 0; i < 128; i++) {
  683. objects[i].handler = 0;
  684. objects[i].collide = 0xff;
  685. }
  686. for (i = 0; i < 128; i++)
  687. push(&enemies, i);
  688. for (i = 0; i < 40; i++) {
  689. char e = pop(&enemies);
  690. objects[e].x = COORD(random(400));
  691. objects[e].y = COORD(random(300));
  692. objects[e].handler = qrand1() ? HANDLE_ROCK1 : HANDLE_ROCK0;
  693. objects[e].state = qrand(8);
  694. }
  695. byte startgame = 0;
  696. for (i = 0; startgame < 50; i++) {
  697. for (j = 0; j < 256; j++) {
  698. byte index = 15 & ((-i >> 2) + (j >> 4));
  699. uint16_t color = pgm_read_word_near(title_ramp + 2 * index);
  700. GD.setpal(4 * j + 3, color);
  701. }
  702. if (!startgame &&
  703. (controller_sense(i) || (i == (2048 - 400)))) {
  704. // explode all rocks!
  705. for (j = 0; j < 128; j++) {
  706. byte h = objects[j].handler;
  707. if ((h == HANDLE_ROCK0) || (h == HANDLE_ROCK1))
  708. objects[j].handler = HANDLE_BANG1;
  709. objects[j].state = i;
  710. }
  711. startgame = 1;
  712. }
  713. if (startgame)
  714. startgame++;
  715. runobjects(i);
  716. GD.waitvblank();
  717. GD.wr(SPR_PAGE, (i & 1));
  718. GD.wr(SPR_DISABLE, 0);
  719. GD.wr16(SCROLL_X, i);
  720. if ((i & 7) == 0) {
  721. byte x = ((i >> 3) + 56);
  722. column(63 & x, 255 & x);
  723. }
  724. // if (SCREENSHOT && (i & 1)) // JCB
  725. // GD.screenshot(screenshot_i++); // JCB
  726. }
  727. // JCB-FADE-A
  728. for (i = 0; i < 32; i++) {
  729. for (j = 0; j < 256; j++) {
  730. uint16_t a = RAM_PAL + (8 * j) + 6;
  731. uint16_t pal = GD.rd16(a);
  732. byte r = 31 & (pal >> 10);
  733. byte g = 31 & (pal >> 5);
  734. byte b = 31 & pal;
  735. if (r) r--;
  736. if (g) g--;
  737. if (b) b--;
  738. pal = (r << 10) | (g << 5) | b;
  739. GD.wr16(a, pal);
  740. }
  741. GD.waitvblank();
  742. GD.waitvblank();
  743. }
  744. // JCB-FADE-B
  745. GD.fill(RAM_PIC, 0, 4096);
  746. }
  747. #define SOUNDCYCLE(state) ((state) = v ? ((state) + 1) : 0)
  748. void loop()
  749. {
  750. title_banner();
  751. GD_uncompress(RAM_CHR, bg_chr_compressed);
  752. GD_uncompress(RAM_PAL, bg_pal_compressed);
  753. GD.wr16(COMM+0, 0);
  754. GD.wr16(COMM+2, 0x8000);
  755. GD.wr16(COMM+4, 8); // split at line 8
  756. GD.wr16(COMM+6, 177);
  757. GD.wr16(COMM+8, 166);
  758. GD.wr16(COMM+10, 291); // split at line 291
  759. GD.wr16(COMM+12, 0);
  760. GD.wr16(COMM+14, 0x8000 | (0x1ff & (8 - 291))); // show line 8 at line 292
  761. GD.microcode(splitscreen_code, sizeof(splitscreen_code));
  762. setup_sprites();
  763. qrandSeed(34); // JCB - determinism
  764. randomSeed(34); // JCB
  765. memset(&sounds, 0, sizeof(sounds));
  766. level = 0;
  767. score = 0;
  768. lives = 3;
  769. unsigned int r = 0;
  770. start_level();
  771. while (lives) {
  772. int i, j;
  773. runobjects(r);
  774. for (i = 0; i < 128; i++)
  775. objects[i].collide = 0xff;
  776. // JCB-SPR_PAGE-A
  777. GD.waitvblank();
  778. // swap frames
  779. GD.wr(SPR_PAGE, (r & 1));
  780. // JCB-SPR_PAGE-B
  781. int scrollx = objects[0].x >> 4;
  782. int scrolly = objects[0].y >> 4;
  783. GD.wr16(COMM+6, scrollx & 0x1ff);
  784. GD.wr16(COMM+8, scrolly & 0x1ff);
  785. map_refresh(scrolly >> 6);
  786. update_score();
  787. GD.wr16(COMM+12, r); // horizontal scroll the bottom banner
  788. GD.waitvblank();
  789. GD.__start(COLLISION);
  790. for (i = 0; i < 256; i++) {
  791. byte c = SPI.transfer(0); // c is the colliding sprite number
  792. if (c != 0xff) {
  793. objects[spr2obj[i]].collide = spr2obj[c];
  794. }
  795. }
  796. GD.__end();
  797. if (SCREENSHOT) // JCB
  798. GD.screenshot(screenshot_i++); // JCB
  799. if (sounds.boing) {
  800. byte v = max(0, 16 - (sounds.boing - 1) * 2);
  801. GD.voice(0, 0, 4 * 4000 - 700 * sounds.boing, v/2, v/2);
  802. GD.voice(1, 1, 1000 - 100 * sounds.boing, v, v);
  803. SOUNDCYCLE(sounds.boing);
  804. }
  805. if (sounds.boom) {
  806. byte v = max(0, 96 - (sounds.boom - 1) * 6);
  807. GD.voice(2, 0, 220, v, v);
  808. GD.voice(3, 1, 220/8, v/2, v/2);
  809. SOUNDCYCLE(sounds.boom);
  810. }
  811. if (sounds.pop) {
  812. byte v = max(0, 32 - (sounds.pop - 1) * 3);
  813. GD.voice(4, 0, 440, v, v);
  814. GD.voice(5, 1, 440/8, v/2, v/2);
  815. SOUNDCYCLE(sounds.pop);
  816. }
  817. GD.voice(6, 1, 40, sounds.thrust ? 10 : 0, sounds.thrust ? 10 : 0);
  818. static byte tune;
  819. if (sounds.bass) {
  820. byte v = sounds.bass < 9 ? 63 : 0;
  821. int f0 = tune ? 130: 163 ;
  822. byte partials[] = { 71, 32, 14, 75, 20, 40 };
  823. byte i;
  824. for (i = 0; i < 6; i++) {
  825. byte a = (v * partials[i]) >> 8;
  826. GD.voice(7 + i, 0, f0 * (i + 1), a, a);
  827. }
  828. SOUNDCYCLE(sounds.bass);
  829. }
  830. static byte rhythm;
  831. if (++rhythm >= max(24 - level, 10)) {
  832. sounds.bass = 1;
  833. rhythm = 0;
  834. tune = !tune;
  835. }
  836. byte nenemies = 64;
  837. byte pe = enemies;
  838. while (pe) {
  839. pe = objects[pe].state;
  840. nenemies--;
  841. }
  842. if (nenemies == 0) {
  843. level++;
  844. start_level();
  845. }
  846. r++;
  847. }
  848. }