Arduboy lib for NRF and mbed

Dependencies:   Adafruit_GFX

Files at this revision

API Documentation at this revision

Comitter:
lmayencou
Date:
Tue Dec 27 11:27:14 2016 +0000
Commit message:
first publish

Changed in this revision

.gitignore Show annotated file Show diff for this revision Revisions of this file
Adafruit_GFX.lib Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
core.cpp Show annotated file Show diff for this revision Revisions of this file
core.h Show annotated file Show diff for this revision Revisions of this file
img/uvision.png Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitignore	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,4 @@
+.build
+.mbed
+projectfiles
+*.py*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Adafruit_GFX.lib	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/nkhorman/code/Adafruit_GFX/#7fb1d4d3525d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,89 @@
+# Getting started with Blinky on mbed OS
+
+This is a very simple guide, reviewing the steps required to get Blinky working on an mbed OS platform.
+
+Please install [mbed CLI](https://github.com/ARMmbed/mbed-cli#installing-mbed-cli).
+
+## Get the example application!
+
+From the command line, import the example:
+
+```
+mbed import mbed-os-example-blinky
+cd mbed-os-example-blinky
+```
+
+### Now compile
+
+Invoke `mbed compile` specifying the name of your platform and your favorite toolchain (`GCC_ARM`, `ARM`, `IAR`). For example, for the ARM Compiler 5:
+
+```
+mbed compile -m K64F -t ARM
+```
+
+Your PC may take a few minutes to compile your code. At the end you should get the following result:
+
+```
+[snip]
++----------------------------+-------+-------+------+
+| Module                     | .text | .data | .bss |
++----------------------------+-------+-------+------+
+| Misc                       | 13939 |    24 | 1372 |
+| core/hal                   | 16993 |    96 |  296 |
+| core/rtos                  |  7384 |    92 | 4204 |
+| features/FEATURE_IPV4      |    80 |     0 |  176 |
+| frameworks/greentea-client |  1830 |    60 |   44 |
+| frameworks/utest           |  2392 |   512 |  292 |
+| Subtotals                  | 42618 |   784 | 6384 |
++----------------------------+-------+-------+------+
+Allocated Heap: unknown
+Allocated Stack: unknown
+Total Static RAM memory (data + bss): 7168 bytes
+Total RAM memory (data + bss + heap + stack): 7168 bytes
+Total Flash memory (text + data + misc): 43402 bytes
+Image: .\.build\K64F\ARM\mbed-os-example-blinky.bin
+```
+
+### Program your board
+
+1. Connect your mbed device to the computer over USB.
+1. Copy the binary file to the mbed device .
+1. Press the reset button to start the program.
+
+You should see the LED of your platform turning on and off.
+
+Congratulations if you managed to complete this test!
+
+## Export the project to Keil MDK and debug your application
+
+From the command line, run the following command:
+
+```
+mbed export -m K64F -i uvision
+```
+
+To debug the application:
+
+1. Start uVision.
+1. Import the uVision project generated earlier.
+1. Compile your application and generate an `.axf` file.
+1. Make sure uVision is configured to debug over CMSIS-DAP (From the Project menu > Options for Target '...' > Debug tab > Use CMSIS-DAP Debugger).
+1. Set breakpoints and start a debug session.
+
+![Image of uVision](img/uvision.png)
+
+## Troubleshooting
+
+1. Make sure `mbed-cli` is working correctly and its version is greater than `0.8.9`
+
+ ```
+ mbed --version
+ ```
+
+ If not, you can update it easily:
+
+ ```
+ pip install mbed-cli --upgrade
+ ```
+
+2. If using Keil MDK, make sure you have a license installed. [MDK-Lite](http://www.keil.com/arm/mdk.asp) has a 32KB restriction on code size.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core.cpp	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,606 @@
+#include "core.h"
+
+unsigned char Arduboy::sBuffer[];
+
+Arduboy::Arduboy() :   
+  i2c(SDA,SCL),
+  Adafruit_SSD1306_I2c(i2c,p13)
+{ }
+
+void Arduboy::start()
+{
+#if F_CPU == 8000000L
+  slowCPU();
+#endif
+
+  // init pin
+  DigitalOut led1(LED1);
+
+  bootLCD();
+
+#ifdef SAFE_MODE
+  if (pressed(LEFT_BUTTON + UP_BUTTON))
+    safeMode();
+#endif
+
+
+  audio.setup();
+  saveMuchPower();
+}
+
+#if F_CPU == 8000000L
+// if we're compiling for 8Mhz we need to slow the CPU down because the
+// hardware clock on the Arduboy is 16MHz
+void Arduboy::slowCPU()
+{
+
+}
+#endif
+
+void Arduboy::bootLCD()
+{
+  clearDisplay();
+  display();
+}
+
+// Safe Mode is engaged by holding down both the LEFT button and UP button
+// when plugging the device into USB.  It puts your device into a tight
+// loop and allows it to be reprogrammed even if you have uploaded a very
+// broken sketch that interferes with the normal USB triggered auto-reboot
+// functionality of the device.
+void Arduboy::safeMode()
+{
+  display(); // too avoid random gibberish
+  while (true) {};
+}
+
+/* Power Management */
+
+void Arduboy::idle()
+{
+}
+
+void Arduboy::saveMuchPower()
+{
+}
+
+
+/* Frame management */
+
+void Arduboy::setFrameRate(uint8_t rate)
+{
+  frameRate = rate;
+  eachFrameMillis = 1000 / rate;
+}
+
+bool Arduboy::everyXFrames(uint8_t frames)
+{
+  return frameCount % frames == 0;
+}
+
+bool Arduboy::nextFrame()
+{
+  long now = millis();
+  uint8_t remaining;
+
+  // post render
+  if (post_render) {
+    lastFrameDurationMs = now - lastFrameStart;
+    frameCount++;
+    post_render = false;
+  }
+
+  // if it's not time for the next frame yet
+  if (now < nextFrameStart) {
+    remaining = nextFrameStart - now;
+    // if we have more than 1ms to spare, lets sleep
+    // we should be woken up by timer0 every 1ms, so this should be ok
+    if (remaining > 1)
+      idle();
+    return false;
+  }
+
+  // pre-render
+
+  // technically next frame should be last frame + each frame but if we're
+  // running a slow render we would constnatly be behind the clock
+  // keep an eye on this and see how it works.  If it works well the
+  // lastFrameStart variable could be eliminated completely
+  nextFrameStart = now + eachFrameMillis;
+  lastFrameStart = now;
+  post_render = true;
+  return post_render;
+}
+
+// returns the load on the CPU as a percentage
+// this is based on how much of the time your app is spends rendering
+// frames.  This number can be higher than 100 if your app is rendering
+// really slowly.
+int Arduboy::cpuLoad()
+{
+  return lastFrameDurationMs * 100 / eachFrameMillis;
+}
+
+// seed the random number generator with entropy from the temperature,
+// voltage reading, and microseconds since boot.
+// this method is still most effective when called semi-randomly such
+// as after a user hits a button to start a game or other semi-random
+// events
+void Arduboy::initRandomSeed()
+{
+}
+
+uint16_t Arduboy::rawADC(byte adc_bits)
+{
+  return 0;
+}
+
+
+unsigned char* Arduboy::getBuffer() {
+  return sBuffer;
+}
+
+uint8_t Arduboy::width() {
+  return WIDTH;
+}
+
+uint8_t Arduboy::height() {
+  return HEIGHT;
+}
+
+
+void Arduboy::poll()
+{
+  previousButtonState = currentButtonState;
+  currentButtonState = getInput();
+}
+
+// returns true if the button mask passed in is pressed
+//
+//   if (pressed(LEFT_BUTTON + A_BUTTON))
+boolean Arduboy::pressed(uint8_t buttons)
+{
+  uint8_t button_state = getInput();
+  return (button_state & buttons) == buttons;
+}
+
+// returns true if the button mask passed in not pressed
+//
+//   if (not_pressed(LEFT_BUTTON))
+boolean Arduboy::notPressed(uint8_t buttons)
+{
+  uint8_t button_state = getInput();
+  return (button_state & buttons) == 0;
+}
+
+// returns true if a button has just been pressed
+// if the button has been held down for multiple frames this will return
+// false.  You should only use this to poll a single button.
+boolean Arduboy::justPressed(uint8_t button)
+{
+  uint8_t button_state = getInput();
+  return (!(previousButtonState & button) && (currentButtonState & button));
+}
+
+
+
+uint8_t Arduboy::getInput()
+{
+  uint8_t buttons;
+
+  return buttons;
+}
+
+void Arduboy::swap(int16_t& a, int16_t& b) {
+  int temp = a;
+  a = b;
+  b = temp;
+}
+
+
+/* AUDIO */
+
+void ArduboyAudio::on() {
+
+  audio_enabled = true;
+}
+
+bool ArduboyAudio::enabled() {
+  return audio_enabled;
+}
+
+void ArduboyAudio::off() {
+
+  audio_enabled = false;
+}
+
+void ArduboyAudio::saveOnOff() {
+}
+
+void ArduboyAudio::setup() {
+
+}
+
+void ArduboyAudio::tone(unsigned int frequency, unsigned long duration)
+{
+
+}
+
+
+/////////////////////////
+// Sprites by Dreamer3 //
+/////////////////////////
+Sprites::Sprites(Arduboy &a)
+{
+  arduboy = &a;
+  sBuffer = arduboy->getBuffer();
+}
+
+// new API
+
+void Sprites::drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
+                               const uint8_t *mask, uint8_t frame, uint8_t mask_frame)
+{
+  draw(x, y, bitmap, frame, mask, mask_frame, SPRITE_MASKED);
+}
+
+void Sprites::drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
+{
+  draw(x, y, bitmap, frame, NULL, 0, SPRITE_OVERWRITE);
+}
+
+void Sprites::drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
+{
+  draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK_ERASE);
+}
+
+void Sprites::drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
+{
+  draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK);
+}
+
+void Sprites::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
+{
+  draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK);
+}
+
+
+//common functions
+void Sprites::draw(int16_t x, int16_t y,
+                   const uint8_t *bitmap, uint8_t frame,
+                   const uint8_t *mask, uint8_t sprite_frame,
+                   uint8_t drawMode
+                  )
+{
+  unsigned int frame_offset;
+
+  if (bitmap == NULL)
+    return;
+
+  uint8_t width = pgm_read_byte(bitmap);
+  uint8_t height = pgm_read_byte(++bitmap);
+  bitmap++;
+  if (frame > 0 || sprite_frame > 0) {
+    frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
+    // sprite plus mask uses twice as much space for each frame
+    if (drawMode == SPRITE_PLUS_MASK) {
+      frame_offset *= 2;
+    } else if (mask != NULL) {
+      mask += sprite_frame * frame_offset;
+    }
+    bitmap += frame * frame_offset;
+  }
+
+  // if we're detecting the draw mode then base it on whether a mask
+  // was passed as a separate object
+  if (drawMode == SPRITE_AUTO_MODE) {
+    drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
+  }
+
+  drawBitmap(x, y, bitmap, mask, width, height, drawMode);
+}
+
+void Sprites::drawBitmap(int16_t x, int16_t y,
+                         const uint8_t *bitmap, const uint8_t *mask,
+                         int8_t w, int8_t h, uint8_t draw_mode) {
+  // no need to draw at all of we're offscreen
+  if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
+    return;
+
+  if (bitmap == NULL)
+    return;
+
+  // xOffset technically doesn't need to be 16 bit but the math operations
+  // are measurably faster if it is
+  uint16_t xOffset, ofs;
+  int8_t yOffset = abs(y) % 8;
+  int8_t sRow = y / 8;
+  uint8_t loop_h, start_h, rendered_width;
+
+  if (y < 0 && yOffset > 0) {
+    sRow--;
+    yOffset = 8 - yOffset;
+  }
+
+  // if the left side of the render is offscreen skip those loops
+  if (x < 0) {
+    xOffset = abs(x);
+  } else {
+    xOffset = 0;
+  }
+
+  // if the right side of the render is offscreen skip those loops
+  if (x + w > WIDTH - 1) {
+    rendered_width = ((WIDTH - x) - xOffset);
+  } else {
+    rendered_width = (w - xOffset);
+  }
+
+  // if the top side of the render is offscreen skip those loops
+  if (sRow < -1) {
+    start_h = abs(sRow) - 1;
+  } else {
+    start_h = 0;
+  }
+
+  loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
+
+  // if (sRow + loop_h - 1 > (HEIGHT/8)-1)
+  if (sRow + loop_h > (HEIGHT / 8)) {
+    loop_h = (HEIGHT / 8) - sRow;
+  }
+
+  // prepare variables for loops later so we can compare with 0
+  // instead of comparing two variables
+  loop_h -= start_h;
+
+  sRow += start_h;
+  ofs = (sRow * WIDTH) + x + xOffset;
+  uint8_t *bofs = (uint8_t *)bitmap + (start_h * w) + xOffset;
+  uint8_t *mask_ofs;
+  if (mask != 0)
+    mask_ofs = (uint8_t *)mask + (start_h * w) + xOffset;
+  uint8_t data;
+
+  uint8_t mul_amt = 1 << yOffset;
+  uint16_t mask_data;
+  uint16_t bitmap_data;
+
+  switch (draw_mode) {
+    case SPRITE_UNMASKED:
+      // we only want to mask the 8 bits of our own sprite, so we can
+      // calculate the mask before the start of the loop
+      mask_data = ~(0xFF * mul_amt);
+      // really if yOffset = 0 you have a faster case here that could be
+      // optimized
+      for (uint8_t a = 0; a < loop_h; a++) {
+        for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
+          bitmap_data = pgm_read_byte(bofs) * mul_amt;
+
+          if (sRow >= 0) {
+            data = sBuffer[ofs];
+            data &= (uint8_t)(mask_data);
+            data |= (uint8_t)(bitmap_data);
+            sBuffer[ofs] = data;
+          }
+          if (yOffset != 0 && sRow < 7) {
+            data = sBuffer[ofs + WIDTH];
+            data &= (*((unsigned char *) (&mask_data) + 1));
+            data |= (*((unsigned char *) (&bitmap_data) + 1));
+            sBuffer[ofs + WIDTH] = data;
+          }
+          ofs++;
+          bofs++;
+        }
+        sRow++;
+        bofs += w - rendered_width;
+        ofs += WIDTH - rendered_width;
+      }
+      break;
+
+    case SPRITE_IS_MASK:
+      for (uint8_t a = 0; a < loop_h; a++) {
+        for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
+          bitmap_data = pgm_read_byte(bofs) * mul_amt;
+          if (sRow >= 0) {
+            sBuffer[ofs] |= (uint8_t)(bitmap_data);
+          }
+          if (yOffset != 0 && sRow < 7) {
+            sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
+          }
+          ofs++;
+          bofs++;
+        }
+        sRow++;
+        bofs += w - rendered_width;
+        ofs += WIDTH - rendered_width;
+      }
+      break;
+
+    case SPRITE_IS_MASK_ERASE:
+      for (uint8_t a = 0; a < loop_h; a++) {
+        for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
+          bitmap_data = pgm_read_byte(bofs) * mul_amt;
+          if (sRow >= 0) {
+            sBuffer[ofs]  &= ~(uint8_t)(bitmap_data);
+          }
+          if (yOffset != 0 && sRow < 7) {
+            sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
+          }
+          ofs++;
+          bofs++;
+        }
+        sRow++;
+        bofs += w - rendered_width;
+        ofs += WIDTH - rendered_width;
+      }
+      break;
+
+    case SPRITE_MASKED:
+      for (uint8_t a = 0; a < loop_h; a++) {
+        for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
+          // NOTE: you might think in the yOffset==0 case that this results
+          // in more effort, but in all my testing the compiler was forcing
+          // 16-bit math to happen here anyways, so this isn't actually
+          // compiling to more code than it otherwise would. If the offset
+          // is 0 the high part of the word will just never be used.
+
+          // load data and bit shift
+          // mask needs to be bit flipped
+          mask_data = ~(pgm_read_byte(mask_ofs) * mul_amt);
+          bitmap_data = pgm_read_byte(bofs) * mul_amt;
+
+          if (sRow >= 0) {
+            data = sBuffer[ofs];
+            data &= (uint8_t)(mask_data);
+            data |= (uint8_t)(bitmap_data);
+            sBuffer[ofs] = data;
+          }
+          if (yOffset != 0 && sRow < 7) {
+            data = sBuffer[ofs + WIDTH];
+            data &= (*((unsigned char *) (&mask_data) + 1));
+            data |= (*((unsigned char *) (&bitmap_data) + 1));
+            sBuffer[ofs + WIDTH] = data;
+          }
+          ofs++;
+          mask_ofs++;
+          bofs++;
+        }
+        sRow++;
+        bofs += w - rendered_width;
+        mask_ofs += w - rendered_width;
+        ofs += WIDTH - rendered_width;
+      }
+      break;
+
+
+    case SPRITE_PLUS_MASK:
+      // *2 because we use double the bits (mask + bitmap)
+      bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2);
+
+      uint8_t xi = rendered_width; // used for x loop below
+      uint8_t yi = loop_h; // used for y loop below
+
+      asm volatile(
+        "push r28\n" // save Y
+        "push r29\n"
+        "mov r28, %A[buffer_page2_ofs]\n" // Y = buffer page 2 offset
+        "mov r29, %B[buffer_page2_ofs]\n"
+        "loop_y:\n"
+        "loop_x:\n"
+        // load bitmap and mask data
+        "lpm %A[bitmap_data], Z+\n"
+        "lpm %A[mask_data], Z+\n"
+
+        // shift mask and buffer data
+        "tst %[yOffset]\n"
+        "breq skip_shifting\n"
+        "mul %A[bitmap_data], %[mul_amt]\n"
+        "mov %A[bitmap_data], r0\n"
+        "mov %B[bitmap_data], r1\n"
+        "mul %A[mask_data], %[mul_amt]\n"
+        "mov %A[mask_data], r0\n"
+        // "mov %B[mask_data], r1\n"
+
+
+        // SECOND PAGE
+        // if yOffset != 0 && sRow < 7
+        "cpi %[sRow], 7\n"
+        "brge end_second_page\n"
+        // then
+        "ld %[data], Y\n"
+        // "com %B[mask_data]\n" // invert high byte of mask
+        "com r1\n"
+        "and %[data], r1\n" // %B[mask_data]
+        "or %[data], %B[bitmap_data]\n"
+        // update buffer, increment
+        "st Y+, %[data]\n"
+
+        "end_second_page:\n"
+        "skip_shifting:\n"
+
+
+        // FIRST PAGE
+        "ld %[data], %a[buffer_ofs]\n"
+        // if sRow >= 0
+        "tst %[sRow]\n"
+        "brmi end_first_page\n"
+        // then
+        "com %A[mask_data]\n"
+        "and %[data], %A[mask_data]\n"
+        "or %[data], %A[bitmap_data]\n"
+
+        "end_first_page:\n"
+        // update buffer, increment
+        "st %a[buffer_ofs]+, %[data]\n"
+
+
+        // "x_loop_next:\n"
+        "dec %[xi]\n"
+        "brne loop_x\n"
+
+        // increment y
+        "next_loop_y:\n"
+        "dec %[yi]\n"
+        "breq finished\n"
+        "mov %[xi], %[x_count]\n" // reset x counter
+        // sRow++;
+        "inc %[sRow]\n"
+        "clr __zero_reg__\n"
+        // sprite_ofs += (w - rendered_width) * 2;
+        "add %A[sprite_ofs], %A[sprite_ofs_jump]\n"
+        "adc %B[sprite_ofs], __zero_reg__\n"
+        // buffer_ofs += WIDTH - rendered_width;
+        "add %A[buffer_ofs], %A[buffer_ofs_jump]\n"
+        "adc %B[buffer_ofs], __zero_reg__\n"
+        // buffer_ofs_page_2 += WIDTH - rendered_width;
+        "add r28, %A[buffer_ofs_jump]\n"
+        "adc r29, __zero_reg__\n"
+
+        "rjmp loop_y\n"
+        "finished:\n"
+        // put the Y register back in place
+        "pop r29\n"
+        "pop r28\n"
+        "clr __zero_reg__\n" // just in case
+        : [xi] "+&r" (xi),
+        [yi] "+&r" (yi),
+        [sRow] "+&a" (sRow), // CPI requires an upper register
+        [data] "+&r" (data),
+        [mask_data] "+&r" (mask_data),
+        [bitmap_data] "+&r" (bitmap_data)
+        :
+        [x_count] "r" (rendered_width),
+        [y_count] "r" (loop_h),
+        [sprite_ofs] "z" (bofs),
+        [buffer_ofs] "x" (sBuffer+ofs),
+        [buffer_page2_ofs] "r" (sBuffer+ofs+WIDTH), // Y pointer
+        [buffer_ofs_jump] "r" (WIDTH-rendered_width),
+        [sprite_ofs_jump] "r" ((w-rendered_width)*2),
+        [yOffset] "r" (yOffset),
+        [mul_amt] "r" (mul_amt)
+        :
+      );
+      break;
+
+  }
+}
+
+
+/////////////////////////////////
+// Basic Collision by Dreamer3 //
+/////////////////////////////////
+bool Arduboy::collide(Point point, Rect rect)
+{
+  // does point fall within the bounds of rect
+  return ((point.x >= rect.x) && (point.x < rect.x + rect.width) &&
+      (point.y >= rect.y) && (point.y < rect.y + rect.height));
+}
+
+bool Arduboy::collide(Rect rect1, Rect rect2)
+{
+  return !( rect2.x                 >=  rect1.x + rect1.width    ||
+            rect2.x + rect2.width   <=  rect1.x                ||
+            rect2.y                 >=  rect1.y + rect1.height ||
+            rect2.y + rect2.height  <=  rect1.y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core.h	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,332 @@
+#ifndef FABOY_BLE_H
+#define FABOY_BLE_H
+
+#include "Adafruit_SSD1306.h"
+#include <stdint.h>
+
+// main hardware compile flags
+
+#if !defined(FABOY_BLE)
+ #define FABOY_BLE      //< compile for the ble board  
+#endif
+
+// EEPROM settings
+#define EEPROM_VERSION 0
+#define EEPROM_BRIGHTNESS 1
+#define EEPROM_AUDIO_ON_OFF 2
+// we reserve the first 16 byte of EEPROM for system use
+#define EEPROM_STORAGE_SPACE_START 16 // and onward
+
+// eeprom settings above are needed for audio
+
+#define PIXEL_SAFE_MODE
+#define SAFE_MODE
+
+#ifdef FABOY_BLE
+#define SCL p12
+#define SDA p11
+#elif defined(FABOY_DEV)
+#define CS 19
+#define DC 8
+#define RST 18
+#else
+#define CS 12
+#define DC 4
+#define RST 6
+#endif
+
+#ifdef FABOY_BLE
+#define RED_LED p17
+#define GREEN_LED p18
+#define BLUE_LED p19
+//#define TX_LED p20
+//#define RX_LED p20
+
+#define PIN_LEFT_BUTTON p13
+#define PIN_RIGHT_BUTTON p14
+#define PIN_UP_BUTTON p15
+#define PIN_DOWN_BUTTON p16
+#define PIN_A_BUTTON p16
+#define PIN_B_BUTTON p16
+
+#define LEFT_BUTTON _BV(0)
+#define RIGHT_BUTTON _BV(1)
+#define UP_BUTTON _BV(2)
+#define DOWN_BUTTON _BV(3)
+#define A_BUTTON _BV(4)
+#define B_BUTTON _BV(5)
+
+#define PIN_SPEAKER_1 p20
+
+#elif defined(FABOY_DEV)
+#define LEFT_BUTTON _BV(5)
+#define RIGHT_BUTTON _BV(0)
+#define UP_BUTTON _BV(4)
+#define DOWN_BUTTON _BV(1)
+#define A_BUTTON _BV(3)
+#define B_BUTTON _BV(2)
+
+#define PIN_LEFT_BUTTON A2
+#define PIN_RIGHT_BUTTON A5
+#define PIN_UP_BUTTON A3
+#define PIN_DOWN_BUTTON A4
+#define PIN_A_BUTTON 0
+#define PIN_B_BUTTON 1
+
+#define RED_LED 10
+#define GREEN_LED 11
+#define BLUE_LED 9
+#define TX_LED 7
+#define RX_LED 7
+
+#define PIN_SPEAKER_1 5
+#define PIN_SPEAKER_2 13
+
+#else
+#error no platform defined !
+#endif
+
+#define WIDTH 128
+#define HEIGHT 64
+
+#define WHITE 1
+#define BLACK 0
+
+#define COLUMN_ADDRESS_END (WIDTH - 1) & 0x7F
+#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 0x07
+
+#define SPRITE_MASKED 1
+#define SPRITE_UNMASKED 2
+#define SPRITE_OVERWRITE 2
+#define SPRITE_PLUS_MASK 3
+#define SPRITE_IS_MASK 250
+#define SPRITE_IS_MASK_ERASE 251
+#define SPRITE_AUTO_MODE 255
+
+class ArduboyAudio
+{
+  public:
+    void setup();
+    void on();
+    void off();
+    void saveOnOff();
+    bool enabled();
+    void tone(unsigned int frequency, unsigned long duration);
+
+  protected:
+    bool audio_enabled;
+};
+
+struct Rect
+{
+  public:
+    int x;
+    int y;
+    uint8_t width;
+    int height;
+};
+
+struct Point
+{
+  public:
+    int x;
+    int y;
+};
+
+class Arduboy : public Adafruit_SSD1306_I2c
+{
+  public:
+    Arduboy();
+//    void LCDDataMode();
+//    void LCDCommandMode();
+
+    I2C i2c;
+
+    uint8_t getInput();
+    void poll();
+    bool pressed(uint8_t buttons);
+    bool notPressed(uint8_t buttons);
+    bool justPressed(uint8_t buttons);
+    void start();
+    void saveMuchPower();
+    void idle();
+    void blank();
+    void clearDisplay();
+    void display();
+//    void drawScreen(const unsigned char *image);
+//    void drawScreen(unsigned char image[]);
+//    void drawPixel(int x, int y, uint8_t color);
+//    uint8_t getPixel(uint8_t x, uint8_t y);
+//    void drawCircle(int16_t x0, int16_t y0, int16_t r, uint8_t color);
+//    void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint8_t color);
+//    void fillCircle(int16_t x0, int16_t y0, int16_t r, uint8_t color);
+//    void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint8_t color);
+//    void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color);
+//    void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color);
+//    void drawFastVLine(int16_t x, int16_t y, int16_t h, uint8_t color);
+//    void drawFastHLine(int16_t x, int16_t y, int16_t w, uint8_t color);
+//    void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color);
+//    void fillScreen(uint8_t color);
+//    void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint8_t color);
+//    void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint8_t color);
+    void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint8_t color);
+    void drawCompressed(int16_t sx, int16_t sy, const uint8_t *bitmap, uint8_t color);
+    unsigned char* getBuffer();
+    uint8_t width();
+    uint8_t height();
+    virtual size_t write(uint8_t);
+    void initRandomSeed();
+    void swap(int16_t& a, int16_t& b);
+
+    //ArduboyTunes tunes;
+    ArduboyAudio audio;
+
+    void setFrameRate(uint8_t rate);
+    bool nextFrame();
+    bool everyXFrames(uint8_t frames);
+    int cpuLoad();
+    uint8_t frameRate ;
+    uint16_t frameCount ;
+    uint8_t eachFrameMillis ;
+    long lastFrameStart;
+    long nextFrameStart ;
+    bool post_render;
+    uint8_t lastFrameDurationMs;
+
+    bool static collide(Point point, Rect rect);
+    bool static collide(Rect rect, Rect rect2);
+
+  private:
+    static unsigned char sBuffer[(HEIGHT * WIDTH) / 8];
+
+    void bootLCD() __attribute__((always_inline));
+    void safeMode() __attribute__((always_inline));
+    void slowCPU() __attribute__((always_inline));
+    uint8_t readCapacitivePin(int pinToMeasure);
+    uint8_t readCapXtal(int pinToMeasure);
+    uint16_t rawADC(char adc_bits);
+    volatile uint8_t *mosiport, *clkport, *csport, *dcport;
+    uint8_t mosipinmask, clkpinmask, cspinmask, dcpinmask;
+    uint8_t currentButtonState ;
+    uint8_t previousButtonState ;
+};
+
+
+/////////////////////////////////
+//      sprites by Dreamer3    //
+/////////////////////////////////
+class Sprites
+{
+  public:
+    Sprites(Arduboy &arduboy);
+    
+    // drawExternalMask() uses a separate mask to mask image (MASKED)
+    //
+    // image  mask   before  after
+    //
+    // .....  .OOO.  .....   .....
+    // ..O..  OOOOO  .....   ..O..
+    // OO.OO  OO.OO  .....   OO.OO
+    // ..O..  OOOOO  .....   ..O..
+    // .....  .OOO.  .....   .....
+    //
+    // image  mask   before  after
+    //
+    // .....  .OOO.  OOOOO   O...O
+    // ..O..  OOOOO  OOOOO   ..O..
+    // OO.OO  OOOOO  OOOOO   OO.OO
+    // ..O..  OOOOO  OOOOO   ..O..
+    // .....  .OOO.  OOOOO   O...O
+    //
+    void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap, const uint8_t *mask, uint8_t frame, uint8_t mask_frame);
+
+    // drawPlusMask has the same behavior as drawExternalMask except the
+    // data is arranged in byte tuples interposing the mask right along
+    // with the image data (SPRITE_PLUS_MASK)
+    //
+    // typical image data (8 bytes):
+    // [I][I][I][I][I][I][I][I]
+    //
+    // interposed image/mask data (8 byes):
+    // [I][M][I][M][I][M][I][M]
+    //
+    // The byte order does not change, just for every image byte you mix
+    // in it's matching mask byte.  Softare tools make easy work of this.
+    //
+    // See: https://github.com/yyyc514/img2ard
+    void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
+
+    // drawOverwrite() replaces the existing content completely (UNMASKED)
+    //
+    // image  before  after
+    //
+    // .....  .....   .....
+    // ..O..  .....   ..O..
+    // OO.OO  .....   OO.OO
+    // ..O..  .....   ..O..
+    // .....  .....   .....
+    //
+    // image  before  after
+    //
+    // .....  OOOOO   .....
+    // ..O..  OOOOO   ..O..
+    // OO.OO  OOOOO   OO.OO
+    // ..O..  OOOOO   ..O..
+    // .....  OOOOO   .....
+    //
+    void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
+
+    // drawErase() removes the lit pixels in the image from the display
+    // (SPRITE_IS_MASK_ERASE)
+    //
+    // image  before  after
+    //
+    // .....  .....   .....
+    // ..O..  .....   .....
+    // OO.OO  .....   .....
+    // ..O..  .....   .....
+    // .....  .....   .....
+    //
+    // image  before  after
+    //
+    // .....  OOOOO   OOOOO
+    // ..O..  OOOOO   OO.OO
+    // OO.OO  OOOOO   ..O..
+    // ..O..  OOOOO   OO.OO
+    // .....  OOOOO   OOOOO
+    //
+
+    void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
+
+    // drawSelfMasked() only draws lit pixels, black pixels in
+    // your image are treated as "transparent" (SPRITE_IS_MASK)
+    //
+    // image  before  after
+    //
+    // .....  .....   .....
+    // ..O..  .....   ..O..
+    // OO.OO  .....   OO.OO
+    // ..O..  .....   ..O..
+    // .....  .....   .....
+    //
+    // image  before  after
+    //
+    // .....  OOOOO   OOOOO  (no change because all pixels were
+    // ..O..  OOOOO   OOOOO  already white)
+    // OO.OO  OOOOO   OOOOO
+    // ..O..  OOOOO   OOOOO
+    // .....  OOOOO   OOOOO
+    //
+    void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
+    // master function, needs to be abstracted into sep function for
+    // every render type
+    void draw(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame, const uint8_t *mask, uint8_t sprite_frame, uint8_t drawMode);
+    void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, const uint8_t *mask, int8_t w, int8_t h, uint8_t draw_mode);
+
+  private:
+
+    Arduboy *arduboy;
+    unsigned char *sBuffer;
+};
+
+#endif
Binary file img/uvision.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,17 @@
+#include "mbed.h"
+#include "core.h"
+
+DigitalOut led1(LED1);
+
+// main() runs in its own thread in the OS
+// (note the calls to Thread::wait below for delays)
+int main() {
+    
+    Arduboy arduboy; 
+    
+    while (true) {
+        led1 = !led1;
+        Thread::wait(500);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Tue Dec 27 11:27:14 2016 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#c3b9436e12610acaab723f730ab15b48a539a5ac