Louis Mayencourt
/
NRF_OLED
Arduboy lib for NRF and mbed
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Sat Jul 16 2022 09:43:30 by 1.7.2