update Arduboy and Arduboy2 libs
Arduboy lib: added support for Noritake GU128X64-800B VFD display Arduboy2 lib: added support for Noritake GU128X64-800B VFD display removed experimental batterycheck code and keywords temporary reverted assembly code in sprites.cpp fixed one off bug in drawBitmap buttonsState no longer uses timer ISR code exitToBootloader function uses timer ISR code
This commit is contained in:
parent
7b4faf6a85
commit
162a70371e
|
@ -2,7 +2,7 @@
|
|||
|
||||
// need to redeclare these here since we declare them static in .h
|
||||
volatile uint8_t *ArduboyCore::mosiport,
|
||||
/* *ArduboyCore::csport, */ *ArduboyCore::dcport;
|
||||
*ArduboyCore::csport, *ArduboyCore::dcport;
|
||||
uint8_t ArduboyCore::mosipinmask,
|
||||
ArduboyCore::cspinmask, ArduboyCore::dcpinmask;
|
||||
|
||||
|
@ -35,7 +35,12 @@ const uint8_t PROGMEM lcdBootProgram[] = {
|
|||
//
|
||||
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
|
||||
|
||||
#ifdef OLED_SH1106
|
||||
#if defined(GU12864_800B)
|
||||
0x24, 0x40, // enable Layer 0, graphic display area on
|
||||
0x47, // set brightness
|
||||
0x64, 0x00, // set x position 0
|
||||
0x84, // address mode set: X increment
|
||||
#elif OLED_SH1106
|
||||
0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
|
||||
0xA1, // Set Segment Re-map
|
||||
0xC8, // Set COM Output Scan Direction
|
||||
|
@ -217,6 +222,27 @@ void ArduboyCore::bootPins()
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(GU12864_800B)
|
||||
void ArduboyCore::displayEnable()
|
||||
{
|
||||
*csport |= cspinmask;
|
||||
SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPOL) | _BV(CPHA);
|
||||
LCDCommandMode();
|
||||
}
|
||||
|
||||
void ArduboyCore::displayDisable()
|
||||
{
|
||||
SPCR = _BV(SPE) | _BV(MSTR);
|
||||
}
|
||||
|
||||
void ArduboyCore::displayWrite(uint8_t data)
|
||||
{
|
||||
*csport &= ~cspinmask;
|
||||
SPI.transfer(data);
|
||||
*csport |= cspinmask;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ArduboyCore::bootLCD()
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
|
@ -230,13 +256,32 @@ void ArduboyCore::bootLCD()
|
|||
i2c_stop();
|
||||
#else
|
||||
// setup the ports we need to talk to the OLED
|
||||
//csport = portOutputRegister(digitalPinToPort(CS));
|
||||
*portOutputRegister(digitalPinToPort(CS)) &= ~cspinmask;
|
||||
csport = portOutputRegister(digitalPinToPort(CS));
|
||||
cspinmask = digitalPinToBitMask(CS);
|
||||
*portOutputRegister(digitalPinToPort(CS)) &= ~cspinmask;
|
||||
dcport = portOutputRegister(digitalPinToPort(DC));
|
||||
dcpinmask = digitalPinToBitMask(DC);
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV2);
|
||||
#if defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128)
|
||||
#if defined(GU12864_800B)
|
||||
delayShort(1);
|
||||
digitalWrite(RST, HIGH);
|
||||
delayShort(10);
|
||||
displayEnable();
|
||||
for (uint8_t i = 0; i < sizeof(lcdBootProgram) + 8; i++)
|
||||
{
|
||||
if (i < 8)
|
||||
{
|
||||
displayWrite(0x62); // set display area
|
||||
displayWrite(i); // display area address
|
||||
LCDDataMode();
|
||||
displayWrite(0xFF); // Graphic display
|
||||
LCDCommandMode();
|
||||
}
|
||||
else
|
||||
displayWrite(pgm_read_byte(lcdBootProgram + i - 8));
|
||||
}
|
||||
displayDisable();
|
||||
#elif defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128)
|
||||
LCDDataMode();
|
||||
for (uint16_t i = 0; i < 8192; i++) SPI.transfer(0); //Clear all display ram
|
||||
#endif
|
||||
|
@ -253,14 +298,22 @@ void ArduboyCore::bootLCD()
|
|||
|
||||
void ArduboyCore::LCDDataMode()
|
||||
{
|
||||
#if defined(GU12864_800B)
|
||||
*dcport &= ~dcpinmask;
|
||||
#else
|
||||
*dcport |= dcpinmask;
|
||||
#endif
|
||||
// *csport &= ~cspinmask;
|
||||
}
|
||||
|
||||
void ArduboyCore::LCDCommandMode()
|
||||
{
|
||||
// *csport |= cspinmask;
|
||||
#if defined(GU12864_800B)
|
||||
*dcport |= dcpinmask;
|
||||
#else
|
||||
*dcport &= ~dcpinmask;
|
||||
#endif
|
||||
// *csport &= ~cspinmask; CS set once at bootLCD
|
||||
}
|
||||
|
||||
|
@ -359,7 +412,24 @@ void ArduboyCore::paint8Pixels(uint8_t pixels)
|
|||
|
||||
void ArduboyCore::paintScreen(const unsigned char *image)
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
for (uint8_t r = 0; r < (HEIGHT/8); r++)
|
||||
{
|
||||
LCDCommandMode();
|
||||
displayWrite(0x60);
|
||||
displayWrite(r);
|
||||
LCDDataMode();
|
||||
for (uint8_t c = 0; c < (WIDTH); c++)
|
||||
{
|
||||
*csport &= ~cspinmask;
|
||||
SPDR = pgm_read_byte(image++);
|
||||
while (!(SPSR & _BV(SPIF)));
|
||||
*csport |= cspinmask;
|
||||
}
|
||||
}
|
||||
displayDisable();
|
||||
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
i2c_start(SSD1306_I2C_DATA);
|
||||
for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++)
|
||||
i2c_sendByte(pgm_read_byte(image+i));
|
||||
|
@ -439,7 +509,24 @@ void ArduboyCore::paintScreen(const unsigned char *image)
|
|||
// will be used by any buffer based subclass
|
||||
void ArduboyCore::paintScreen(unsigned char image[])
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
for (uint8_t r = 0; r < (HEIGHT/8); r++)
|
||||
{
|
||||
LCDCommandMode();
|
||||
displayWrite(0x60);
|
||||
displayWrite(r);
|
||||
LCDDataMode();
|
||||
for (uint8_t c = 0; c < (WIDTH); c++)
|
||||
{
|
||||
*csport &= ~cspinmask;
|
||||
SPDR = *(image++);
|
||||
while (!(SPSR & _BV(SPIF)));
|
||||
*csport |= cspinmask;
|
||||
}
|
||||
}
|
||||
displayDisable();
|
||||
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
uint16_t length = WIDTH * HEIGHT / 8;
|
||||
uint8_t sda_clr = I2C_PORT & ~((1 << I2C_SDA) | (1 << I2C_SCL));
|
||||
uint8_t scl = 1 << I2C_SCL;
|
||||
|
@ -719,14 +806,37 @@ void ArduboyCore::sendLCDCommand(uint8_t command)
|
|||
// when inverted, a pixel set to 0 will be on
|
||||
void ArduboyCore::invert(boolean inverse)
|
||||
{
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
displayWrite(0x24);
|
||||
if (inverse) displayWrite(0x50);
|
||||
else displayWrite(0x40);
|
||||
LCDDataMode();
|
||||
displayDisable();
|
||||
#else
|
||||
sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// turn all display pixels on, ignoring buffer contents
|
||||
// or set to normal buffer display
|
||||
void ArduboyCore::allPixelsOn(boolean on)
|
||||
{
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
if (on)
|
||||
{
|
||||
displayWrite(0x20);
|
||||
displayWrite(0x50);
|
||||
}
|
||||
else
|
||||
displayWrite(0x24);
|
||||
displayWrite(0x40);
|
||||
LCDDataMode();
|
||||
displayDisable();
|
||||
#else
|
||||
sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
|
||||
#endif
|
||||
}
|
||||
|
||||
// flip the display vertically or set to normal
|
||||
|
|
|
@ -212,7 +212,13 @@ public:
|
|||
* in a frame loop.
|
||||
*/
|
||||
void static idle();
|
||||
|
||||
#if defined(GU12864_800B)
|
||||
void static displayEnable();
|
||||
|
||||
void static displayDisable();
|
||||
|
||||
void static displayWrite(uint8_t data);
|
||||
#endif
|
||||
void static LCDDataMode(); //< put the display in data mode
|
||||
|
||||
/// put the display in command mode
|
||||
|
@ -365,7 +371,7 @@ protected:
|
|||
|
||||
|
||||
private:
|
||||
volatile static uint8_t *mosiport, /* *csport, */ *dcport;
|
||||
volatile static uint8_t *mosiport, *csport, *dcport;
|
||||
uint8_t static mosipinmask, cspinmask, dcpinmask;
|
||||
|
||||
};
|
||||
|
|
|
@ -32,8 +32,6 @@ bootLogoSpritesOverwrite KEYWORD2
|
|||
bootLogoSpritesSelfMasked KEYWORD2
|
||||
bootLogoText KEYWORD2
|
||||
buttonsState KEYWORD2
|
||||
checkBatteryState KEYWORD2
|
||||
checkBatteryStateLED KEYWORD2
|
||||
clear KEYWORD2
|
||||
collide KEYWORD2
|
||||
cpuLoad KEYWORD2
|
||||
|
@ -167,7 +165,3 @@ RGB_ON LITERAL1
|
|||
|
||||
ARDUBOY_NO_USB LITERAL1
|
||||
|
||||
BATTERY_STATE_LOW LITERAL1
|
||||
BATTERY_STATE_NORMAL LITERAL1
|
||||
BATTERY_STATE_INVALID LITERAL1
|
||||
FLASH_LED LITERAL1
|
|
@ -8,32 +8,12 @@
|
|||
#include "ab_logo.c"
|
||||
#include "glcdfont.c"
|
||||
|
||||
//================================
|
||||
//========== class Rect ==========
|
||||
//================================
|
||||
|
||||
Rect::Rect(int16_t x, int16_t y, uint8_t width, uint8_t height)
|
||||
: x(x), y(y), width(width), height(height)
|
||||
{
|
||||
}
|
||||
|
||||
//=================================
|
||||
//========== class Point ==========
|
||||
//=================================
|
||||
|
||||
Point::Point(int16_t x, int16_t y)
|
||||
: x(x), y(y)
|
||||
{
|
||||
}
|
||||
|
||||
//========================================
|
||||
//========== class Arduboy2Base ==========
|
||||
//========================================
|
||||
|
||||
uint8_t Arduboy2Base::sBuffer[];
|
||||
|
||||
uint8_t Arduboy2Base::batteryLow = EEPROM.read(EEPROM_BATTERY_LOW); //Low battery bandgap value - 192
|
||||
|
||||
Arduboy2Base::Arduboy2Base()
|
||||
{
|
||||
currentButtonState = 0;
|
||||
|
@ -78,16 +58,17 @@ void Arduboy2Base::flashlight()
|
|||
if (!pressed(UP_BUTTON)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef GU12864_800B
|
||||
allPixelsOn(true);
|
||||
#else
|
||||
sendLCDCommand(OLED_ALL_PIXELS_ON); // smaller than allPixelsOn()
|
||||
#endif
|
||||
digitalWriteRGB(RGB_ON, RGB_ON, RGB_ON);
|
||||
|
||||
#ifndef ARDUBOY_CORE // for Arduboy core timer 0 should remain enabled
|
||||
// prevent the bootloader magic number from being overwritten by timer 0
|
||||
// when a timer variable overlaps the magic number location, for when
|
||||
// flashlight mode is used for upload problem recovery
|
||||
power_timer0_disable();
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
idle();
|
||||
|
@ -267,11 +248,7 @@ bool Arduboy2Base::everyXFrames(uint8_t frames)
|
|||
|
||||
bool Arduboy2Base::nextFrame()
|
||||
{
|
||||
#ifdef ARDUBOY_CORE
|
||||
uint8_t now = millisChar();
|
||||
#else
|
||||
uint8_t now = (uint8_t) millis();
|
||||
#endif
|
||||
uint8_t now = (uint8_t) timer0_millis;
|
||||
uint8_t frameDurationMs = now - thisFrameStart;
|
||||
|
||||
if (justRendered) {
|
||||
|
@ -867,7 +844,7 @@ void Arduboy2Base::drawBitmap
|
|||
uint8_t color)
|
||||
{
|
||||
// no need to draw at all if we're offscreen
|
||||
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1)
|
||||
if (x+w <= 0 || x > WIDTH-1 || y+h <= 0 || y > HEIGHT-1)
|
||||
return;
|
||||
|
||||
int8_t yOffset = y & 7;
|
||||
|
@ -1200,88 +1177,6 @@ void Arduboy2Base::swap(int16_t& a, int16_t& b)
|
|||
b = temp;
|
||||
}
|
||||
|
||||
uint8_t Arduboy2Base::checkBatteryState()
|
||||
{
|
||||
uint8_t state = BATTERY_STATE_INVALID;
|
||||
asm volatile (
|
||||
" ldi r30, lo8(%[prr0]) \n" // if (bit_is_set(PRR0,PRADC)) //ADC power off
|
||||
" ldi r31, hi8(%[prr0]) \n" // {
|
||||
" ld r24, z \n"
|
||||
" lds r25, %[adcsra] \n"
|
||||
" sbrs r24, %[pradc] \n"
|
||||
" rjmp 1f \n"
|
||||
" \n"
|
||||
" andi r24, ~(1<<%[pradc]) \n" // PRR0 &= ~_BV(PRADC); // ADC power on
|
||||
" st z, r24 \n"
|
||||
" ldi r24, %[admuxval] \n" // ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)
|
||||
" sts %[admux], r24 \n"
|
||||
" ori r25, 1<<%[adsc] \n" // ADCSRA |= _BV(ADSC) //start conversion
|
||||
" sts %[adcsra], r25 \n" // }
|
||||
" ;rjmp 2f \n" // bit is set so continue below to jump to 2f
|
||||
"1: \n"
|
||||
" sbrc r25, %[adsc] \n" // else if (!(ADCSRA & _BV(ADSC)) //ADC conversion ready
|
||||
" rjmp 2f \n" // {
|
||||
" \n"
|
||||
" ori r24, 1<<%[pradc] \n" // PRR0 |= _BV(PRADC); // ADC power off
|
||||
" st z, r24 \n"
|
||||
" ldi r30, %[adcl] \n" // uint16_t bandgap = ADCL | (ADCH << 8);
|
||||
" ld r24, z+ \n"
|
||||
" ld r25, z \n"
|
||||
" subi r24, 192 \n" // bandgap -= 192;
|
||||
" sbci r25, 0 \n"
|
||||
" and r25, r25 \n" // if (bandgap < 256)
|
||||
" brne 2f \n" // {
|
||||
" \n"
|
||||
" ldi %[state],%[normal] \n" // state = BATTERY_STATE_NORMAL;
|
||||
" lds r25, %[battlow] \n"
|
||||
" cp r25, r24 \n"
|
||||
" brcc 2f \n" // if (batteryLow < bandgap) state = BATTERY_STATE_LOW;
|
||||
" ldi %[state],%[low] \n" // }
|
||||
"2: \n" // }
|
||||
:[state] "+d"(state)
|
||||
:[prr0] "M" (_SFR_MEM_ADDR(PRR0)),
|
||||
[adcsra] "M" (_SFR_MEM_ADDR(ADCSRA)),
|
||||
[pradc] "I" (PRADC),
|
||||
[admuxval] "M" (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)),
|
||||
[admux] "M" (_SFR_MEM_ADDR(ADMUX)),
|
||||
[adsc] "I" (ADSC),
|
||||
[adcl] "M" (_SFR_MEM_ADDR(ADCL)),
|
||||
[battlow] "" (&batteryLow),
|
||||
[normal] "I" (BATTERY_STATE_NORMAL),
|
||||
[low] "I" (BATTERY_STATE_LOW)
|
||||
: "r24", "r25", "r30", "r31"
|
||||
);
|
||||
#if 0
|
||||
// For reference, this is the C++ equivalent
|
||||
uint8_t state = BATTERY_STATE_UNDEFINED;
|
||||
if (bit_is_set(PRR0,PRADC)) //only enable when ADC power is disabled
|
||||
{
|
||||
PRR0 &= ~_BV(PRADC); // ADC power on
|
||||
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); //meassure 1.1V bandgap against AVcc
|
||||
ADCSRA |= _BV(ADSC); //start conversion
|
||||
}
|
||||
else if (!(ADCSRA & _BV(ADSC)))
|
||||
{
|
||||
PRR0 |= _BV(PRADC); // ADC power off
|
||||
uint16_t bandgap = ADCL | (ADCH << 8);
|
||||
bandgap -= 192;
|
||||
if (bandgap < 256)
|
||||
{
|
||||
state = BATTERY_STATE_NORMAL;
|
||||
if (batteryLow < (uint8_t)bandgap) state = BATTERY_STATE_LOW;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
uint8_t Arduboy2Base::checkBatteryStateLED(bool flash)
|
||||
{
|
||||
uint8_t state = checkBatteryState();
|
||||
if (state == BATTERY_STATE_NORMAL | flash) TXLED0;
|
||||
if (state == BATTERY_STATE_LOW) TXLED1;
|
||||
return state;
|
||||
}
|
||||
|
||||
//====================================
|
||||
//========== class Arduboy2 ==========
|
||||
|
|
|
@ -41,8 +41,6 @@
|
|||
#define EEPROM_VERSION 0
|
||||
#define EEPROM_SYS_FLAGS 1
|
||||
#define EEPROM_AUDIO_ON_OFF 2
|
||||
#define EEPROM_BANDGAP_CAL 6 //Bandgap calibration value
|
||||
#define EEPROM_BATTERY_LOW 7 //Battery low threshold
|
||||
#define EEPROM_UNIT_ID 8 // A uint16_t binary unit ID
|
||||
#define EEPROM_UNIT_NAME 10 // An up to 6 character unit name. Cannot contain
|
||||
// 0x00 or 0xFF. Lengths less than 6 are padded
|
||||
|
@ -89,10 +87,6 @@
|
|||
|
||||
#define CLEAR_BUFFER true /**< Value to be passed to `display()` to clear the screen buffer. */
|
||||
|
||||
#define BATTERY_STATE_LOW 0
|
||||
#define BATTERY_STATE_NORMAL 1
|
||||
#define BATTERY_STATE_INVALID 0xFF
|
||||
#define FLASH_LED true
|
||||
|
||||
//=============================================
|
||||
//========== Rect (rectangle) object ==========
|
||||
|
@ -128,7 +122,10 @@ struct Rect
|
|||
* \param width The width of the rectangle. Copied to variable `width`.
|
||||
* \param height The height of the rectangle. Copied to variable `height`.
|
||||
*/
|
||||
Rect(int16_t x, int16_t y, uint8_t width, uint8_t height);
|
||||
constexpr Rect(int16_t x, int16_t y, uint8_t width, uint8_t height)
|
||||
: x(x), y(y), width(width), height(height)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//==================================
|
||||
|
@ -159,7 +156,10 @@ struct Point
|
|||
* \param x The X coordinate of the point. Copied to variable `x`.
|
||||
* \param y The Y coordinate of the point. Copied to variable `y`.
|
||||
*/
|
||||
Point(int16_t x, int16_t y);
|
||||
constexpr Point(int16_t x, int16_t y)
|
||||
: x(x), y(y)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//==================================
|
||||
|
@ -1266,60 +1266,6 @@ class Arduboy2Base : public Arduboy2Core
|
|||
*/
|
||||
void writeShowBootLogoLEDsFlag(bool val);
|
||||
|
||||
/** \brief
|
||||
* Returns the battery state.
|
||||
* \details
|
||||
* This function is intended as a method to determine a low battery state
|
||||
*
|
||||
* Returns the following states:
|
||||
* - BATTERY_STATE_LOW The battery low threshold has been reached.
|
||||
* - BATTERY_STATE_NORMAL The battery is considered normal.
|
||||
* - BATTERY_STATE_INVALID The ADC conversion is not ready yet or the
|
||||
* result is out of range.
|
||||
*
|
||||
* This fucntion depends on the EEPROM_BATTERY_LOW value been set to the
|
||||
* low batterly bandgap voltage value. The default value (0xFF) will
|
||||
* disable the EEPROM_BATTERY_LOW state.
|
||||
*
|
||||
* example:
|
||||
* \code{.cpp}
|
||||
* void loop() {
|
||||
* if (!arduboy.nextFrame()) return;
|
||||
* if (arduboy.everyXFrames(FRAMERATE) && arduboy.checkBatteryState() == BATTERY_STATE_LOW)
|
||||
* {
|
||||
* batteryLowWarning = true;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \see everyXFrames()
|
||||
*/
|
||||
uint8_t checkBatteryState();
|
||||
|
||||
/** \brief
|
||||
* Returns battery state and sets TXLED as a low battery indicator
|
||||
* \param flash defaults to 'false' for no flashing. use 'FLASH_LED' or
|
||||
* `true` for flashing.
|
||||
* \details
|
||||
* This function is intended as a method to determine a low battery state.
|
||||
* The TXLED is used as a battery low indicator. The TXLED will light up
|
||||
* continiously by default when the battery state is low. The optional
|
||||
* FLASH_LED parameter can be passed to make the LED toggle on or off on low
|
||||
* battery state.
|
||||
*
|
||||
* This function is a quick way of adding a low battery indicator to a sketch.
|
||||
*
|
||||
* example:
|
||||
* \code{.cpp}
|
||||
* void loop() {
|
||||
* if (!arduboy.nextFrame()) return;
|
||||
* //turn TXLED alternately on 1 second and off 1 second when battery is low
|
||||
* if (arduboy.everyXFrames(FRAMERATE)) checkBatteryStateLED(FLASH_LED);
|
||||
* \endcode
|
||||
*
|
||||
* \see checkBatteryState() everyXFrames()
|
||||
*/
|
||||
uint8_t checkBatteryStateLED(bool flash = false);
|
||||
|
||||
/** \brief
|
||||
* A counter which is incremented once per frame.
|
||||
*
|
||||
|
@ -1369,8 +1315,6 @@ class Arduboy2Base : public Arduboy2Core
|
|||
*/
|
||||
static uint8_t sBuffer[(HEIGHT*WIDTH)/8];
|
||||
|
||||
static uint8_t batteryLow;
|
||||
|
||||
protected:
|
||||
// helper function for sound enable/disable system control
|
||||
void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal);
|
||||
|
@ -1712,5 +1656,7 @@ class Arduboy2 : public Print, public Arduboy2Base
|
|||
bool textWrap;
|
||||
};
|
||||
|
||||
extern volatile unsigned long timer0_millis;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -13,8 +13,13 @@ const uint8_t PROGMEM lcdBootProgram[] = {
|
|||
// might prove useful for reference
|
||||
//
|
||||
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
|
||||
|
||||
#if defined(OLED_SH1106)
|
||||
|
||||
#if defined(GU12864_800B)
|
||||
0x24, 0x40, // enable Layer 0, graphic display area on
|
||||
0x47, // set brightness
|
||||
0x64, 0x00, // set x position 0
|
||||
0x84, // address mode set: X increment
|
||||
#elif defined(OLED_SH1106)
|
||||
0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
|
||||
0xA1, // Set Segment Re-map
|
||||
0xC8, // Set COM Output Scan Direction
|
||||
|
@ -321,7 +326,25 @@ void Arduboy2Core::bootPins()
|
|||
|
||||
void Arduboy2Core::bootOLED()
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
#if defined(GU12864_800B)
|
||||
bitSet(RST_PORT,RST_BIT);
|
||||
delayShort(10);
|
||||
displayEnable();
|
||||
for (uint8_t i = 0; i < sizeof(lcdBootProgram) + 8; i++)
|
||||
{
|
||||
if (i < 8)
|
||||
{
|
||||
displayWrite(0x62); // set display area
|
||||
displayWrite(i); // display area address
|
||||
LCDDataMode();
|
||||
displayWrite(0xFF); // Graphic display
|
||||
LCDCommandMode();
|
||||
}
|
||||
else
|
||||
displayWrite(pgm_read_byte(lcdBootProgram + i - 8));
|
||||
}
|
||||
displayDisable();
|
||||
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
i2c_start(SSD1306_I2C_CMD);
|
||||
for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++)
|
||||
i2c_sendByte(pgm_read_byte(lcdBootProgram + i));
|
||||
|
@ -476,10 +499,38 @@ void Arduboy2Core::bootPowerSaving()
|
|||
PRR1 = _BV(PRUSART1);
|
||||
}
|
||||
|
||||
#if defined(GU12864_800B)
|
||||
void Arduboy2Core::displayEnable()
|
||||
{
|
||||
bitSet(CS_PORT,CS_BIT);
|
||||
SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPOL) | _BV(CPHA);
|
||||
//bitClear(CS_PORT,CS_BIT);
|
||||
LCDCommandMode();
|
||||
}
|
||||
|
||||
void Arduboy2Core::displayDisable()
|
||||
{
|
||||
//bitSet(CS_PORT,CS_BIT);
|
||||
SPCR = _BV(SPE) | _BV(MSTR);
|
||||
}
|
||||
|
||||
void Arduboy2Core::displayWrite(uint8_t data)
|
||||
{
|
||||
bitClear(CS_PORT,CS_BIT);
|
||||
SPItransfer(data);
|
||||
bitSet(CS_PORT,CS_BIT);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shut down the display
|
||||
void Arduboy2Core::displayOff()
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
displayWrite(0x20);
|
||||
displayWrite(0x00);
|
||||
displayDisable();
|
||||
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
i2c_start(SSD1306_I2C_CMD);
|
||||
i2c_sendByte(0xAE); // display off
|
||||
i2c_sendByte(0x8D); // charge pump:
|
||||
|
@ -519,7 +570,24 @@ void Arduboy2Core::paint8Pixels(uint8_t pixels)
|
|||
|
||||
void Arduboy2Core::paintScreen(const uint8_t *image)
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
for (uint8_t r = 0; r < (HEIGHT/8); r++)
|
||||
{
|
||||
LCDCommandMode();
|
||||
displayWrite(0x60);
|
||||
displayWrite(r);
|
||||
LCDDataMode();
|
||||
for (uint8_t c = 0; c < (WIDTH); c++)
|
||||
{
|
||||
bitClear(CS_PORT,CS_BIT);
|
||||
SPDR = pgm_read_byte(image++);
|
||||
while (!(SPSR & _BV(SPIF)));
|
||||
bitSet(CS_PORT,CS_BIT);
|
||||
}
|
||||
}
|
||||
displayDisable();
|
||||
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
i2c_start(SSD1306_I2C_DATA);
|
||||
for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++)
|
||||
i2c_sendByte(pgm_read_byte(image+i));
|
||||
|
@ -599,7 +667,30 @@ void Arduboy2Core::paintScreen(const uint8_t *image)
|
|||
// will be used by any buffer based subclass
|
||||
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
||||
{
|
||||
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
for (uint8_t r = 0; r < (HEIGHT/8); r++)
|
||||
{
|
||||
LCDCommandMode();
|
||||
displayWrite(0x60);
|
||||
displayWrite(r);
|
||||
LCDDataMode();
|
||||
for (uint8_t c = 0; c < (WIDTH); c++)
|
||||
{
|
||||
bitClear(CS_PORT,CS_BIT);
|
||||
if (clear)
|
||||
{
|
||||
SPDR = *image; // set the first SPI data byte to get things started
|
||||
*(image++) = 0; // clear the first image byte
|
||||
}
|
||||
else
|
||||
SPDR = *(image++);
|
||||
while (!(SPSR & _BV(SPIF)));
|
||||
bitSet(CS_PORT,CS_BIT);
|
||||
}
|
||||
}
|
||||
displayDisable();
|
||||
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
|
||||
uint16_t length = WIDTH * HEIGHT / 8;
|
||||
uint8_t sda_clr = I2C_PORT & ~((1 << I2C_SDA) | (1 << I2C_SCL));
|
||||
uint8_t scl = 1 << I2C_SCL;
|
||||
|
@ -957,7 +1048,7 @@ void Arduboy2Core::sendLCDCommand(uint8_t command)
|
|||
i2c_start(SSD1306_I2C_CMD);
|
||||
i2c_sendByte(command);
|
||||
i2c_stop();
|
||||
#else
|
||||
#else if !defined GU12864_800B
|
||||
LCDCommandMode();
|
||||
SPItransfer(command);
|
||||
LCDDataMode();
|
||||
|
@ -968,26 +1059,57 @@ void Arduboy2Core::sendLCDCommand(uint8_t command)
|
|||
// when inverted, a pixel set to 0 will be on
|
||||
void Arduboy2Core::invert(bool inverse)
|
||||
{
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
displayWrite(0x24);
|
||||
if (inverse) displayWrite(0x50);
|
||||
else displayWrite(0x40);
|
||||
LCDDataMode();
|
||||
displayDisable();
|
||||
#else
|
||||
sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// turn all display pixels on, ignoring buffer contents
|
||||
// or set to normal buffer display
|
||||
void Arduboy2Core::allPixelsOn(bool on)
|
||||
{
|
||||
#if defined(GU12864_800B)
|
||||
displayEnable();
|
||||
if (on)
|
||||
{
|
||||
displayWrite(0x20);
|
||||
displayWrite(0x50);
|
||||
}
|
||||
else
|
||||
displayWrite(0x24);
|
||||
displayWrite(0x40);
|
||||
LCDDataMode();
|
||||
displayDisable();
|
||||
#else
|
||||
sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
|
||||
#endif
|
||||
}
|
||||
|
||||
// flip the display vertically or set to normal
|
||||
void Arduboy2Core::flipVertical(bool flipped)
|
||||
{
|
||||
#ifdef GU12864_800B
|
||||
//not available
|
||||
#else
|
||||
sendLCDCommand(flipped ? OLED_VERTICAL_FLIPPED : OLED_VERTICAL_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// flip the display horizontally or set to normal
|
||||
void Arduboy2Core::flipHorizontal(bool flipped)
|
||||
{
|
||||
#ifdef GU12864_800B
|
||||
//not available
|
||||
#else
|
||||
sendLCDCommand(flipped ? OLED_HORIZ_FLIPPED : OLED_HORIZ_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* RGB LED */
|
||||
|
@ -1201,9 +1323,9 @@ void Arduboy2Core::digitalWriteRGB(uint8_t color, uint8_t val)
|
|||
|
||||
uint8_t Arduboy2Core::buttonsState()
|
||||
{
|
||||
#ifndef ARDUBOY_CORE
|
||||
uint8_t buttons;
|
||||
#ifdef ARDUBOY_10
|
||||
|
||||
#ifdef ARDUBOY_10
|
||||
// up, right, left, down
|
||||
buttons = ((~PINF) &
|
||||
(_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
|
||||
|
@ -1222,11 +1344,8 @@ uint8_t Arduboy2Core::buttonsState()
|
|||
if (bitRead(A_BUTTON_PORTIN, A_BUTTON_BIT) == 0) { buttons |= A_BUTTON; }
|
||||
// B
|
||||
if (bitRead(B_BUTTON_PORTIN, B_BUTTON_BIT) == 0) { buttons |= B_BUTTON; }
|
||||
#endif
|
||||
#else
|
||||
register uint8_t buttons asm("r24");
|
||||
asm volatile("call scan_buttons\n\t" : "=d" (buttons));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
|
@ -1242,8 +1361,10 @@ void Arduboy2Core::delayShort(uint16_t ms)
|
|||
|
||||
void Arduboy2Core::exitToBootloader()
|
||||
{
|
||||
#ifndef ARDUBOY_CORE
|
||||
cli();
|
||||
#ifdef ARDUBOY_CORE
|
||||
asm volatile ("jmp exit_to_bootloader");
|
||||
#else
|
||||
// set bootloader magic key
|
||||
// storing two uint8_t instead of one uint16_t saves an instruction
|
||||
// when high and low bytes of the magic key are the same
|
||||
|
@ -1254,10 +1375,7 @@ void Arduboy2Core::exitToBootloader()
|
|||
WDTCSR = (_BV(WDCE) | _BV(WDE));
|
||||
WDTCSR = _BV(WDE);
|
||||
while (true) { }
|
||||
#else
|
||||
bootloader_timer = 120; //ms
|
||||
while (true) { }
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Replacement main() that eliminates the USB stack code.
|
||||
|
|
|
@ -61,6 +61,9 @@ extern volatile unsigned char bootloader_timer;
|
|||
#define DC_BIT PORTD4 // Display D/C physical bit number
|
||||
|
||||
#ifdef CART_CS_SDA
|
||||
#ifdef AB_ALTERNATE_WIRING
|
||||
#error SDA can not be used as flash chip select when using Pro Micro alternate wiring. Use RX instead.
|
||||
#endif
|
||||
#define PIN_CART 2 // SDA as alternative flash cart chip select
|
||||
#define CART_BIT PORTD1
|
||||
#else
|
||||
|
@ -462,7 +465,11 @@ class Arduboy2Core
|
|||
*/
|
||||
void static inline LCDDataMode() __attribute__((always_inline))
|
||||
{
|
||||
#if defined(GU12864_800B)
|
||||
bitClear(DC_PORT, DC_BIT);
|
||||
#else
|
||||
bitSet(DC_PORT, DC_BIT);
|
||||
#endif
|
||||
}
|
||||
/** \brief
|
||||
* Put the display into command mode.
|
||||
|
@ -488,7 +495,11 @@ class Arduboy2Core
|
|||
*/
|
||||
void static inline LCDCommandMode() __attribute__((always_inline))
|
||||
{
|
||||
#ifdef GU12864_800B
|
||||
bitSet(DC_PORT, DC_BIT);
|
||||
#else
|
||||
bitClear(DC_PORT, DC_BIT);
|
||||
#endif
|
||||
}
|
||||
/** \brief
|
||||
* Transfer a byte to the display.
|
||||
|
@ -972,6 +983,12 @@ class Arduboy2Core
|
|||
void static bootOLED();
|
||||
void static bootPins();
|
||||
void static bootPowerSaving();
|
||||
#if defined(GU12864_800B)
|
||||
void static displayWrite(uint8_t d);
|
||||
void static displayEnable();
|
||||
void static displayDisable();
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,83 +44,84 @@ void Sprites::draw(int16_t x, int16_t y,
|
|||
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;
|
||||
// }
|
||||
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;
|
||||
}
|
||||
// assembly optimisation of above code saving 20(+) bytes
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
asm volatile(
|
||||
" lpm %[width], Z+ \n\t" // width = pgm_read_byte(bitmap++);
|
||||
" lpm %[height], Z+ \n\t" // height = pgm_read_byte(bitmap++);
|
||||
" cp %[frame], __zero_reg__ \n\t" // if (frame > 0 || sprite_frame > 0)
|
||||
" brne 1f \n\t"
|
||||
" cp %[spr_frame], __zero_reg__ \n\t"
|
||||
" breq 3f \n\t"
|
||||
"1: \n\t"
|
||||
" ldi r20, 7 \n\t" //rows = ((height+7)
|
||||
" add r20, %[height] \n\t"
|
||||
" ror r20 \n\t" //include carry for heights > 248
|
||||
" lsr r20 \n\t" //rows = (height+7) / 8
|
||||
" lsr r20 \n\t"
|
||||
" cpi %[mode], %[sprite_plus_mask] \n\t" //if (drawMode == SPRITE_PLUS_MASK) rows *= 2
|
||||
" brne 2f \n\t"
|
||||
" lsl r20 \n\t"
|
||||
"2: \n\t"
|
||||
" mul r20, %[width] \n\t" //frame offset = rows * width
|
||||
" movw r20, r0 \n\t"
|
||||
" mul %[frame] , r20 \n\t"
|
||||
" add %A[bitmap], r0 \n\t" //bitmap += frame * (frame offset & 0xFF)
|
||||
" adc %B[bitmap], r1 \n\t"
|
||||
" mul %[frame] , r21 \n\t" //bitmap += frame * (frame_offset >> 8 )) << 8
|
||||
" add %B[bitmap], r0 \n\t"
|
||||
" \n\t"
|
||||
" adiw %A[mask], 0 \n\t" //if (mask != NULL)
|
||||
" breq 3f \n\t"
|
||||
" \n\t"
|
||||
" mul %[spr_frame] , r20 \n\t"
|
||||
" add %A[mask], r0 \n\t" //mask += sprite_frame * (frame offset & 0xFF)
|
||||
" adc %B[mask], r1 \n\t"
|
||||
" mul %[spr_frame] , r21 \n\t" //mask += (sprite_frame * (frame_offset >> 8 )) << 8
|
||||
" add %B[mask], r0 \n\t"
|
||||
"3: \n\t"
|
||||
" clr __zero_reg__ \n\t"
|
||||
"4: \n\t"
|
||||
" cpi %[mode], %[sprite_auto_mode] \n\t" //if (drawMode == SPRITE_AUTO_MODE)
|
||||
" brne 5f \n\t"
|
||||
" adiw %A[mask], 0 \n\t" //if (mask = NULL) drawMode = SPRITE_UNMASKED
|
||||
" ldi %[mode], %[sprite_unmasked] \n\t"
|
||||
" breq 5f \n\t"
|
||||
" ldi %[mode], %[sprite_masked] \n\t" //else drawMode = SPRITE_PLUS_MASK
|
||||
"5: \n\t"
|
||||
: [width] "=&r" (width),
|
||||
[height] "=&r" (height),
|
||||
[mask] "+x" (mask),
|
||||
[bitmap] "+z" (bitmap),
|
||||
[mode] "+d" (drawMode)
|
||||
: [frame] "r" (frame),
|
||||
[spr_frame] "r" (sprite_frame),
|
||||
[sprite_plus_mask] "M" (SPRITE_PLUS_MASK),
|
||||
[sprite_auto_mode] "M" (SPRITE_AUTO_MODE),
|
||||
[sprite_unmasked] "M" (SPRITE_UNMASKED),
|
||||
[sprite_masked] "M" (SPRITE_MASKED)
|
||||
: "r20", "r21"
|
||||
);
|
||||
// uint8_t width;
|
||||
// uint8_t height;
|
||||
// asm volatile(
|
||||
// " lpm %[width], Z+ \n\t" // width = pgm_read_byte(bitmap++);
|
||||
// " lpm %[height], Z+ \n\t" // height = pgm_read_byte(bitmap++);
|
||||
// " cp %[frame], __zero_reg__ \n\t" // if (frame > 0 || sprite_frame > 0)
|
||||
// " brne 1f \n\t"
|
||||
// " cp %[spr_frame], __zero_reg__ \n\t"
|
||||
// " breq 3f \n\t"
|
||||
// "1: \n\t"
|
||||
// " ldi r20, 7 \n\t" //rows = ((height+7)
|
||||
// " add r20, %[height] \n\t"
|
||||
// " ror r20 \n\t" //include carry for heights > 248
|
||||
// " lsr r20 \n\t" //rows = (height+7) / 8
|
||||
// " lsr r20 \n\t"
|
||||
// " cpi %[mode], %[sprite_plus_mask] \n\t" //if (drawMode == SPRITE_PLUS_MASK) rows *= 2
|
||||
// " brne 2f \n\t"
|
||||
// " lsl r20 \n\t"
|
||||
// "2: \n\t"
|
||||
// " mul r20, %[width] \n\t" //frame offset = rows * width
|
||||
// " movw r20, r0 \n\t"
|
||||
// " mul %[frame] , r20 \n\t"
|
||||
// " add %A[bitmap], r0 \n\t" //bitmap += frame * (frame offset & 0xFF)
|
||||
// " adc %B[bitmap], r1 \n\t"
|
||||
// " mul %[frame] , r21 \n\t" //bitmap += frame * (frame_offset >> 8 )) << 8
|
||||
// " add %B[bitmap], r0 \n\t"
|
||||
// " \n\t"
|
||||
// " adiw %A[mask], 0 \n\t" //if (mask != NULL)
|
||||
// " breq 3f \n\t"
|
||||
// " \n\t"
|
||||
// " mul %[spr_frame] , r20 \n\t"
|
||||
// " add %A[mask], r0 \n\t" //mask += sprite_frame * (frame offset & 0xFF)
|
||||
// " adc %B[mask], r1 \n\t"
|
||||
// " mul %[spr_frame] , r21 \n\t" //mask += (sprite_frame * (frame_offset >> 8 )) << 8
|
||||
// " add %B[mask], r0 \n\t"
|
||||
// "3: \n\t"
|
||||
// " clr __zero_reg__ \n\t"
|
||||
// "4: \n\t"
|
||||
// " cpi %[mode], %[sprite_auto_mode] \n\t" //if (drawMode == SPRITE_AUTO_MODE)
|
||||
// " brne 5f \n\t"
|
||||
// " adiw %A[mask], 0 \n\t" //if (mask = NULL) drawMode = SPRITE_UNMASKED
|
||||
// " ldi %[mode], %[sprite_unmasked] \n\t"
|
||||
// " breq 5f \n\t"
|
||||
// " ldi %[mode], %[sprite_masked] \n\t" //else drawMode = SPRITE_PLUS_MASK
|
||||
// "5: \n\t"
|
||||
// : [width] "=&r" (width),
|
||||
// [height] "=&r" (height),
|
||||
// [mask] "+x" (mask),
|
||||
// [bitmap] "+z" (bitmap),
|
||||
// [mode] "+d" (drawMode)
|
||||
// : [frame] "r" (frame),
|
||||
// [spr_frame] "r" (sprite_frame),
|
||||
// [sprite_plus_mask] "M" (SPRITE_PLUS_MASK),
|
||||
// [sprite_auto_mode] "M" (SPRITE_AUTO_MODE),
|
||||
// [sprite_unmasked] "M" (SPRITE_UNMASKED),
|
||||
// [sprite_masked] "M" (SPRITE_MASKED)
|
||||
// : "r20", "r21"
|
||||
// );
|
||||
drawBitmap(x, y, bitmap, mask, width, height, drawMode);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue