Arduboy lib for NRF and mbed

Dependencies:   Adafruit_GFX

Committer:
lmayencou
Date:
Tue Dec 27 11:27:14 2016 +0000
Revision:
0:2b6d7af79c9c
first publish

Who changed what in which revision?

UserRevisionLine numberNew contents of line
lmayencou 0:2b6d7af79c9c 1 #include "core.h"
lmayencou 0:2b6d7af79c9c 2
lmayencou 0:2b6d7af79c9c 3 unsigned char Arduboy::sBuffer[];
lmayencou 0:2b6d7af79c9c 4
lmayencou 0:2b6d7af79c9c 5 Arduboy::Arduboy() :
lmayencou 0:2b6d7af79c9c 6 i2c(SDA,SCL),
lmayencou 0:2b6d7af79c9c 7 Adafruit_SSD1306_I2c(i2c,p13)
lmayencou 0:2b6d7af79c9c 8 { }
lmayencou 0:2b6d7af79c9c 9
lmayencou 0:2b6d7af79c9c 10 void Arduboy::start()
lmayencou 0:2b6d7af79c9c 11 {
lmayencou 0:2b6d7af79c9c 12 #if F_CPU == 8000000L
lmayencou 0:2b6d7af79c9c 13 slowCPU();
lmayencou 0:2b6d7af79c9c 14 #endif
lmayencou 0:2b6d7af79c9c 15
lmayencou 0:2b6d7af79c9c 16 // init pin
lmayencou 0:2b6d7af79c9c 17 DigitalOut led1(LED1);
lmayencou 0:2b6d7af79c9c 18
lmayencou 0:2b6d7af79c9c 19 bootLCD();
lmayencou 0:2b6d7af79c9c 20
lmayencou 0:2b6d7af79c9c 21 #ifdef SAFE_MODE
lmayencou 0:2b6d7af79c9c 22 if (pressed(LEFT_BUTTON + UP_BUTTON))
lmayencou 0:2b6d7af79c9c 23 safeMode();
lmayencou 0:2b6d7af79c9c 24 #endif
lmayencou 0:2b6d7af79c9c 25
lmayencou 0:2b6d7af79c9c 26
lmayencou 0:2b6d7af79c9c 27 audio.setup();
lmayencou 0:2b6d7af79c9c 28 saveMuchPower();
lmayencou 0:2b6d7af79c9c 29 }
lmayencou 0:2b6d7af79c9c 30
lmayencou 0:2b6d7af79c9c 31 #if F_CPU == 8000000L
lmayencou 0:2b6d7af79c9c 32 // if we're compiling for 8Mhz we need to slow the CPU down because the
lmayencou 0:2b6d7af79c9c 33 // hardware clock on the Arduboy is 16MHz
lmayencou 0:2b6d7af79c9c 34 void Arduboy::slowCPU()
lmayencou 0:2b6d7af79c9c 35 {
lmayencou 0:2b6d7af79c9c 36
lmayencou 0:2b6d7af79c9c 37 }
lmayencou 0:2b6d7af79c9c 38 #endif
lmayencou 0:2b6d7af79c9c 39
lmayencou 0:2b6d7af79c9c 40 void Arduboy::bootLCD()
lmayencou 0:2b6d7af79c9c 41 {
lmayencou 0:2b6d7af79c9c 42 clearDisplay();
lmayencou 0:2b6d7af79c9c 43 display();
lmayencou 0:2b6d7af79c9c 44 }
lmayencou 0:2b6d7af79c9c 45
lmayencou 0:2b6d7af79c9c 46 // Safe Mode is engaged by holding down both the LEFT button and UP button
lmayencou 0:2b6d7af79c9c 47 // when plugging the device into USB. It puts your device into a tight
lmayencou 0:2b6d7af79c9c 48 // loop and allows it to be reprogrammed even if you have uploaded a very
lmayencou 0:2b6d7af79c9c 49 // broken sketch that interferes with the normal USB triggered auto-reboot
lmayencou 0:2b6d7af79c9c 50 // functionality of the device.
lmayencou 0:2b6d7af79c9c 51 void Arduboy::safeMode()
lmayencou 0:2b6d7af79c9c 52 {
lmayencou 0:2b6d7af79c9c 53 display(); // too avoid random gibberish
lmayencou 0:2b6d7af79c9c 54 while (true) {};
lmayencou 0:2b6d7af79c9c 55 }
lmayencou 0:2b6d7af79c9c 56
lmayencou 0:2b6d7af79c9c 57 /* Power Management */
lmayencou 0:2b6d7af79c9c 58
lmayencou 0:2b6d7af79c9c 59 void Arduboy::idle()
lmayencou 0:2b6d7af79c9c 60 {
lmayencou 0:2b6d7af79c9c 61 }
lmayencou 0:2b6d7af79c9c 62
lmayencou 0:2b6d7af79c9c 63 void Arduboy::saveMuchPower()
lmayencou 0:2b6d7af79c9c 64 {
lmayencou 0:2b6d7af79c9c 65 }
lmayencou 0:2b6d7af79c9c 66
lmayencou 0:2b6d7af79c9c 67
lmayencou 0:2b6d7af79c9c 68 /* Frame management */
lmayencou 0:2b6d7af79c9c 69
lmayencou 0:2b6d7af79c9c 70 void Arduboy::setFrameRate(uint8_t rate)
lmayencou 0:2b6d7af79c9c 71 {
lmayencou 0:2b6d7af79c9c 72 frameRate = rate;
lmayencou 0:2b6d7af79c9c 73 eachFrameMillis = 1000 / rate;
lmayencou 0:2b6d7af79c9c 74 }
lmayencou 0:2b6d7af79c9c 75
lmayencou 0:2b6d7af79c9c 76 bool Arduboy::everyXFrames(uint8_t frames)
lmayencou 0:2b6d7af79c9c 77 {
lmayencou 0:2b6d7af79c9c 78 return frameCount % frames == 0;
lmayencou 0:2b6d7af79c9c 79 }
lmayencou 0:2b6d7af79c9c 80
lmayencou 0:2b6d7af79c9c 81 bool Arduboy::nextFrame()
lmayencou 0:2b6d7af79c9c 82 {
lmayencou 0:2b6d7af79c9c 83 long now = millis();
lmayencou 0:2b6d7af79c9c 84 uint8_t remaining;
lmayencou 0:2b6d7af79c9c 85
lmayencou 0:2b6d7af79c9c 86 // post render
lmayencou 0:2b6d7af79c9c 87 if (post_render) {
lmayencou 0:2b6d7af79c9c 88 lastFrameDurationMs = now - lastFrameStart;
lmayencou 0:2b6d7af79c9c 89 frameCount++;
lmayencou 0:2b6d7af79c9c 90 post_render = false;
lmayencou 0:2b6d7af79c9c 91 }
lmayencou 0:2b6d7af79c9c 92
lmayencou 0:2b6d7af79c9c 93 // if it's not time for the next frame yet
lmayencou 0:2b6d7af79c9c 94 if (now < nextFrameStart) {
lmayencou 0:2b6d7af79c9c 95 remaining = nextFrameStart - now;
lmayencou 0:2b6d7af79c9c 96 // if we have more than 1ms to spare, lets sleep
lmayencou 0:2b6d7af79c9c 97 // we should be woken up by timer0 every 1ms, so this should be ok
lmayencou 0:2b6d7af79c9c 98 if (remaining > 1)
lmayencou 0:2b6d7af79c9c 99 idle();
lmayencou 0:2b6d7af79c9c 100 return false;
lmayencou 0:2b6d7af79c9c 101 }
lmayencou 0:2b6d7af79c9c 102
lmayencou 0:2b6d7af79c9c 103 // pre-render
lmayencou 0:2b6d7af79c9c 104
lmayencou 0:2b6d7af79c9c 105 // technically next frame should be last frame + each frame but if we're
lmayencou 0:2b6d7af79c9c 106 // running a slow render we would constnatly be behind the clock
lmayencou 0:2b6d7af79c9c 107 // keep an eye on this and see how it works. If it works well the
lmayencou 0:2b6d7af79c9c 108 // lastFrameStart variable could be eliminated completely
lmayencou 0:2b6d7af79c9c 109 nextFrameStart = now + eachFrameMillis;
lmayencou 0:2b6d7af79c9c 110 lastFrameStart = now;
lmayencou 0:2b6d7af79c9c 111 post_render = true;
lmayencou 0:2b6d7af79c9c 112 return post_render;
lmayencou 0:2b6d7af79c9c 113 }
lmayencou 0:2b6d7af79c9c 114
lmayencou 0:2b6d7af79c9c 115 // returns the load on the CPU as a percentage
lmayencou 0:2b6d7af79c9c 116 // this is based on how much of the time your app is spends rendering
lmayencou 0:2b6d7af79c9c 117 // frames. This number can be higher than 100 if your app is rendering
lmayencou 0:2b6d7af79c9c 118 // really slowly.
lmayencou 0:2b6d7af79c9c 119 int Arduboy::cpuLoad()
lmayencou 0:2b6d7af79c9c 120 {
lmayencou 0:2b6d7af79c9c 121 return lastFrameDurationMs * 100 / eachFrameMillis;
lmayencou 0:2b6d7af79c9c 122 }
lmayencou 0:2b6d7af79c9c 123
lmayencou 0:2b6d7af79c9c 124 // seed the random number generator with entropy from the temperature,
lmayencou 0:2b6d7af79c9c 125 // voltage reading, and microseconds since boot.
lmayencou 0:2b6d7af79c9c 126 // this method is still most effective when called semi-randomly such
lmayencou 0:2b6d7af79c9c 127 // as after a user hits a button to start a game or other semi-random
lmayencou 0:2b6d7af79c9c 128 // events
lmayencou 0:2b6d7af79c9c 129 void Arduboy::initRandomSeed()
lmayencou 0:2b6d7af79c9c 130 {
lmayencou 0:2b6d7af79c9c 131 }
lmayencou 0:2b6d7af79c9c 132
lmayencou 0:2b6d7af79c9c 133 uint16_t Arduboy::rawADC(byte adc_bits)
lmayencou 0:2b6d7af79c9c 134 {
lmayencou 0:2b6d7af79c9c 135 return 0;
lmayencou 0:2b6d7af79c9c 136 }
lmayencou 0:2b6d7af79c9c 137
lmayencou 0:2b6d7af79c9c 138
lmayencou 0:2b6d7af79c9c 139 unsigned char* Arduboy::getBuffer() {
lmayencou 0:2b6d7af79c9c 140 return sBuffer;
lmayencou 0:2b6d7af79c9c 141 }
lmayencou 0:2b6d7af79c9c 142
lmayencou 0:2b6d7af79c9c 143 uint8_t Arduboy::width() {
lmayencou 0:2b6d7af79c9c 144 return WIDTH;
lmayencou 0:2b6d7af79c9c 145 }
lmayencou 0:2b6d7af79c9c 146
lmayencou 0:2b6d7af79c9c 147 uint8_t Arduboy::height() {
lmayencou 0:2b6d7af79c9c 148 return HEIGHT;
lmayencou 0:2b6d7af79c9c 149 }
lmayencou 0:2b6d7af79c9c 150
lmayencou 0:2b6d7af79c9c 151
lmayencou 0:2b6d7af79c9c 152 void Arduboy::poll()
lmayencou 0:2b6d7af79c9c 153 {
lmayencou 0:2b6d7af79c9c 154 previousButtonState = currentButtonState;
lmayencou 0:2b6d7af79c9c 155 currentButtonState = getInput();
lmayencou 0:2b6d7af79c9c 156 }
lmayencou 0:2b6d7af79c9c 157
lmayencou 0:2b6d7af79c9c 158 // returns true if the button mask passed in is pressed
lmayencou 0:2b6d7af79c9c 159 //
lmayencou 0:2b6d7af79c9c 160 // if (pressed(LEFT_BUTTON + A_BUTTON))
lmayencou 0:2b6d7af79c9c 161 boolean Arduboy::pressed(uint8_t buttons)
lmayencou 0:2b6d7af79c9c 162 {
lmayencou 0:2b6d7af79c9c 163 uint8_t button_state = getInput();
lmayencou 0:2b6d7af79c9c 164 return (button_state & buttons) == buttons;
lmayencou 0:2b6d7af79c9c 165 }
lmayencou 0:2b6d7af79c9c 166
lmayencou 0:2b6d7af79c9c 167 // returns true if the button mask passed in not pressed
lmayencou 0:2b6d7af79c9c 168 //
lmayencou 0:2b6d7af79c9c 169 // if (not_pressed(LEFT_BUTTON))
lmayencou 0:2b6d7af79c9c 170 boolean Arduboy::notPressed(uint8_t buttons)
lmayencou 0:2b6d7af79c9c 171 {
lmayencou 0:2b6d7af79c9c 172 uint8_t button_state = getInput();
lmayencou 0:2b6d7af79c9c 173 return (button_state & buttons) == 0;
lmayencou 0:2b6d7af79c9c 174 }
lmayencou 0:2b6d7af79c9c 175
lmayencou 0:2b6d7af79c9c 176 // returns true if a button has just been pressed
lmayencou 0:2b6d7af79c9c 177 // if the button has been held down for multiple frames this will return
lmayencou 0:2b6d7af79c9c 178 // false. You should only use this to poll a single button.
lmayencou 0:2b6d7af79c9c 179 boolean Arduboy::justPressed(uint8_t button)
lmayencou 0:2b6d7af79c9c 180 {
lmayencou 0:2b6d7af79c9c 181 uint8_t button_state = getInput();
lmayencou 0:2b6d7af79c9c 182 return (!(previousButtonState & button) && (currentButtonState & button));
lmayencou 0:2b6d7af79c9c 183 }
lmayencou 0:2b6d7af79c9c 184
lmayencou 0:2b6d7af79c9c 185
lmayencou 0:2b6d7af79c9c 186
lmayencou 0:2b6d7af79c9c 187 uint8_t Arduboy::getInput()
lmayencou 0:2b6d7af79c9c 188 {
lmayencou 0:2b6d7af79c9c 189 uint8_t buttons;
lmayencou 0:2b6d7af79c9c 190
lmayencou 0:2b6d7af79c9c 191 return buttons;
lmayencou 0:2b6d7af79c9c 192 }
lmayencou 0:2b6d7af79c9c 193
lmayencou 0:2b6d7af79c9c 194 void Arduboy::swap(int16_t& a, int16_t& b) {
lmayencou 0:2b6d7af79c9c 195 int temp = a;
lmayencou 0:2b6d7af79c9c 196 a = b;
lmayencou 0:2b6d7af79c9c 197 b = temp;
lmayencou 0:2b6d7af79c9c 198 }
lmayencou 0:2b6d7af79c9c 199
lmayencou 0:2b6d7af79c9c 200
lmayencou 0:2b6d7af79c9c 201 /* AUDIO */
lmayencou 0:2b6d7af79c9c 202
lmayencou 0:2b6d7af79c9c 203 void ArduboyAudio::on() {
lmayencou 0:2b6d7af79c9c 204
lmayencou 0:2b6d7af79c9c 205 audio_enabled = true;
lmayencou 0:2b6d7af79c9c 206 }
lmayencou 0:2b6d7af79c9c 207
lmayencou 0:2b6d7af79c9c 208 bool ArduboyAudio::enabled() {
lmayencou 0:2b6d7af79c9c 209 return audio_enabled;
lmayencou 0:2b6d7af79c9c 210 }
lmayencou 0:2b6d7af79c9c 211
lmayencou 0:2b6d7af79c9c 212 void ArduboyAudio::off() {
lmayencou 0:2b6d7af79c9c 213
lmayencou 0:2b6d7af79c9c 214 audio_enabled = false;
lmayencou 0:2b6d7af79c9c 215 }
lmayencou 0:2b6d7af79c9c 216
lmayencou 0:2b6d7af79c9c 217 void ArduboyAudio::saveOnOff() {
lmayencou 0:2b6d7af79c9c 218 }
lmayencou 0:2b6d7af79c9c 219
lmayencou 0:2b6d7af79c9c 220 void ArduboyAudio::setup() {
lmayencou 0:2b6d7af79c9c 221
lmayencou 0:2b6d7af79c9c 222 }
lmayencou 0:2b6d7af79c9c 223
lmayencou 0:2b6d7af79c9c 224 void ArduboyAudio::tone(unsigned int frequency, unsigned long duration)
lmayencou 0:2b6d7af79c9c 225 {
lmayencou 0:2b6d7af79c9c 226
lmayencou 0:2b6d7af79c9c 227 }
lmayencou 0:2b6d7af79c9c 228
lmayencou 0:2b6d7af79c9c 229
lmayencou 0:2b6d7af79c9c 230 /////////////////////////
lmayencou 0:2b6d7af79c9c 231 // Sprites by Dreamer3 //
lmayencou 0:2b6d7af79c9c 232 /////////////////////////
lmayencou 0:2b6d7af79c9c 233 Sprites::Sprites(Arduboy &a)
lmayencou 0:2b6d7af79c9c 234 {
lmayencou 0:2b6d7af79c9c 235 arduboy = &a;
lmayencou 0:2b6d7af79c9c 236 sBuffer = arduboy->getBuffer();
lmayencou 0:2b6d7af79c9c 237 }
lmayencou 0:2b6d7af79c9c 238
lmayencou 0:2b6d7af79c9c 239 // new API
lmayencou 0:2b6d7af79c9c 240
lmayencou 0:2b6d7af79c9c 241 void Sprites::drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
lmayencou 0:2b6d7af79c9c 242 const uint8_t *mask, uint8_t frame, uint8_t mask_frame)
lmayencou 0:2b6d7af79c9c 243 {
lmayencou 0:2b6d7af79c9c 244 draw(x, y, bitmap, frame, mask, mask_frame, SPRITE_MASKED);
lmayencou 0:2b6d7af79c9c 245 }
lmayencou 0:2b6d7af79c9c 246
lmayencou 0:2b6d7af79c9c 247 void Sprites::drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
lmayencou 0:2b6d7af79c9c 248 {
lmayencou 0:2b6d7af79c9c 249 draw(x, y, bitmap, frame, NULL, 0, SPRITE_OVERWRITE);
lmayencou 0:2b6d7af79c9c 250 }
lmayencou 0:2b6d7af79c9c 251
lmayencou 0:2b6d7af79c9c 252 void Sprites::drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
lmayencou 0:2b6d7af79c9c 253 {
lmayencou 0:2b6d7af79c9c 254 draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK_ERASE);
lmayencou 0:2b6d7af79c9c 255 }
lmayencou 0:2b6d7af79c9c 256
lmayencou 0:2b6d7af79c9c 257 void Sprites::drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
lmayencou 0:2b6d7af79c9c 258 {
lmayencou 0:2b6d7af79c9c 259 draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK);
lmayencou 0:2b6d7af79c9c 260 }
lmayencou 0:2b6d7af79c9c 261
lmayencou 0:2b6d7af79c9c 262 void Sprites::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
lmayencou 0:2b6d7af79c9c 263 {
lmayencou 0:2b6d7af79c9c 264 draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK);
lmayencou 0:2b6d7af79c9c 265 }
lmayencou 0:2b6d7af79c9c 266
lmayencou 0:2b6d7af79c9c 267
lmayencou 0:2b6d7af79c9c 268 //common functions
lmayencou 0:2b6d7af79c9c 269 void Sprites::draw(int16_t x, int16_t y,
lmayencou 0:2b6d7af79c9c 270 const uint8_t *bitmap, uint8_t frame,
lmayencou 0:2b6d7af79c9c 271 const uint8_t *mask, uint8_t sprite_frame,
lmayencou 0:2b6d7af79c9c 272 uint8_t drawMode
lmayencou 0:2b6d7af79c9c 273 )
lmayencou 0:2b6d7af79c9c 274 {
lmayencou 0:2b6d7af79c9c 275 unsigned int frame_offset;
lmayencou 0:2b6d7af79c9c 276
lmayencou 0:2b6d7af79c9c 277 if (bitmap == NULL)
lmayencou 0:2b6d7af79c9c 278 return;
lmayencou 0:2b6d7af79c9c 279
lmayencou 0:2b6d7af79c9c 280 uint8_t width = pgm_read_byte(bitmap);
lmayencou 0:2b6d7af79c9c 281 uint8_t height = pgm_read_byte(++bitmap);
lmayencou 0:2b6d7af79c9c 282 bitmap++;
lmayencou 0:2b6d7af79c9c 283 if (frame > 0 || sprite_frame > 0) {
lmayencou 0:2b6d7af79c9c 284 frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
lmayencou 0:2b6d7af79c9c 285 // sprite plus mask uses twice as much space for each frame
lmayencou 0:2b6d7af79c9c 286 if (drawMode == SPRITE_PLUS_MASK) {
lmayencou 0:2b6d7af79c9c 287 frame_offset *= 2;
lmayencou 0:2b6d7af79c9c 288 } else if (mask != NULL) {
lmayencou 0:2b6d7af79c9c 289 mask += sprite_frame * frame_offset;
lmayencou 0:2b6d7af79c9c 290 }
lmayencou 0:2b6d7af79c9c 291 bitmap += frame * frame_offset;
lmayencou 0:2b6d7af79c9c 292 }
lmayencou 0:2b6d7af79c9c 293
lmayencou 0:2b6d7af79c9c 294 // if we're detecting the draw mode then base it on whether a mask
lmayencou 0:2b6d7af79c9c 295 // was passed as a separate object
lmayencou 0:2b6d7af79c9c 296 if (drawMode == SPRITE_AUTO_MODE) {
lmayencou 0:2b6d7af79c9c 297 drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
lmayencou 0:2b6d7af79c9c 298 }
lmayencou 0:2b6d7af79c9c 299
lmayencou 0:2b6d7af79c9c 300 drawBitmap(x, y, bitmap, mask, width, height, drawMode);
lmayencou 0:2b6d7af79c9c 301 }
lmayencou 0:2b6d7af79c9c 302
lmayencou 0:2b6d7af79c9c 303 void Sprites::drawBitmap(int16_t x, int16_t y,
lmayencou 0:2b6d7af79c9c 304 const uint8_t *bitmap, const uint8_t *mask,
lmayencou 0:2b6d7af79c9c 305 int8_t w, int8_t h, uint8_t draw_mode) {
lmayencou 0:2b6d7af79c9c 306 // no need to draw at all of we're offscreen
lmayencou 0:2b6d7af79c9c 307 if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
lmayencou 0:2b6d7af79c9c 308 return;
lmayencou 0:2b6d7af79c9c 309
lmayencou 0:2b6d7af79c9c 310 if (bitmap == NULL)
lmayencou 0:2b6d7af79c9c 311 return;
lmayencou 0:2b6d7af79c9c 312
lmayencou 0:2b6d7af79c9c 313 // xOffset technically doesn't need to be 16 bit but the math operations
lmayencou 0:2b6d7af79c9c 314 // are measurably faster if it is
lmayencou 0:2b6d7af79c9c 315 uint16_t xOffset, ofs;
lmayencou 0:2b6d7af79c9c 316 int8_t yOffset = abs(y) % 8;
lmayencou 0:2b6d7af79c9c 317 int8_t sRow = y / 8;
lmayencou 0:2b6d7af79c9c 318 uint8_t loop_h, start_h, rendered_width;
lmayencou 0:2b6d7af79c9c 319
lmayencou 0:2b6d7af79c9c 320 if (y < 0 && yOffset > 0) {
lmayencou 0:2b6d7af79c9c 321 sRow--;
lmayencou 0:2b6d7af79c9c 322 yOffset = 8 - yOffset;
lmayencou 0:2b6d7af79c9c 323 }
lmayencou 0:2b6d7af79c9c 324
lmayencou 0:2b6d7af79c9c 325 // if the left side of the render is offscreen skip those loops
lmayencou 0:2b6d7af79c9c 326 if (x < 0) {
lmayencou 0:2b6d7af79c9c 327 xOffset = abs(x);
lmayencou 0:2b6d7af79c9c 328 } else {
lmayencou 0:2b6d7af79c9c 329 xOffset = 0;
lmayencou 0:2b6d7af79c9c 330 }
lmayencou 0:2b6d7af79c9c 331
lmayencou 0:2b6d7af79c9c 332 // if the right side of the render is offscreen skip those loops
lmayencou 0:2b6d7af79c9c 333 if (x + w > WIDTH - 1) {
lmayencou 0:2b6d7af79c9c 334 rendered_width = ((WIDTH - x) - xOffset);
lmayencou 0:2b6d7af79c9c 335 } else {
lmayencou 0:2b6d7af79c9c 336 rendered_width = (w - xOffset);
lmayencou 0:2b6d7af79c9c 337 }
lmayencou 0:2b6d7af79c9c 338
lmayencou 0:2b6d7af79c9c 339 // if the top side of the render is offscreen skip those loops
lmayencou 0:2b6d7af79c9c 340 if (sRow < -1) {
lmayencou 0:2b6d7af79c9c 341 start_h = abs(sRow) - 1;
lmayencou 0:2b6d7af79c9c 342 } else {
lmayencou 0:2b6d7af79c9c 343 start_h = 0;
lmayencou 0:2b6d7af79c9c 344 }
lmayencou 0:2b6d7af79c9c 345
lmayencou 0:2b6d7af79c9c 346 loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
lmayencou 0:2b6d7af79c9c 347
lmayencou 0:2b6d7af79c9c 348 // if (sRow + loop_h - 1 > (HEIGHT/8)-1)
lmayencou 0:2b6d7af79c9c 349 if (sRow + loop_h > (HEIGHT / 8)) {
lmayencou 0:2b6d7af79c9c 350 loop_h = (HEIGHT / 8) - sRow;
lmayencou 0:2b6d7af79c9c 351 }
lmayencou 0:2b6d7af79c9c 352
lmayencou 0:2b6d7af79c9c 353 // prepare variables for loops later so we can compare with 0
lmayencou 0:2b6d7af79c9c 354 // instead of comparing two variables
lmayencou 0:2b6d7af79c9c 355 loop_h -= start_h;
lmayencou 0:2b6d7af79c9c 356
lmayencou 0:2b6d7af79c9c 357 sRow += start_h;
lmayencou 0:2b6d7af79c9c 358 ofs = (sRow * WIDTH) + x + xOffset;
lmayencou 0:2b6d7af79c9c 359 uint8_t *bofs = (uint8_t *)bitmap + (start_h * w) + xOffset;
lmayencou 0:2b6d7af79c9c 360 uint8_t *mask_ofs;
lmayencou 0:2b6d7af79c9c 361 if (mask != 0)
lmayencou 0:2b6d7af79c9c 362 mask_ofs = (uint8_t *)mask + (start_h * w) + xOffset;
lmayencou 0:2b6d7af79c9c 363 uint8_t data;
lmayencou 0:2b6d7af79c9c 364
lmayencou 0:2b6d7af79c9c 365 uint8_t mul_amt = 1 << yOffset;
lmayencou 0:2b6d7af79c9c 366 uint16_t mask_data;
lmayencou 0:2b6d7af79c9c 367 uint16_t bitmap_data;
lmayencou 0:2b6d7af79c9c 368
lmayencou 0:2b6d7af79c9c 369 switch (draw_mode) {
lmayencou 0:2b6d7af79c9c 370 case SPRITE_UNMASKED:
lmayencou 0:2b6d7af79c9c 371 // we only want to mask the 8 bits of our own sprite, so we can
lmayencou 0:2b6d7af79c9c 372 // calculate the mask before the start of the loop
lmayencou 0:2b6d7af79c9c 373 mask_data = ~(0xFF * mul_amt);
lmayencou 0:2b6d7af79c9c 374 // really if yOffset = 0 you have a faster case here that could be
lmayencou 0:2b6d7af79c9c 375 // optimized
lmayencou 0:2b6d7af79c9c 376 for (uint8_t a = 0; a < loop_h; a++) {
lmayencou 0:2b6d7af79c9c 377 for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
lmayencou 0:2b6d7af79c9c 378 bitmap_data = pgm_read_byte(bofs) * mul_amt;
lmayencou 0:2b6d7af79c9c 379
lmayencou 0:2b6d7af79c9c 380 if (sRow >= 0) {
lmayencou 0:2b6d7af79c9c 381 data = sBuffer[ofs];
lmayencou 0:2b6d7af79c9c 382 data &= (uint8_t)(mask_data);
lmayencou 0:2b6d7af79c9c 383 data |= (uint8_t)(bitmap_data);
lmayencou 0:2b6d7af79c9c 384 sBuffer[ofs] = data;
lmayencou 0:2b6d7af79c9c 385 }
lmayencou 0:2b6d7af79c9c 386 if (yOffset != 0 && sRow < 7) {
lmayencou 0:2b6d7af79c9c 387 data = sBuffer[ofs + WIDTH];
lmayencou 0:2b6d7af79c9c 388 data &= (*((unsigned char *) (&mask_data) + 1));
lmayencou 0:2b6d7af79c9c 389 data |= (*((unsigned char *) (&bitmap_data) + 1));
lmayencou 0:2b6d7af79c9c 390 sBuffer[ofs + WIDTH] = data;
lmayencou 0:2b6d7af79c9c 391 }
lmayencou 0:2b6d7af79c9c 392 ofs++;
lmayencou 0:2b6d7af79c9c 393 bofs++;
lmayencou 0:2b6d7af79c9c 394 }
lmayencou 0:2b6d7af79c9c 395 sRow++;
lmayencou 0:2b6d7af79c9c 396 bofs += w - rendered_width;
lmayencou 0:2b6d7af79c9c 397 ofs += WIDTH - rendered_width;
lmayencou 0:2b6d7af79c9c 398 }
lmayencou 0:2b6d7af79c9c 399 break;
lmayencou 0:2b6d7af79c9c 400
lmayencou 0:2b6d7af79c9c 401 case SPRITE_IS_MASK:
lmayencou 0:2b6d7af79c9c 402 for (uint8_t a = 0; a < loop_h; a++) {
lmayencou 0:2b6d7af79c9c 403 for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
lmayencou 0:2b6d7af79c9c 404 bitmap_data = pgm_read_byte(bofs) * mul_amt;
lmayencou 0:2b6d7af79c9c 405 if (sRow >= 0) {
lmayencou 0:2b6d7af79c9c 406 sBuffer[ofs] |= (uint8_t)(bitmap_data);
lmayencou 0:2b6d7af79c9c 407 }
lmayencou 0:2b6d7af79c9c 408 if (yOffset != 0 && sRow < 7) {
lmayencou 0:2b6d7af79c9c 409 sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
lmayencou 0:2b6d7af79c9c 410 }
lmayencou 0:2b6d7af79c9c 411 ofs++;
lmayencou 0:2b6d7af79c9c 412 bofs++;
lmayencou 0:2b6d7af79c9c 413 }
lmayencou 0:2b6d7af79c9c 414 sRow++;
lmayencou 0:2b6d7af79c9c 415 bofs += w - rendered_width;
lmayencou 0:2b6d7af79c9c 416 ofs += WIDTH - rendered_width;
lmayencou 0:2b6d7af79c9c 417 }
lmayencou 0:2b6d7af79c9c 418 break;
lmayencou 0:2b6d7af79c9c 419
lmayencou 0:2b6d7af79c9c 420 case SPRITE_IS_MASK_ERASE:
lmayencou 0:2b6d7af79c9c 421 for (uint8_t a = 0; a < loop_h; a++) {
lmayencou 0:2b6d7af79c9c 422 for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
lmayencou 0:2b6d7af79c9c 423 bitmap_data = pgm_read_byte(bofs) * mul_amt;
lmayencou 0:2b6d7af79c9c 424 if (sRow >= 0) {
lmayencou 0:2b6d7af79c9c 425 sBuffer[ofs] &= ~(uint8_t)(bitmap_data);
lmayencou 0:2b6d7af79c9c 426 }
lmayencou 0:2b6d7af79c9c 427 if (yOffset != 0 && sRow < 7) {
lmayencou 0:2b6d7af79c9c 428 sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
lmayencou 0:2b6d7af79c9c 429 }
lmayencou 0:2b6d7af79c9c 430 ofs++;
lmayencou 0:2b6d7af79c9c 431 bofs++;
lmayencou 0:2b6d7af79c9c 432 }
lmayencou 0:2b6d7af79c9c 433 sRow++;
lmayencou 0:2b6d7af79c9c 434 bofs += w - rendered_width;
lmayencou 0:2b6d7af79c9c 435 ofs += WIDTH - rendered_width;
lmayencou 0:2b6d7af79c9c 436 }
lmayencou 0:2b6d7af79c9c 437 break;
lmayencou 0:2b6d7af79c9c 438
lmayencou 0:2b6d7af79c9c 439 case SPRITE_MASKED:
lmayencou 0:2b6d7af79c9c 440 for (uint8_t a = 0; a < loop_h; a++) {
lmayencou 0:2b6d7af79c9c 441 for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
lmayencou 0:2b6d7af79c9c 442 // NOTE: you might think in the yOffset==0 case that this results
lmayencou 0:2b6d7af79c9c 443 // in more effort, but in all my testing the compiler was forcing
lmayencou 0:2b6d7af79c9c 444 // 16-bit math to happen here anyways, so this isn't actually
lmayencou 0:2b6d7af79c9c 445 // compiling to more code than it otherwise would. If the offset
lmayencou 0:2b6d7af79c9c 446 // is 0 the high part of the word will just never be used.
lmayencou 0:2b6d7af79c9c 447
lmayencou 0:2b6d7af79c9c 448 // load data and bit shift
lmayencou 0:2b6d7af79c9c 449 // mask needs to be bit flipped
lmayencou 0:2b6d7af79c9c 450 mask_data = ~(pgm_read_byte(mask_ofs) * mul_amt);
lmayencou 0:2b6d7af79c9c 451 bitmap_data = pgm_read_byte(bofs) * mul_amt;
lmayencou 0:2b6d7af79c9c 452
lmayencou 0:2b6d7af79c9c 453 if (sRow >= 0) {
lmayencou 0:2b6d7af79c9c 454 data = sBuffer[ofs];
lmayencou 0:2b6d7af79c9c 455 data &= (uint8_t)(mask_data);
lmayencou 0:2b6d7af79c9c 456 data |= (uint8_t)(bitmap_data);
lmayencou 0:2b6d7af79c9c 457 sBuffer[ofs] = data;
lmayencou 0:2b6d7af79c9c 458 }
lmayencou 0:2b6d7af79c9c 459 if (yOffset != 0 && sRow < 7) {
lmayencou 0:2b6d7af79c9c 460 data = sBuffer[ofs + WIDTH];
lmayencou 0:2b6d7af79c9c 461 data &= (*((unsigned char *) (&mask_data) + 1));
lmayencou 0:2b6d7af79c9c 462 data |= (*((unsigned char *) (&bitmap_data) + 1));
lmayencou 0:2b6d7af79c9c 463 sBuffer[ofs + WIDTH] = data;
lmayencou 0:2b6d7af79c9c 464 }
lmayencou 0:2b6d7af79c9c 465 ofs++;
lmayencou 0:2b6d7af79c9c 466 mask_ofs++;
lmayencou 0:2b6d7af79c9c 467 bofs++;
lmayencou 0:2b6d7af79c9c 468 }
lmayencou 0:2b6d7af79c9c 469 sRow++;
lmayencou 0:2b6d7af79c9c 470 bofs += w - rendered_width;
lmayencou 0:2b6d7af79c9c 471 mask_ofs += w - rendered_width;
lmayencou 0:2b6d7af79c9c 472 ofs += WIDTH - rendered_width;
lmayencou 0:2b6d7af79c9c 473 }
lmayencou 0:2b6d7af79c9c 474 break;
lmayencou 0:2b6d7af79c9c 475
lmayencou 0:2b6d7af79c9c 476
lmayencou 0:2b6d7af79c9c 477 case SPRITE_PLUS_MASK:
lmayencou 0:2b6d7af79c9c 478 // *2 because we use double the bits (mask + bitmap)
lmayencou 0:2b6d7af79c9c 479 bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2);
lmayencou 0:2b6d7af79c9c 480
lmayencou 0:2b6d7af79c9c 481 uint8_t xi = rendered_width; // used for x loop below
lmayencou 0:2b6d7af79c9c 482 uint8_t yi = loop_h; // used for y loop below
lmayencou 0:2b6d7af79c9c 483
lmayencou 0:2b6d7af79c9c 484 asm volatile(
lmayencou 0:2b6d7af79c9c 485 "push r28\n" // save Y
lmayencou 0:2b6d7af79c9c 486 "push r29\n"
lmayencou 0:2b6d7af79c9c 487 "mov r28, %A[buffer_page2_ofs]\n" // Y = buffer page 2 offset
lmayencou 0:2b6d7af79c9c 488 "mov r29, %B[buffer_page2_ofs]\n"
lmayencou 0:2b6d7af79c9c 489 "loop_y:\n"
lmayencou 0:2b6d7af79c9c 490 "loop_x:\n"
lmayencou 0:2b6d7af79c9c 491 // load bitmap and mask data
lmayencou 0:2b6d7af79c9c 492 "lpm %A[bitmap_data], Z+\n"
lmayencou 0:2b6d7af79c9c 493 "lpm %A[mask_data], Z+\n"
lmayencou 0:2b6d7af79c9c 494
lmayencou 0:2b6d7af79c9c 495 // shift mask and buffer data
lmayencou 0:2b6d7af79c9c 496 "tst %[yOffset]\n"
lmayencou 0:2b6d7af79c9c 497 "breq skip_shifting\n"
lmayencou 0:2b6d7af79c9c 498 "mul %A[bitmap_data], %[mul_amt]\n"
lmayencou 0:2b6d7af79c9c 499 "mov %A[bitmap_data], r0\n"
lmayencou 0:2b6d7af79c9c 500 "mov %B[bitmap_data], r1\n"
lmayencou 0:2b6d7af79c9c 501 "mul %A[mask_data], %[mul_amt]\n"
lmayencou 0:2b6d7af79c9c 502 "mov %A[mask_data], r0\n"
lmayencou 0:2b6d7af79c9c 503 // "mov %B[mask_data], r1\n"
lmayencou 0:2b6d7af79c9c 504
lmayencou 0:2b6d7af79c9c 505
lmayencou 0:2b6d7af79c9c 506 // SECOND PAGE
lmayencou 0:2b6d7af79c9c 507 // if yOffset != 0 && sRow < 7
lmayencou 0:2b6d7af79c9c 508 "cpi %[sRow], 7\n"
lmayencou 0:2b6d7af79c9c 509 "brge end_second_page\n"
lmayencou 0:2b6d7af79c9c 510 // then
lmayencou 0:2b6d7af79c9c 511 "ld %[data], Y\n"
lmayencou 0:2b6d7af79c9c 512 // "com %B[mask_data]\n" // invert high byte of mask
lmayencou 0:2b6d7af79c9c 513 "com r1\n"
lmayencou 0:2b6d7af79c9c 514 "and %[data], r1\n" // %B[mask_data]
lmayencou 0:2b6d7af79c9c 515 "or %[data], %B[bitmap_data]\n"
lmayencou 0:2b6d7af79c9c 516 // update buffer, increment
lmayencou 0:2b6d7af79c9c 517 "st Y+, %[data]\n"
lmayencou 0:2b6d7af79c9c 518
lmayencou 0:2b6d7af79c9c 519 "end_second_page:\n"
lmayencou 0:2b6d7af79c9c 520 "skip_shifting:\n"
lmayencou 0:2b6d7af79c9c 521
lmayencou 0:2b6d7af79c9c 522
lmayencou 0:2b6d7af79c9c 523 // FIRST PAGE
lmayencou 0:2b6d7af79c9c 524 "ld %[data], %a[buffer_ofs]\n"
lmayencou 0:2b6d7af79c9c 525 // if sRow >= 0
lmayencou 0:2b6d7af79c9c 526 "tst %[sRow]\n"
lmayencou 0:2b6d7af79c9c 527 "brmi end_first_page\n"
lmayencou 0:2b6d7af79c9c 528 // then
lmayencou 0:2b6d7af79c9c 529 "com %A[mask_data]\n"
lmayencou 0:2b6d7af79c9c 530 "and %[data], %A[mask_data]\n"
lmayencou 0:2b6d7af79c9c 531 "or %[data], %A[bitmap_data]\n"
lmayencou 0:2b6d7af79c9c 532
lmayencou 0:2b6d7af79c9c 533 "end_first_page:\n"
lmayencou 0:2b6d7af79c9c 534 // update buffer, increment
lmayencou 0:2b6d7af79c9c 535 "st %a[buffer_ofs]+, %[data]\n"
lmayencou 0:2b6d7af79c9c 536
lmayencou 0:2b6d7af79c9c 537
lmayencou 0:2b6d7af79c9c 538 // "x_loop_next:\n"
lmayencou 0:2b6d7af79c9c 539 "dec %[xi]\n"
lmayencou 0:2b6d7af79c9c 540 "brne loop_x\n"
lmayencou 0:2b6d7af79c9c 541
lmayencou 0:2b6d7af79c9c 542 // increment y
lmayencou 0:2b6d7af79c9c 543 "next_loop_y:\n"
lmayencou 0:2b6d7af79c9c 544 "dec %[yi]\n"
lmayencou 0:2b6d7af79c9c 545 "breq finished\n"
lmayencou 0:2b6d7af79c9c 546 "mov %[xi], %[x_count]\n" // reset x counter
lmayencou 0:2b6d7af79c9c 547 // sRow++;
lmayencou 0:2b6d7af79c9c 548 "inc %[sRow]\n"
lmayencou 0:2b6d7af79c9c 549 "clr __zero_reg__\n"
lmayencou 0:2b6d7af79c9c 550 // sprite_ofs += (w - rendered_width) * 2;
lmayencou 0:2b6d7af79c9c 551 "add %A[sprite_ofs], %A[sprite_ofs_jump]\n"
lmayencou 0:2b6d7af79c9c 552 "adc %B[sprite_ofs], __zero_reg__\n"
lmayencou 0:2b6d7af79c9c 553 // buffer_ofs += WIDTH - rendered_width;
lmayencou 0:2b6d7af79c9c 554 "add %A[buffer_ofs], %A[buffer_ofs_jump]\n"
lmayencou 0:2b6d7af79c9c 555 "adc %B[buffer_ofs], __zero_reg__\n"
lmayencou 0:2b6d7af79c9c 556 // buffer_ofs_page_2 += WIDTH - rendered_width;
lmayencou 0:2b6d7af79c9c 557 "add r28, %A[buffer_ofs_jump]\n"
lmayencou 0:2b6d7af79c9c 558 "adc r29, __zero_reg__\n"
lmayencou 0:2b6d7af79c9c 559
lmayencou 0:2b6d7af79c9c 560 "rjmp loop_y\n"
lmayencou 0:2b6d7af79c9c 561 "finished:\n"
lmayencou 0:2b6d7af79c9c 562 // put the Y register back in place
lmayencou 0:2b6d7af79c9c 563 "pop r29\n"
lmayencou 0:2b6d7af79c9c 564 "pop r28\n"
lmayencou 0:2b6d7af79c9c 565 "clr __zero_reg__\n" // just in case
lmayencou 0:2b6d7af79c9c 566 : [xi] "+&r" (xi),
lmayencou 0:2b6d7af79c9c 567 [yi] "+&r" (yi),
lmayencou 0:2b6d7af79c9c 568 [sRow] "+&a" (sRow), // CPI requires an upper register
lmayencou 0:2b6d7af79c9c 569 [data] "+&r" (data),
lmayencou 0:2b6d7af79c9c 570 [mask_data] "+&r" (mask_data),
lmayencou 0:2b6d7af79c9c 571 [bitmap_data] "+&r" (bitmap_data)
lmayencou 0:2b6d7af79c9c 572 :
lmayencou 0:2b6d7af79c9c 573 [x_count] "r" (rendered_width),
lmayencou 0:2b6d7af79c9c 574 [y_count] "r" (loop_h),
lmayencou 0:2b6d7af79c9c 575 [sprite_ofs] "z" (bofs),
lmayencou 0:2b6d7af79c9c 576 [buffer_ofs] "x" (sBuffer+ofs),
lmayencou 0:2b6d7af79c9c 577 [buffer_page2_ofs] "r" (sBuffer+ofs+WIDTH), // Y pointer
lmayencou 0:2b6d7af79c9c 578 [buffer_ofs_jump] "r" (WIDTH-rendered_width),
lmayencou 0:2b6d7af79c9c 579 [sprite_ofs_jump] "r" ((w-rendered_width)*2),
lmayencou 0:2b6d7af79c9c 580 [yOffset] "r" (yOffset),
lmayencou 0:2b6d7af79c9c 581 [mul_amt] "r" (mul_amt)
lmayencou 0:2b6d7af79c9c 582 :
lmayencou 0:2b6d7af79c9c 583 );
lmayencou 0:2b6d7af79c9c 584 break;
lmayencou 0:2b6d7af79c9c 585
lmayencou 0:2b6d7af79c9c 586 }
lmayencou 0:2b6d7af79c9c 587 }
lmayencou 0:2b6d7af79c9c 588
lmayencou 0:2b6d7af79c9c 589
lmayencou 0:2b6d7af79c9c 590 /////////////////////////////////
lmayencou 0:2b6d7af79c9c 591 // Basic Collision by Dreamer3 //
lmayencou 0:2b6d7af79c9c 592 /////////////////////////////////
lmayencou 0:2b6d7af79c9c 593 bool Arduboy::collide(Point point, Rect rect)
lmayencou 0:2b6d7af79c9c 594 {
lmayencou 0:2b6d7af79c9c 595 // does point fall within the bounds of rect
lmayencou 0:2b6d7af79c9c 596 return ((point.x >= rect.x) && (point.x < rect.x + rect.width) &&
lmayencou 0:2b6d7af79c9c 597 (point.y >= rect.y) && (point.y < rect.y + rect.height));
lmayencou 0:2b6d7af79c9c 598 }
lmayencou 0:2b6d7af79c9c 599
lmayencou 0:2b6d7af79c9c 600 bool Arduboy::collide(Rect rect1, Rect rect2)
lmayencou 0:2b6d7af79c9c 601 {
lmayencou 0:2b6d7af79c9c 602 return !( rect2.x >= rect1.x + rect1.width ||
lmayencou 0:2b6d7af79c9c 603 rect2.x + rect2.width <= rect1.x ||
lmayencou 0:2b6d7af79c9c 604 rect2.y >= rect1.y + rect1.height ||
lmayencou 0:2b6d7af79c9c 605 rect2.y + rect2.height <= rect1.y);
lmayencou 0:2b6d7af79c9c 606 }