Arduboy lib for NRF and mbed

Dependencies:   Adafruit_GFX

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers core.cpp Source File

core.cpp

00001 #include "core.h"
00002 
00003 unsigned char Arduboy::sBuffer[];
00004 
00005 Arduboy::Arduboy() :   
00006   i2c(SDA,SCL),
00007   Adafruit_SSD1306_I2c(i2c,p13)
00008 { }
00009 
00010 void Arduboy::start()
00011 {
00012 #if F_CPU == 8000000L
00013   slowCPU();
00014 #endif
00015 
00016   // init pin
00017   DigitalOut led1(LED1);
00018 
00019   bootLCD();
00020 
00021 #ifdef SAFE_MODE
00022   if (pressed(LEFT_BUTTON + UP_BUTTON))
00023     safeMode();
00024 #endif
00025 
00026 
00027   audio.setup();
00028   saveMuchPower();
00029 }
00030 
00031 #if F_CPU == 8000000L
00032 // if we're compiling for 8Mhz we need to slow the CPU down because the
00033 // hardware clock on the Arduboy is 16MHz
00034 void Arduboy::slowCPU()
00035 {
00036 
00037 }
00038 #endif
00039 
00040 void Arduboy::bootLCD()
00041 {
00042   clearDisplay();
00043   display();
00044 }
00045 
00046 // Safe Mode is engaged by holding down both the LEFT button and UP button
00047 // when plugging the device into USB.  It puts your device into a tight
00048 // loop and allows it to be reprogrammed even if you have uploaded a very
00049 // broken sketch that interferes with the normal USB triggered auto-reboot
00050 // functionality of the device.
00051 void Arduboy::safeMode()
00052 {
00053   display(); // too avoid random gibberish
00054   while (true) {};
00055 }
00056 
00057 /* Power Management */
00058 
00059 void Arduboy::idle()
00060 {
00061 }
00062 
00063 void Arduboy::saveMuchPower()
00064 {
00065 }
00066 
00067 
00068 /* Frame management */
00069 
00070 void Arduboy::setFrameRate(uint8_t rate)
00071 {
00072   frameRate = rate;
00073   eachFrameMillis = 1000 / rate;
00074 }
00075 
00076 bool Arduboy::everyXFrames(uint8_t frames)
00077 {
00078   return frameCount % frames == 0;
00079 }
00080 
00081 bool Arduboy::nextFrame()
00082 {
00083   long now = millis();
00084   uint8_t remaining;
00085 
00086   // post render
00087   if (post_render) {
00088     lastFrameDurationMs = now - lastFrameStart;
00089     frameCount++;
00090     post_render = false;
00091   }
00092 
00093   // if it's not time for the next frame yet
00094   if (now < nextFrameStart) {
00095     remaining = nextFrameStart - now;
00096     // if we have more than 1ms to spare, lets sleep
00097     // we should be woken up by timer0 every 1ms, so this should be ok
00098     if (remaining > 1)
00099       idle();
00100     return false;
00101   }
00102 
00103   // pre-render
00104 
00105   // technically next frame should be last frame + each frame but if we're
00106   // running a slow render we would constnatly be behind the clock
00107   // keep an eye on this and see how it works.  If it works well the
00108   // lastFrameStart variable could be eliminated completely
00109   nextFrameStart = now + eachFrameMillis;
00110   lastFrameStart = now;
00111   post_render = true;
00112   return post_render;
00113 }
00114 
00115 // returns the load on the CPU as a percentage
00116 // this is based on how much of the time your app is spends rendering
00117 // frames.  This number can be higher than 100 if your app is rendering
00118 // really slowly.
00119 int Arduboy::cpuLoad()
00120 {
00121   return lastFrameDurationMs * 100 / eachFrameMillis;
00122 }
00123 
00124 // seed the random number generator with entropy from the temperature,
00125 // voltage reading, and microseconds since boot.
00126 // this method is still most effective when called semi-randomly such
00127 // as after a user hits a button to start a game or other semi-random
00128 // events
00129 void Arduboy::initRandomSeed()
00130 {
00131 }
00132 
00133 uint16_t Arduboy::rawADC(byte adc_bits)
00134 {
00135   return 0;
00136 }
00137 
00138 
00139 unsigned char* Arduboy::getBuffer() {
00140   return sBuffer;
00141 }
00142 
00143 uint8_t Arduboy::width() {
00144   return WIDTH;
00145 }
00146 
00147 uint8_t Arduboy::height() {
00148   return HEIGHT;
00149 }
00150 
00151 
00152 void Arduboy::poll()
00153 {
00154   previousButtonState = currentButtonState;
00155   currentButtonState = getInput();
00156 }
00157 
00158 // returns true if the button mask passed in is pressed
00159 //
00160 //   if (pressed(LEFT_BUTTON + A_BUTTON))
00161 boolean Arduboy::pressed(uint8_t buttons)
00162 {
00163   uint8_t button_state = getInput();
00164   return (button_state & buttons) == buttons;
00165 }
00166 
00167 // returns true if the button mask passed in not pressed
00168 //
00169 //   if (not_pressed(LEFT_BUTTON))
00170 boolean Arduboy::notPressed(uint8_t buttons)
00171 {
00172   uint8_t button_state = getInput();
00173   return (button_state & buttons) == 0;
00174 }
00175 
00176 // returns true if a button has just been pressed
00177 // if the button has been held down for multiple frames this will return
00178 // false.  You should only use this to poll a single button.
00179 boolean Arduboy::justPressed(uint8_t button)
00180 {
00181   uint8_t button_state = getInput();
00182   return (!(previousButtonState & button) && (currentButtonState & button));
00183 }
00184 
00185 
00186 
00187 uint8_t Arduboy::getInput()
00188 {
00189   uint8_t buttons;
00190 
00191   return buttons;
00192 }
00193 
00194 void Arduboy::swap(int16_t& a, int16_t& b) {
00195   int temp = a;
00196   a = b;
00197   b = temp;
00198 }
00199 
00200 
00201 /* AUDIO */
00202 
00203 void ArduboyAudio::on() {
00204 
00205   audio_enabled = true;
00206 }
00207 
00208 bool ArduboyAudio::enabled() {
00209   return audio_enabled;
00210 }
00211 
00212 void ArduboyAudio::off() {
00213 
00214   audio_enabled = false;
00215 }
00216 
00217 void ArduboyAudio::saveOnOff() {
00218 }
00219 
00220 void ArduboyAudio::setup() {
00221 
00222 }
00223 
00224 void ArduboyAudio::tone(unsigned int frequency, unsigned long duration)
00225 {
00226 
00227 }
00228 
00229 
00230 /////////////////////////
00231 // Sprites by Dreamer3 //
00232 /////////////////////////
00233 Sprites::Sprites(Arduboy &a)
00234 {
00235   arduboy = &a;
00236   sBuffer = arduboy->getBuffer();
00237 }
00238 
00239 // new API
00240 
00241 void Sprites::drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
00242                                const uint8_t *mask, uint8_t frame, uint8_t mask_frame)
00243 {
00244   draw(x, y, bitmap, frame, mask, mask_frame, SPRITE_MASKED);
00245 }
00246 
00247 void Sprites::drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
00248 {
00249   draw(x, y, bitmap, frame, NULL, 0, SPRITE_OVERWRITE);
00250 }
00251 
00252 void Sprites::drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
00253 {
00254   draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK_ERASE);
00255 }
00256 
00257 void Sprites::drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
00258 {
00259   draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK);
00260 }
00261 
00262 void Sprites::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
00263 {
00264   draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK);
00265 }
00266 
00267 
00268 //common functions
00269 void Sprites::draw(int16_t x, int16_t y,
00270                    const uint8_t *bitmap, uint8_t frame,
00271                    const uint8_t *mask, uint8_t sprite_frame,
00272                    uint8_t drawMode
00273                   )
00274 {
00275   unsigned int frame_offset;
00276 
00277   if (bitmap == NULL)
00278     return;
00279 
00280   uint8_t width = pgm_read_byte(bitmap);
00281   uint8_t height = pgm_read_byte(++bitmap);
00282   bitmap++;
00283   if (frame > 0 || sprite_frame > 0) {
00284     frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
00285     // sprite plus mask uses twice as much space for each frame
00286     if (drawMode == SPRITE_PLUS_MASK) {
00287       frame_offset *= 2;
00288     } else if (mask != NULL) {
00289       mask += sprite_frame * frame_offset;
00290     }
00291     bitmap += frame * frame_offset;
00292   }
00293 
00294   // if we're detecting the draw mode then base it on whether a mask
00295   // was passed as a separate object
00296   if (drawMode == SPRITE_AUTO_MODE) {
00297     drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
00298   }
00299 
00300   drawBitmap(x, y, bitmap, mask, width, height, drawMode);
00301 }
00302 
00303 void Sprites::drawBitmap(int16_t x, int16_t y,
00304                          const uint8_t *bitmap, const uint8_t *mask,
00305                          int8_t w, int8_t h, uint8_t draw_mode) {
00306   // no need to draw at all of we're offscreen
00307   if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
00308     return;
00309 
00310   if (bitmap == NULL)
00311     return;
00312 
00313   // xOffset technically doesn't need to be 16 bit but the math operations
00314   // are measurably faster if it is
00315   uint16_t xOffset, ofs;
00316   int8_t yOffset = abs(y) % 8;
00317   int8_t sRow = y / 8;
00318   uint8_t loop_h, start_h, rendered_width;
00319 
00320   if (y < 0 && yOffset > 0) {
00321     sRow--;
00322     yOffset = 8 - yOffset;
00323   }
00324 
00325   // if the left side of the render is offscreen skip those loops
00326   if (x < 0) {
00327     xOffset = abs(x);
00328   } else {
00329     xOffset = 0;
00330   }
00331 
00332   // if the right side of the render is offscreen skip those loops
00333   if (x + w > WIDTH - 1) {
00334     rendered_width = ((WIDTH - x) - xOffset);
00335   } else {
00336     rendered_width = (w - xOffset);
00337   }
00338 
00339   // if the top side of the render is offscreen skip those loops
00340   if (sRow < -1) {
00341     start_h = abs(sRow) - 1;
00342   } else {
00343     start_h = 0;
00344   }
00345 
00346   loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
00347 
00348   // if (sRow + loop_h - 1 > (HEIGHT/8)-1)
00349   if (sRow + loop_h > (HEIGHT / 8)) {
00350     loop_h = (HEIGHT / 8) - sRow;
00351   }
00352 
00353   // prepare variables for loops later so we can compare with 0
00354   // instead of comparing two variables
00355   loop_h -= start_h;
00356 
00357   sRow += start_h;
00358   ofs = (sRow * WIDTH) + x + xOffset;
00359   uint8_t *bofs = (uint8_t *)bitmap + (start_h * w) + xOffset;
00360   uint8_t *mask_ofs;
00361   if (mask != 0)
00362     mask_ofs = (uint8_t *)mask + (start_h * w) + xOffset;
00363   uint8_t data;
00364 
00365   uint8_t mul_amt = 1 << yOffset;
00366   uint16_t mask_data;
00367   uint16_t bitmap_data;
00368 
00369   switch (draw_mode) {
00370     case SPRITE_UNMASKED:
00371       // we only want to mask the 8 bits of our own sprite, so we can
00372       // calculate the mask before the start of the loop
00373       mask_data = ~(0xFF * mul_amt);
00374       // really if yOffset = 0 you have a faster case here that could be
00375       // optimized
00376       for (uint8_t a = 0; a < loop_h; a++) {
00377         for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
00378           bitmap_data = pgm_read_byte(bofs) * mul_amt;
00379 
00380           if (sRow >= 0) {
00381             data = sBuffer[ofs];
00382             data &= (uint8_t)(mask_data);
00383             data |= (uint8_t)(bitmap_data);
00384             sBuffer[ofs] = data;
00385           }
00386           if (yOffset != 0 && sRow < 7) {
00387             data = sBuffer[ofs + WIDTH];
00388             data &= (*((unsigned char *) (&mask_data) + 1));
00389             data |= (*((unsigned char *) (&bitmap_data) + 1));
00390             sBuffer[ofs + WIDTH] = data;
00391           }
00392           ofs++;
00393           bofs++;
00394         }
00395         sRow++;
00396         bofs += w - rendered_width;
00397         ofs += WIDTH - rendered_width;
00398       }
00399       break;
00400 
00401     case SPRITE_IS_MASK:
00402       for (uint8_t a = 0; a < loop_h; a++) {
00403         for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
00404           bitmap_data = pgm_read_byte(bofs) * mul_amt;
00405           if (sRow >= 0) {
00406             sBuffer[ofs] |= (uint8_t)(bitmap_data);
00407           }
00408           if (yOffset != 0 && sRow < 7) {
00409             sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
00410           }
00411           ofs++;
00412           bofs++;
00413         }
00414         sRow++;
00415         bofs += w - rendered_width;
00416         ofs += WIDTH - rendered_width;
00417       }
00418       break;
00419 
00420     case SPRITE_IS_MASK_ERASE:
00421       for (uint8_t a = 0; a < loop_h; a++) {
00422         for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
00423           bitmap_data = pgm_read_byte(bofs) * mul_amt;
00424           if (sRow >= 0) {
00425             sBuffer[ofs]  &= ~(uint8_t)(bitmap_data);
00426           }
00427           if (yOffset != 0 && sRow < 7) {
00428             sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
00429           }
00430           ofs++;
00431           bofs++;
00432         }
00433         sRow++;
00434         bofs += w - rendered_width;
00435         ofs += WIDTH - rendered_width;
00436       }
00437       break;
00438 
00439     case SPRITE_MASKED:
00440       for (uint8_t a = 0; a < loop_h; a++) {
00441         for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
00442           // NOTE: you might think in the yOffset==0 case that this results
00443           // in more effort, but in all my testing the compiler was forcing
00444           // 16-bit math to happen here anyways, so this isn't actually
00445           // compiling to more code than it otherwise would. If the offset
00446           // is 0 the high part of the word will just never be used.
00447 
00448           // load data and bit shift
00449           // mask needs to be bit flipped
00450           mask_data = ~(pgm_read_byte(mask_ofs) * mul_amt);
00451           bitmap_data = pgm_read_byte(bofs) * mul_amt;
00452 
00453           if (sRow >= 0) {
00454             data = sBuffer[ofs];
00455             data &= (uint8_t)(mask_data);
00456             data |= (uint8_t)(bitmap_data);
00457             sBuffer[ofs] = data;
00458           }
00459           if (yOffset != 0 && sRow < 7) {
00460             data = sBuffer[ofs + WIDTH];
00461             data &= (*((unsigned char *) (&mask_data) + 1));
00462             data |= (*((unsigned char *) (&bitmap_data) + 1));
00463             sBuffer[ofs + WIDTH] = data;
00464           }
00465           ofs++;
00466           mask_ofs++;
00467           bofs++;
00468         }
00469         sRow++;
00470         bofs += w - rendered_width;
00471         mask_ofs += w - rendered_width;
00472         ofs += WIDTH - rendered_width;
00473       }
00474       break;
00475 
00476 
00477     case SPRITE_PLUS_MASK:
00478       // *2 because we use double the bits (mask + bitmap)
00479       bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2);
00480 
00481       uint8_t xi = rendered_width; // used for x loop below
00482       uint8_t yi = loop_h; // used for y loop below
00483 
00484       asm volatile(
00485         "push r28\n" // save Y
00486         "push r29\n"
00487         "mov r28, %A[buffer_page2_ofs]\n" // Y = buffer page 2 offset
00488         "mov r29, %B[buffer_page2_ofs]\n"
00489         "loop_y:\n"
00490         "loop_x:\n"
00491         // load bitmap and mask data
00492         "lpm %A[bitmap_data], Z+\n"
00493         "lpm %A[mask_data], Z+\n"
00494 
00495         // shift mask and buffer data
00496         "tst %[yOffset]\n"
00497         "breq skip_shifting\n"
00498         "mul %A[bitmap_data], %[mul_amt]\n"
00499         "mov %A[bitmap_data], r0\n"
00500         "mov %B[bitmap_data], r1\n"
00501         "mul %A[mask_data], %[mul_amt]\n"
00502         "mov %A[mask_data], r0\n"
00503         // "mov %B[mask_data], r1\n"
00504 
00505 
00506         // SECOND PAGE
00507         // if yOffset != 0 && sRow < 7
00508         "cpi %[sRow], 7\n"
00509         "brge end_second_page\n"
00510         // then
00511         "ld %[data], Y\n"
00512         // "com %B[mask_data]\n" // invert high byte of mask
00513         "com r1\n"
00514         "and %[data], r1\n" // %B[mask_data]
00515         "or %[data], %B[bitmap_data]\n"
00516         // update buffer, increment
00517         "st Y+, %[data]\n"
00518 
00519         "end_second_page:\n"
00520         "skip_shifting:\n"
00521 
00522 
00523         // FIRST PAGE
00524         "ld %[data], %a[buffer_ofs]\n"
00525         // if sRow >= 0
00526         "tst %[sRow]\n"
00527         "brmi end_first_page\n"
00528         // then
00529         "com %A[mask_data]\n"
00530         "and %[data], %A[mask_data]\n"
00531         "or %[data], %A[bitmap_data]\n"
00532 
00533         "end_first_page:\n"
00534         // update buffer, increment
00535         "st %a[buffer_ofs]+, %[data]\n"
00536 
00537 
00538         // "x_loop_next:\n"
00539         "dec %[xi]\n"
00540         "brne loop_x\n"
00541 
00542         // increment y
00543         "next_loop_y:\n"
00544         "dec %[yi]\n"
00545         "breq finished\n"
00546         "mov %[xi], %[x_count]\n" // reset x counter
00547         // sRow++;
00548         "inc %[sRow]\n"
00549         "clr __zero_reg__\n"
00550         // sprite_ofs += (w - rendered_width) * 2;
00551         "add %A[sprite_ofs], %A[sprite_ofs_jump]\n"
00552         "adc %B[sprite_ofs], __zero_reg__\n"
00553         // buffer_ofs += WIDTH - rendered_width;
00554         "add %A[buffer_ofs], %A[buffer_ofs_jump]\n"
00555         "adc %B[buffer_ofs], __zero_reg__\n"
00556         // buffer_ofs_page_2 += WIDTH - rendered_width;
00557         "add r28, %A[buffer_ofs_jump]\n"
00558         "adc r29, __zero_reg__\n"
00559 
00560         "rjmp loop_y\n"
00561         "finished:\n"
00562         // put the Y register back in place
00563         "pop r29\n"
00564         "pop r28\n"
00565         "clr __zero_reg__\n" // just in case
00566         : [xi] "+&r" (xi),
00567         [yi] "+&r" (yi),
00568         [sRow] "+&a" (sRow), // CPI requires an upper register
00569         [data] "+&r" (data),
00570         [mask_data] "+&r" (mask_data),
00571         [bitmap_data] "+&r" (bitmap_data)
00572         :
00573         [x_count] "r" (rendered_width),
00574         [y_count] "r" (loop_h),
00575         [sprite_ofs] "z" (bofs),
00576         [buffer_ofs] "x" (sBuffer+ofs),
00577         [buffer_page2_ofs] "r" (sBuffer+ofs+WIDTH), // Y pointer
00578         [buffer_ofs_jump] "r" (WIDTH-rendered_width),
00579         [sprite_ofs_jump] "r" ((w-rendered_width)*2),
00580         [yOffset] "r" (yOffset),
00581         [mul_amt] "r" (mul_amt)
00582         :
00583       );
00584       break;
00585 
00586   }
00587 }
00588 
00589 
00590 /////////////////////////////////
00591 // Basic Collision by Dreamer3 //
00592 /////////////////////////////////
00593 bool Arduboy::collide(Point point, Rect rect)
00594 {
00595   // does point fall within the bounds of rect
00596   return ((point.x >= rect.x) && (point.x < rect.x + rect.width) &&
00597       (point.y >= rect.y) && (point.y < rect.y + rect.height));
00598 }
00599 
00600 bool Arduboy::collide(Rect rect1, Rect rect2)
00601 {
00602   return !( rect2.x                 >=  rect1.x + rect1.width    ||
00603             rect2.x + rect2.width   <=  rect1.x                ||
00604             rect2.y                 >=  rect1.y + rect1.height ||
00605             rect2.y + rect2.height  <=  rect1.y);
00606 }