update ArduboyFX library

Update Arduboy FX library to v1.0.8
add manufacturer strings to boards.txt
This commit is contained in:
Mr.Blinky 2023-02-28 16:46:51 +01:00
parent e4eba41912
commit fed21b40cb
5 changed files with 393 additions and 125 deletions

View File

@ -326,6 +326,7 @@ arduboy-fx.build.board=AVR_ARDUBOY
arduboy-fx.build.vid=0x2341 arduboy-fx.build.vid=0x2341
arduboy-fx.build.pid=0x8036 arduboy-fx.build.pid=0x8036
arduboy-fx.build.variant=arduboy arduboy-fx.build.variant=arduboy
arduboy-fx.build.usb_manufacturer="Arduboy Inc"
arduboy-fx.build.usb_product="Arduboy" arduboy-fx.build.usb_product="Arduboy"
arduboy-fx.build.board=AVR_ARDUBOY arduboy-fx.build.board=AVR_ARDUBOY
arduboy-fx.build.core=arduino:arduino arduboy-fx.build.core=arduino:arduino
@ -407,6 +408,7 @@ arduboy-fx-devkit.build.board=AVR_ARDUBOY
arduboy-fx-devkit.build.vid=0x2341 arduboy-fx-devkit.build.vid=0x2341
arduboy-fx-devkit.build.pid=0x8036 arduboy-fx-devkit.build.pid=0x8036
arduboy-fx-devkit.build.variant=arduboy arduboy-fx-devkit.build.variant=arduboy
arduboy-fx-devkit.build.usb_manufacturer="Arduboy Inc"
arduboy-fx-devkit.build.usb_product="Arduboy" arduboy-fx-devkit.build.usb_product="Arduboy"
arduboy-fx-devkit.build.board=AVR_ARDUBOY arduboy-fx-devkit.build.board=AVR_ARDUBOY
arduboy-fx-devkit.build.core=arduino:arduino arduboy-fx-devkit.build.core=arduino:arduino
@ -488,6 +490,7 @@ arduboy.build.board=AVR_ARDUBOY
arduboy.build.vid=0x2341 arduboy.build.vid=0x2341
arduboy.build.pid=0x8036 arduboy.build.pid=0x8036
arduboy.build.variant=arduboy arduboy.build.variant=arduboy
arduboy.build.usb_manufacturer="Arduboy Inc"
arduboy.build.usb_product="Arduboy" arduboy.build.usb_product="Arduboy"
arduboy.build.board=AVR_ARDUBOY arduboy.build.board=AVR_ARDUBOY
arduboy.build.core=arduino:arduino arduboy.build.core=arduino:arduino
@ -574,6 +577,7 @@ arduboy-devkit.build.board=AVR_ARDUBOY
arduboy-devkit.build.vid=0x2341 arduboy-devkit.build.vid=0x2341
arduboy-devkit.build.pid=0x8036 arduboy-devkit.build.pid=0x8036
arduboy-devkit.build.variant=arduboy-devkit arduboy-devkit.build.variant=arduboy-devkit
arduboy-devkit.build.usb_manufacturer="Arduboy Inc"
arduboy-devkit.build.usb_product="ABDevKit" arduboy-devkit.build.usb_product="ABDevKit"
arduboy-devkit.build.board=AVR_ARDUBOY_DEVKIT arduboy-devkit.build.board=AVR_ARDUBOY_DEVKIT
arduboy-devkit.build.core=arduino:arduino arduboy-devkit.build.core=arduino:arduino
@ -641,6 +645,7 @@ arduboy-devkit.menu.boot.cathy3kg.bootloader.file=cathy3k/arduboy3k-bootloader-g
8bitcadexl.build.board=AVR_PROMICRO -DAB_ALTERNATE_WIRING 8bitcadexl.build.board=AVR_PROMICRO -DAB_ALTERNATE_WIRING
8bitcadexl.build.vid=0x2341 8bitcadexl.build.vid=0x2341
8bitcadexl.build.pid=0x8036 8bitcadexl.build.pid=0x8036
8bitcadexl.build.usb_manufacturer="8bitCADE"
8bitcadexl.build.usb_product="8bitCADE" 8bitcadexl.build.usb_product="8bitCADE"
8bitcadexl.build.core=arduino:arduino 8bitcadexl.build.core=arduino:arduino
8bitcadexl.build.flash_cs=-DCART_CS_RX 8bitcadexl.build.flash_cs=-DCART_CS_RX
@ -724,6 +729,7 @@ arduboy-devkit.menu.boot.cathy3kg.bootloader.file=cathy3k/arduboy3k-bootloader-g
8bitcadexlup.build.board=AVR_PROMICRO -DAB_ALTERNATE_WIRING 8bitcadexlup.build.board=AVR_PROMICRO -DAB_ALTERNATE_WIRING
8bitcadexlup.build.vid=0x2341 8bitcadexlup.build.vid=0x2341
8bitcadexlup.build.pid=0x8036 8bitcadexlup.build.pid=0x8036
8bitcadexlup.build.usb_manufacturer="8bitCADE"
8bitcadexlup.build.usb_product="8bitCADE" 8bitcadexlup.build.usb_product="8bitCADE"
8bitcadexlup.build.core=arduino:arduino 8bitcadexlup.build.core=arduino:arduino
8bitcadexlup.build.flash_cs=-DCART_CS_RX 8bitcadexlup.build.flash_cs=-DCART_CS_RX
@ -807,6 +813,7 @@ microcade.build.variant=arduboy-promicro-alt
microcade.build.board=AVR_PROMICRO -DAB_ALTERNATE_WIRING microcade.build.board=AVR_PROMICRO -DAB_ALTERNATE_WIRING
microcade.build.vid=0x2341 microcade.build.vid=0x2341
microcade.build.pid=0x8036 microcade.build.pid=0x8036
microcade.build.usb_manufacturer="microcade"
microcade.build.usb_product="microcade" microcade.build.usb_product="microcade"
microcade.build.core=arduino:arduino microcade.build.core=arduino:arduino
microcade.build.flash_cs=-DCART_CS_RX microcade.build.flash_cs=-DCART_CS_RX

View File

@ -1,5 +1,5 @@
/* ***************************************************************************** /* *****************************************************************************
* FX gameState example v1.00 by Mr.Blinky Jan.2023 licenced under CC0 * FX gameState example v1.01 by Mr.Blinky Jan-Feb.2023 licenced under CC0
* ***************************************************************************** * *****************************************************************************
* *
* This is a example showing how you can save and load game data to the FX flash * This is a example showing how you can save and load game data to the FX flash
@ -22,9 +22,9 @@
* *
* The FX library has two functions for saving and loading game data: * The FX library has two functions for saving and loading game data:
* *
* uint8_t FX::loadGameState(uint8_t* gameState, size_t size) * uint8_t FX::loadGameState(Type gameState)
* *
* void FX::saveGameState(uint8_t* gameState, size_t size) * void FX::saveGameState(Type gameState)
* *
* loadGameState * loadGameState
* ------------- * -------------
@ -32,15 +32,12 @@
* Loads previously saved game data. It returns 0 if there was no data to load * Loads previously saved game data. It returns 0 if there was no data to load
* or 1 if the data was successfully loaded. * or 1 if the data was successfully loaded.
* *
* gameState is a pointer to an uint8_t array or to a structure in RAM containing * gameState is an object structure in RAM to where the game data will be loaded.
* your game data. When using a structure for the game data, the structure must be * If the size of the object in ram differes from the size of previously saved
* cast to an uint8_t array by putting (uint8_t*)& in front of the structures name. * data, no data will be loaded.
* *
* size is the size of the uint8_t array or structure in RAM and is usually * When no game data loaded (result 0), the contents of the gameState structure
* determined by using the sizeof() operator. * remains unchanged.
*
* When there is no game data loaded (result 0), the contents of the gameState
* structure remains unchanged.
* *
* saveGameState * saveGameState
* ------------- * -------------
@ -54,7 +51,7 @@
* This process is fully transparent and you do not need to worry about it. * This process is fully transparent and you do not need to worry about it.
* It just explains why sometimes this function may take a bit more time then usual. * It just explains why sometimes this function may take a bit more time then usual.
* *
* gameState and size are the same parameters as for the loadSaveData function. * gameState is an object structure in RAM which will be saved to external flash memory.
* *
* initilizing FX chip when using save capabilities * initilizing FX chip when using save capabilities
* ------------------------------------------------ * ------------------------------------------------
@ -135,8 +132,8 @@ void setup() {
FX::begin(FX_DATA_PAGE, FX_SAVE_PAGE); // Initialize FX chip. When using FX save data, FX::begin(FX_DATA_PAGE, FX_SAVE_PAGE); // Initialize FX chip. When using FX save data,
// FX_SAVE_PAGE must also be passed on in FX::begin. // FX_SAVE_PAGE must also be passed on in FX::begin.
if (!FX::loadGameState((uint8_t*) &gameState, sizeof(gameState))) // Load saveState from the 4K FX save data block. '(uint8_t*) &' is used if (!FX::loadGameState(gameState)) // Load saveState from the 4K FX save data block. '(uint8_t*) &' is used
// here to cast a uint8_t pointer to the saveState structure. // here to cast a uint8_t pointer to the saveState structure.
{ {
// This code gets executed only when the function didn't load a saveState (function returned 0) // This code gets executed only when the function didn't load a saveState (function returned 0)
strcpy(gameState.name, "Hello World !!!"); // copy some introductory text to saveState.name strcpy(gameState.name, "Hello World !!!"); // copy some introductory text to saveState.name
@ -149,7 +146,7 @@ void setup() {
gameState.count ++; // increase the number of times this sketch was run. gameState.count ++; // increase the number of times this sketch was run.
} }
FX::saveGameState((uint8_t*) &gameState, sizeof(gameState)); //Save the game state to 4K FX save block FX::saveGameState(gameState); //Save the game state to 4K FX save block
arduboy.clear(); arduboy.clear();
arduboy.setCursor(0,32 - 8); arduboy.setCursor(0,32 - 8);

View File

@ -1,5 +1,5 @@
name=ArduboyFX name=ArduboyFX
version=1.0.7 version=1.0.8
author=Mr.Blinky author=Mr.Blinky
maintainer=mstr.blinky@gmail.com maintainer=mstr.blinky@gmail.com
sentence=The Arduboy FX library. sentence=The Arduboy FX library.

View File

@ -11,8 +11,15 @@ FrameControl FX::frameControl;
uint8_t FX::writeByte(uint8_t data) uint8_t FX::writeByte(uint8_t data)
{ {
writeByteBeforeWait(data); SPDR = data;
return SPDR; asm volatile("nop\n");
uint8_t result;
do
{
result = SPDR; //reading data here saves 1 extra cycle
}
while ((SPSR & (1 << SPIF)) == 0);
return result;
} }
@ -33,8 +40,9 @@ void FX::begin(uint16_t developmentDataPage)
{ {
disableOLED(); disableOLED();
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
const uint8_t* vector = FX_DATA_VECTOR_KEY_POINTER; const uint8_t* vector = (const uint8_t*)FX_DATA_VECTOR_KEY_POINTER;
asm volatile( asm volatile
(
"lpm r18, z+ \n" "lpm r18, z+ \n"
"lpm r19, z+ \n" "lpm r19, z+ \n"
"lpm r21, z+ \n" "lpm r21, z+ \n"
@ -68,8 +76,9 @@ void FX::begin(uint16_t developmentDataPage, uint16_t developmentSavePage)
{ {
disableOLED(); disableOLED();
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
const uint8_t* vector = FX_DATA_VECTOR_KEY_POINTER; const uint8_t* vector = (const uint8_t*)FX_DATA_VECTOR_KEY_POINTER;
asm volatile( asm volatile
(
"lpm r18, z+ \n" "lpm r18, z+ \n"
"lpm r19, z+ \n" "lpm r19, z+ \n"
"lpm r21, z+ \n" "lpm r21, z+ \n"
@ -118,6 +127,16 @@ void FX::begin(uint16_t developmentDataPage, uint16_t developmentSavePage)
wakeUp(); wakeUp();
} }
void FX::readJedecID(JedecID & id)
{
enable();
writeByte(SFC_JEDEC_ID);
id.manufacturer = readByte();
id.device = readByte();
id.size = readByte();
disable();
}
void FX::readJedecID(JedecID* id) void FX::readJedecID(JedecID* id)
{ {
enable(); enable();
@ -180,14 +199,15 @@ void FX::seekCommand(uint8_t command, uint24_t address)
enable(); enable();
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
register uint8_t cmd asm("r24") = command; //assembly optimizer for AVR platform ~saves 12 bytes register uint8_t cmd asm("r24") = command; //assembly optimizer for AVR platform ~saves 12 bytes
asm volatile( asm volatile
(
"call %x2 \n" "call %x2 \n"
"mov r24, %C[addr] \n" "mov r24, %C[addr] \n"
"call %x2 \n" "call %x2 \n"
"mov r24, %B[addr] \n" "mov r24, %B[addr] \n"
"call %x2 \n" "call %x2 \n"
"mov r24, %A[addr] \n" "mov r24, %A[addr] \n"
"call %x2 \n" "jmp %x2 \n"
: [cmd] "+&r" (cmd) : [cmd] "+&r" (cmd)
: [addr] "r" (address), : [addr] "r" (address),
[write] "i" (writeByte) [write] "i" (writeByte)
@ -204,20 +224,25 @@ void FX::seekCommand(uint8_t command, uint24_t address)
void FX::seekData(uint24_t address) void FX::seekData(uint24_t address)
{ {
uint24_t abs_address = address;
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
asm volatile( // assembly optimizer for AVR platform asm volatile
"lds r0, %[page]+0 \n" (
"add %B[addr], r0 \n" "mov %A[abs], %A[addr] \n"
"lds r0, %[page]+1 \n" "lds %B[abs], %[page]+0 \n"
"adc %C[addr], r0 \n" "add %B[abs], %B[addr] \n"
:[addr] "+&r" (address) "lds %C[abs], %[page]+1 \n"
:[page] "" (&programDataPage) "adc %C[abs], %C[addr] \n"
:
[abs] "=r" (abs_address)
:[page] "" (&programDataPage),
[addr] "r" (address)
: :
); );
#else // C++ version for non AVR platforms #else // C++ version for non AVR platforms
address += (uint24_t)programDataPage << 8; abs_address = address + (uint24_t)programDataPage << 8;
#endif #endif
seekCommand(SFC_READ, address); seekCommand(SFC_READ, abs_address);
SPDR = 0; SPDR = 0;
} }
@ -225,7 +250,8 @@ void FX::seekData(uint24_t address)
void FX::seekDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t elementSize) void FX::seekDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t elementSize)
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
asm volatile ( asm volatile
(
" mul %[index], %[size] \n" " mul %[index], %[size] \n"
" brne .+2 \n" //treat size 0 as size 256 " brne .+2 \n" //treat size 0 as size 256
" mov r1, %[index] \n" " mov r1, %[index] \n"
@ -236,43 +262,49 @@ void FX::seekDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t
" adc %B[address], r1 \n" " adc %B[address], r1 \n"
" adc %C[address], r21 \n" " adc %C[address], r21 \n"
" clr r1 \n" " clr r1 \n"
: [address] "+&r" (address) " jmp %x4 \n" //seekData
:
: [index] "r" (index), : [index] "r" (index),
[offset] "r" (offset), [offset] "r" (offset),
[size] "r" (elementSize) [size] "r" (elementSize),
[address] "r" (address),
"" (seekData)
: "r21" : "r21"
); );
#else #else
address += size ? index * size + offset : index * 256 + offset; address += size ? index * size + offset : index * 256 + offset;
#endif
seekData(address); seekData(address);
#endif
} }
void FX::seekSave(uint24_t address) void FX::seekSave(uint24_t address)
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
asm volatile( // assembly optimizer for AVR platform uint24_t abs_address = address;
"lds r0, %[page]+0 \n" asm volatile
"add %B[addr], r0 \n" (
"lds r0, %[page]+1 \n" "mov %A[abs], %A[addr] \n"
"adc %C[addr], r0 \n" "lds %B[abs], %[page]+0 \n"
:[addr] "+&r" (address) "add %B[abs], %B[addr] \n"
:[page] "" (&programSavePage) "lds %C[abs], %[page]+1 \n"
"adc %C[abs], %C[addr] \n"
:
[abs] "=r" (abs_address)
:[page] "" (&programSavePage),
[addr] "r" (address)
:
); );
#else // C++ version for non AVR platforms #else // C++ version for non AVR platforms
address += (uint24_t)programSavePage << 8; abs_address = address + (uint24_t)programSavePage << 8;
#endif #endif
seekCommand(SFC_READ, address); seekCommand(SFC_READ, abs_address);
SPDR = 0; SPDR = 0;
} }
uint8_t FX::readPendingUInt8() uint8_t FX::readPendingUInt8()
{ {
#ifdef ARDUINO_ARCH_AVR
asm volatile("ArduboyFX_cpp_readPendingUInt8:\n"); // create label for calls in FX::readPendingUInt16
#endif
wait(); wait();
uint8_t result = SPDR; uint8_t result = SPDR;
SPDR = 0; SPDR = 0;
@ -282,7 +314,8 @@ uint8_t FX::readPendingUInt8()
uint8_t FX::readPendingLastUInt8() uint8_t FX::readPendingLastUInt8()
{ {
return readEnd(); wait(); // wait for a pending read to complete
return readUnsafeEnd(); // read last byte and disable flash
} }
@ -291,10 +324,10 @@ uint16_t FX::readPendingUInt16()
#ifdef ARDUINO_ARCH_AVR // Assembly implementation for AVR platform #ifdef ARDUINO_ARCH_AVR // Assembly implementation for AVR platform
uint16_t result asm("r24"); // we want result to be assigned to r24,r25 uint16_t result asm("r24"); // we want result to be assigned to r24,r25
asm volatile asm volatile
( "ArduboyFX_cpp_readPendingUInt16: \n" (
"call ArduboyFX_cpp_readPendingUInt8 \n" "call %x1 \n"
"mov %B[val], r24 \n" "mov %B[val], r24 \n"
"jmp ArduboyFX_cpp_readPendingUInt8 \n" "jmp %x1 \n"
: [val] "=&r" (result) : [val] "=&r" (result)
: "" (readPendingUInt8) : "" (readPendingUInt8)
: :
@ -311,7 +344,7 @@ uint16_t FX::readPendingLastUInt16()
#ifdef ARDUINO_ARCH_AVR // Assembly implementation for AVR platform #ifdef ARDUINO_ARCH_AVR // Assembly implementation for AVR platform
uint16_t result asm("r24"); // we want result to be assigned to r24,r25 uint16_t result asm("r24"); // we want result to be assigned to r24,r25
asm volatile asm volatile
( "ArduboyFX_cpp_readPendingLastUInt16: \n" (
"call %x1 \n" "call %x1 \n"
"mov %B[val], r24 \n" "mov %B[val], r24 \n"
"jmp %x2 \n" "jmp %x2 \n"
@ -333,11 +366,11 @@ uint24_t FX::readPendingUInt24()
uint24_t result asm("r24"); // we want result to be assigned to r24,r25,r26 uint24_t result asm("r24"); // we want result to be assigned to r24,r25,r26
asm volatile asm volatile
( (
"call ArduboyFX_cpp_readPendingUInt16 \n" "call %x1 \n"
"mov %B[val], r24 \n" "mov %B[val], r24 \n"
"call ArduboyFX_cpp_readPendingUInt8 \n" "call %x2 \n"
"mov %A[val], r24 \n" "mov %A[val], r24 \n"
"mov %C[val], r25 \n" "mov %C[val], r25 \n"
: [val] "=&r" (result) : [val] "=&r" (result)
: "" (readPendingUInt16), : "" (readPendingUInt16),
"" (readPendingUInt8) "" (readPendingUInt8)
@ -379,9 +412,9 @@ uint32_t FX::readPendingUInt32()
uint32_t result asm("r24"); // we want result to be assigned to r24,r25,r26,r27 uint32_t result asm("r24"); // we want result to be assigned to r24,r25,r26,r27
asm volatile asm volatile
( (
"call ArduboyFX_cpp_readPendingUInt16 \n" "call %x1 \n"
"movw %C[val], r24 \n" "movw %C[val], r24 \n"
"call ArduboyFX_cpp_readPendingUInt16 \n" "call %x1 \n"
: [val] "=&r" (result) : [val] "=&r" (result)
: "" (readPendingUInt16) : "" (readPendingUInt16)
: :
@ -399,11 +432,12 @@ uint32_t FX::readPendingLastUInt32()
uint32_t result asm("r24"); // we want result to be assigned to r24,r25,r26,r27 uint32_t result asm("r24"); // we want result to be assigned to r24,r25,r26,r27
asm volatile asm volatile
( (
"call ArduboyFX_cpp_readPendingUInt16 \n" "call %x1 \n"
"movw %C[val], r24 \n" "movw %C[val], r24 \n"
"call ArduboyFX_cpp_readPendingLastUInt16 \n" "call %x2 \n"
: [val] "=&r" (result) : [val] "=&r" (result)
: "" (readPendingUInt16) : "" (readPendingUInt16),
"" (readPendingLastUInt16)
: :
); );
return result; return result;
@ -415,16 +449,52 @@ uint32_t FX::readPendingLastUInt32()
void FX::readBytes(uint8_t* buffer, size_t length) void FX::readBytes(uint8_t* buffer, size_t length)
{ {
#ifdef ARDUINO_ARCH_AVR
asm volatile(
"1: \n"
"call %x2 \n"
"st z+,r24 \n"
"subi %A[len], 1 \n"
"sbci %B[len], 0 \n"
"brne 1b \n"
: "+&z" (buffer),
[len] "+&d" (length)
: "" (readPendingUInt8)
: "r24"
);
#else
for (size_t i = 0; i < length; i++) for (size_t i = 0; i < length; i++)
{ {
buffer[i] = readPendingUInt8(); buffer[i] = readPendingUInt8();
} }
#endif
} }
void FX::readBytesEnd(uint8_t* buffer, size_t length) void FX::readBytesEnd(uint8_t* buffer, size_t length)
{ {
for (size_t i = 0; i <= length; i++) #ifdef ARDUINO_ARCH_AVR
asm volatile(
"1: \n"
"subi %A[len], 1 \n"
"sbci %B[len], 0 \n"
"breq 2f \n"
" \n"
"call %x2 \n"
"st z+, r24 \n"
"rjmp 1b \n"
"2: \n"
"call %x3 \n"
"st z, r24 \n"
: "+&z" (buffer),
[len] "+&d" (length)
: "" (readPendingUInt8),
"" (readEnd)
: "r24"
);
#else
for (size_t i = 0; i <= length; i++)
{ {
if ((i+1) != length) if ((i+1) != length)
buffer[i] = readPendingUInt8(); buffer[i] = readPendingUInt8();
@ -434,6 +504,7 @@ void FX::readBytesEnd(uint8_t* buffer, size_t length)
break; break;
} }
} }
#endif
} }
@ -517,7 +588,7 @@ uint8_t FX::loadGameState(uint8_t* gameState, size_t size) // ~54 bytes
return result; return result;
} }
void FX::saveGameState(uint8_t* gameState, size_t size) // ~152 bytes locates free space in 4K save block and saves the GamesState. void FX::saveGameState(const uint8_t* gameState, size_t size) // ~152 bytes locates free space in 4K save block and saves the GamesState.
{ // if there is not enough free space, the block is erased prior to saving { // if there is not enough free space, the block is erased prior to saving
register size_t sz asm("r18") = size; register size_t sz asm("r18") = size;
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
@ -697,7 +768,7 @@ void FX::drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8
skiptop = -y & -8; // optimized -y / 8 * 8 skiptop = -y & -8; // optimized -y / 8 * 8
if (height - skiptop <= HEIGHT) renderheight = height - skiptop; if (height - skiptop <= HEIGHT) renderheight = height - skiptop;
else renderheight = HEIGHT + (-y & 7); else renderheight = HEIGHT + (-y & 7);
skiptop >>= 3;//pixels to displayrows skiptop = fastDiv8(skiptop); // pixels to displayrows
} }
else else
{ {
@ -705,7 +776,7 @@ void FX::drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8
if (y + height > HEIGHT) renderheight = HEIGHT - y; if (y + height > HEIGHT) renderheight = HEIGHT - y;
else renderheight = height; else renderheight = height;
} }
uint24_t offset = (uint24_t)(frame * ((height+7) / 8) + skiptop) * width + skipleft; uint24_t offset = (uint24_t)(frame * (fastDiv8(height+(uint16_t)7)) + skiptop) * width + skipleft;
if (mode & dbmMasked) if (mode & dbmMasked)
{ {
offset += offset; // double for masked bitmaps offset += offset; // double for masked bitmaps
@ -874,20 +945,20 @@ void FX::drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8
{ {
seekData(address); seekData(address);
address += width; address += width;
mode &= ~(_BV(dbfExtraRow)); mode &= ~((1 << dbfExtraRow));
if (yshift != 1 && displayrow < (HEIGHT / 8 - 1)) mode |= _BV(dbfExtraRow); if (yshift != 1 && displayrow < (HEIGHT / 8 - 1)) mode |= (1 << dbfExtraRow);
uint8_t rowmask = 0xFF; uint8_t rowmask = 0xFF;
if (renderheight < 8) rowmask = lastmask; if (renderheight < 8) rowmask = lastmask;
wait(); wait();
for (uint8_t c = 0; c < renderwidth; c++) for (uint8_t c = 0; c < renderwidth; c++)
{ {
uint8_t bitmapbyte = readUnsafe(); uint8_t bitmapbyte = readUnsafe();
if (mode & _BV(dbfReverseBlack)) bitmapbyte ^= rowmask; if (mode & (1 << dbfReverseBlack)) bitmapbyte ^= rowmask;
uint8_t maskbyte = rowmask; uint8_t maskbyte = rowmask;
if (mode & _BV(dbfWhiteBlack)) maskbyte = bitmapbyte; if (mode & (1 << dbfWhiteBlack)) maskbyte = bitmapbyte;
if (mode & _BV(dbfBlack)) bitmapbyte = 0; if (mode & (1 << dbfBlack)) bitmapbyte = 0;
uint16_t bitmap = multiplyUInt8(bitmapbyte, yshift); uint16_t bitmap = multiplyUInt8(bitmapbyte, yshift);
if (mode & _BV(dbfMasked)) if (mode & (1 << dbfMasked))
{ {
wait(); wait();
uint8_t tmp = readUnsafe(); uint8_t tmp = readUnsafe();
@ -898,12 +969,12 @@ void FX::drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8
{ {
uint8_t pixels = bitmap; uint8_t pixels = bitmap;
uint8_t display = Arduboy2Base::sBuffer[displayoffset]; uint8_t display = Arduboy2Base::sBuffer[displayoffset];
if ((mode & _BV(dbfInvert)) == 0) pixels ^= display; if ((mode & (1 << dbfInvert)) == 0) pixels ^= display;
pixels &= mask; pixels &= mask;
pixels ^= display; pixels ^= display;
Arduboy2Base::sBuffer[displayoffset] = pixels; Arduboy2Base::sBuffer[displayoffset] = pixels;
} }
if (mode & _BV(dbfExtraRow)) if (mode & (1 << dbfExtraRow))
{ {
uint8_t display = Arduboy2Base::sBuffer[displayoffset + WIDTH]; uint8_t display = Arduboy2Base::sBuffer[displayoffset + WIDTH];
uint8_t pixels = bitmap >> 8; uint8_t pixels = bitmap >> 8;
@ -1318,5 +1389,3 @@ void FX::drawNumber(uint32_t n, int8_t digits) //
while (digits < 0) {++digits; *--str = ' ';} while (digits < 0) {++digits; *--str = ' ';}
drawString(str); drawString(str);
} }

View File

@ -1,6 +1,16 @@
#ifndef ARDUBOYFX_H #ifndef ARDUBOYFX_H
#define ARDUBOYFX_H #define ARDUBOYFX_H
// For uint8_t, uint16_t
#include <stdint.h>
// For size_t
#include <stddef.h>
// For ARDUINO_ARCH_AVR, PORTD, ...
#include <Arduino.h>
// For Arduboy2Base::sBuffer, WIDTH, HEIGHT, CS_PORT ...
#include <Arduboy2.h> #include <Arduboy2.h>
#ifdef CART_CS_RX #ifdef CART_CS_RX
@ -42,15 +52,15 @@ constexpr uint8_t dbfEndFrame = 6; // last bitmap image of a frame
constexpr uint8_t dbfLastFrame = 7; // last bitmap image of the last frame constexpr uint8_t dbfLastFrame = 7; // last bitmap image of the last frame
// drawBitmap modes with same behaviour as Arduboy library drawBitmap modes // drawBitmap modes with same behaviour as Arduboy library drawBitmap modes
constexpr uint8_t dbmBlack = _BV(dbfReverseBlack) | // white pixels in bitmap will be drawn as black pixels on display constexpr uint8_t dbmBlack = (1 << dbfReverseBlack) | // white pixels in bitmap will be drawn as black pixels on display
_BV(dbfBlack) | // black pixels in bitmap will not change pixels on display (1 << dbfBlack) | // black pixels in bitmap will not change pixels on display
_BV(dbfWhiteBlack); // (same as sprites drawErase) (1 << dbfWhiteBlack); // (same as sprites drawErase)
constexpr uint8_t dbmWhite = _BV(dbfWhiteBlack); // white pixels in bitmap will be drawn as white pixels on display constexpr uint8_t dbmWhite = (1 << dbfWhiteBlack); // white pixels in bitmap will be drawn as white pixels on display
// black pixels in bitmap will not change pixels on display // black pixels in bitmap will not change pixels on display
//(same as sprites drawSelfMasked) //(same as sprites drawSelfMasked)
constexpr uint8_t dbmInvert = _BV(dbfInvert); // when a pixel in bitmap has a different color than on display the constexpr uint8_t dbmInvert = (1 << dbfInvert); // when a pixel in bitmap has a different color than on display the
// pixel on display will be drawn as white. In all other cases the // pixel on display will be drawn as white. In all other cases the
// pixel will be drawn as black // pixel will be drawn as black
// additional drawBitmap modes // additional drawBitmap modes
@ -58,14 +68,14 @@ constexpr uint8_t dbmNormal = 0; // White pixels in bitma
constexpr uint8_t dbmOverwrite = 0; // Black pixels in bitmap will be drawn as black pixels on display constexpr uint8_t dbmOverwrite = 0; // Black pixels in bitmap will be drawn as black pixels on display
// (Same as sprites drawOverwrite) // (Same as sprites drawOverwrite)
constexpr uint8_t dbmReverse = _BV(dbfReverseBlack); // White pixels in bitmap will be drawn as black pixels on display constexpr uint8_t dbmReverse = (1 << dbfReverseBlack); // White pixels in bitmap will be drawn as black pixels on display
// Black pixels in bitmap will be drawn as white pixels on display // Black pixels in bitmap will be drawn as white pixels on display
constexpr uint8_t dbmMasked = _BV(dbfMasked); // The bitmap contains a mask that will determine which pixels are constexpr uint8_t dbmMasked = (1 << dbfMasked); // The bitmap contains a mask that will determine which pixels are
// drawn and which pixels remain unchanged on display // drawn and which pixels remain unchanged on display
// (same as sprites drawPlusMask) // (same as sprites drawPlusMask)
constexpr uint8_t dbmEndFrame = _BV(dbfEndFrame); // last bitmap of a frame but more frames constexpr uint8_t dbmEndFrame = (1 << dbfEndFrame); // last bitmap of a frame but more frames
constexpr uint8_t dbmLastFrame = _BV(dbfLastFrame); // last bitmap of a frame and at end of frames constexpr uint8_t dbmLastFrame = (1 << dbfLastFrame); // last bitmap of a frame and at end of frames
// Note above modes may be combined like (dbmMasked | dbmReverse) // Note above modes may be combined like (dbmMasked | dbmReverse)
@ -78,15 +88,15 @@ constexpr uint8_t dcfMasked = 4; // character contains mask data
constexpr uint8_t dcfProportional = 5; // use fonts width table to mimic proportional characters constexpr uint8_t dcfProportional = 5; // use fonts width table to mimic proportional characters
//draw Font character modes //draw Font character modes
constexpr uint8_t dcmBlack = _BV(dcfReverseBlack) | // white pixels in character will be drawn as black pixels on display constexpr uint8_t dcmBlack = (1 << dcfReverseBlack) | // white pixels in character will be drawn as black pixels on display
_BV(dcfBlack) | // black pixels in character will not change pixels on display (1 << dcfBlack) | // black pixels in character will not change pixels on display
_BV(dcfWhiteBlack); // (same as sprites drawErase) (1 << dcfWhiteBlack); // (same as sprites drawErase)
constexpr uint8_t dcmWhite = _BV(dcfWhiteBlack); // white pixels in character will be drawn as white pixels on display constexpr uint8_t dcmWhite = (1 << dcfWhiteBlack); // white pixels in character will be drawn as white pixels on display
// black pixels in character will not change pixels on display // black pixels in character will not change pixels on display
//(same as sprites drawSelfMasked) //(same as sprites drawSelfMasked)
constexpr uint8_t dcmInvert = _BV(dcfInvert); // when a pixel in character has a different color than on display the constexpr uint8_t dcmInvert = (1 << dcfInvert); // when a pixel in character has a different color than on display the
// pixel on display will be drawn as white. In all other cases the // pixel on display will be drawn as white. In all other cases the
// pixel will be drawn as black // pixel will be drawn as black
// additional drawcharacter modes // additional drawcharacter modes
@ -94,12 +104,12 @@ constexpr uint8_t dcmNormal = 0; // White pixels in chara
constexpr uint8_t dcmOverwrite = 0; // Black pixels in character will be drawn as black pixels on display constexpr uint8_t dcmOverwrite = 0; // Black pixels in character will be drawn as black pixels on display
// (Same as sprites drawOverwrite) // (Same as sprites drawOverwrite)
constexpr uint8_t dcmReverse = _BV(dcfReverseBlack); // White pixels in character will be drawn as black pixels on display constexpr uint8_t dcmReverse = (1 << dcfReverseBlack); // White pixels in character will be drawn as black pixels on display
// Black pixels in character will be drawn as white pixels on display // Black pixels in character will be drawn as white pixels on display
constexpr uint8_t dcmMasked = _BV(dcfMasked); // The character contains a mask that will determine which pixels are constexpr uint8_t dcmMasked = (1 << dcfMasked); // The character contains a mask that will determine which pixels are
constexpr uint8_t dcmProportional = _BV(dcfProportional); // draw characters with variable spacing. When this mode is used a constexpr uint8_t dcmProportional = (1 << dcfProportional); // draw characters with variable spacing. When this mode is used a
// 256 byte width table must precede the font data // 256 byte width table must precede the font data
// Note above modes may be combined like (dcmMasked | dcmProportional) // Note above modes may be combined like (dcmMasked | dcmProportional)
@ -157,41 +167,48 @@ struct FrameData
class FX class FX
{ {
public: public:
static inline void enableOLED() __attribute__((always_inline)) // selects OLED display. [[gnu::always_inline]]
static inline void enableOLED() // selects OLED display.
{ {
CS_PORT &= ~(1 << CS_BIT); CS_PORT &= ~(1 << CS_BIT);
}; };
static inline void disableOLED() __attribute__((always_inline)) // deselects OLED display. [[gnu::always_inline]]
static inline void disableOLED() // deselects OLED display.
{ {
CS_PORT |= (1 << CS_BIT); CS_PORT |= (1 << CS_BIT);
}; };
static inline void enable() __attribute__((always_inline)) // selects external flash memory and allows new commands [[gnu::always_inline]]
static inline void enable() // selects external flash memory and allows new commands
{ {
FX_PORT &= ~(1 << FX_BIT); FX_PORT &= ~(1 << FX_BIT);
}; };
static inline void disable() __attribute__((always_inline)) // deselects external flash memory and ends the last command [[gnu::always_inline]]
static inline void disable() // deselects external flash memory and ends the last command
{ {
FX_PORT |= (1 << FX_BIT); FX_PORT |= (1 << FX_BIT);
}; };
static inline void wait() __attribute__((always_inline)) // wait for a pending flash transfer to complete [[gnu::always_inline]]
static inline void wait() // wait for a pending flash transfer to complete
{ {
while ((SPSR & _BV(SPIF)) == 0); while ((SPSR & (1 << SPIF)) == 0);
} }
static uint8_t writeByte(uint8_t data); // write a single byte to flash memory. static uint8_t writeByte(uint8_t data); // write a single byte to flash memory.
static inline void writeByteBeforeWait(uint8_t data) __attribute__((always_inline)) [[gnu::always_inline]]
static inline void writeByteBeforeWait(uint8_t data)
{ {
SPDR = data; SPDR = data;
asm volatile("nop\n"); asm volatile("nop\n");
wait(); wait();
} }
static inline void writeByteAfterWait(uint8_t data) __attribute__((always_inline)) [[gnu::always_inline]]
static inline void writeByteAfterWait(uint8_t data)
{ {
wait(); wait();
SPDR = data; SPDR = data;
@ -211,6 +228,10 @@ class FX
static void begin(uint16_t datapage, uint16_t savepage); // Initializes flash memory. Use when program depends on both data and save data in flash memory static void begin(uint16_t datapage, uint16_t savepage); // Initializes flash memory. Use when program depends on both data and save data in flash memory
/// @brief Reads the JedecID of the attached flash chip.
/// @param id An object into which the ID will be read.
static void readJedecID(JedecID & id);
static void readJedecID(JedecID* id); static void readJedecID(JedecID* id);
static bool detect(); //detect presence of initialized flash memory static bool detect(); //detect presence of initialized flash memory
@ -225,35 +246,84 @@ class FX
static void writeEnable();// Puts flash memory in write mode, required prior to any write command static void writeEnable();// Puts flash memory in write mode, required prior to any write command
[[gnu::noinline, gnu::naked]]
static void seekCommand(uint8_t command, uint24_t address);// Write command and selects flash memory address. Required by any read or write command static void seekCommand(uint8_t command, uint24_t address);// Write command and selects flash memory address. Required by any read or write command
static void seekData(uint24_t address); // selects flashaddress of program data area for reading and starts the first read /// @brief selects flash address of program data for reading and starts the first read
/// @param address The base address of program data memory.
[[gnu::noinline]]
static void seekData(uint24_t address);
/// @brief Seeks an element of an array.
/// @tparam Type The type of the element to be read.
/// @param address The base address of the array.
/// @param index The index of the array element.
template<typename Type>
static void seekArrayElement(uint24_t address, uint8_t index)
{
// Note: By the laws of the language this should never happen.
// This assert exists only as a precaution against e.g. weird compiler extensions.
static_assert(sizeof(Type) > 0, "Cannot use a Type with a size of 0.");
seekData(address + (index * sizeof(Type)));
}
/// @brief Seeks a member of an object that is an element of an array.
/// @tparam Type The type of the elements in the array.
/// @param address The base address of the array.
/// @param index The index of the array element.
/// @param offset The offset of the member of the array element.
/// @note
/// It is intended that the value of `offset` be acquired using the
/// `offsetof` macro from `stddef.h`, as this is the safest way
/// to obtain the offset of an object member.
template<typename Type>
static void seekArrayElementMember(uint24_t address, uint8_t index, size_t offset)
{
// Note: By the laws of the language this should never happen.
// This assert exists only as a precaution against e.g. weird compiler extensions.
static_assert(sizeof(Type) > 0, "Cannot use a Type with a size of 0.");
seekData(address + ((index * sizeof(Type)) + offset));
}
[[gnu::noinline, gnu::naked]]
static void seekDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t elementSize); static void seekDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t elementSize);
static void seekSave(uint24_t address) __attribute__ ((noinline)); // selects flashaddress of program save area for reading and starts the first read [[gnu::noinline]]
static void seekSave(uint24_t address); // selects flashaddress of program save area for reading and starts the first read
static inline uint8_t readUnsafe() __attribute__((always_inline)) // read flash data without performing any checks and starts the next read. [[gnu::always_inline]]
static inline uint8_t readUnsafe() // read flash data without performing any checks and starts the next read.
{ {
uint8_t result = SPDR; uint8_t result = SPDR;
SPDR = 0; SPDR = 0;
return result; return result;
}; };
static inline uint8_t readUnsafeEnd() __attribute__((always_inline)) [[gnu::always_inline]]
static inline uint8_t readUnsafeEnd()
{ {
uint8_t result = SPDR; uint8_t result = SPDR;
disable(); disable();
return result; return result;
}; };
static uint8_t readPendingUInt8() __attribute__ ((noinline)); //read a prefetched byte from the current flash location [[gnu::noinline]]
static uint8_t readPendingUInt8(); //read a prefetched byte from the current flash location
static uint8_t readPendingLastUInt8() __attribute__ ((noinline)); //depreciated use readEnd() instead (see below) /// @brief read the last prefetched byte from the current flash location and ends the read command.
/// This function performs the same action as readEnd()
static uint16_t readPendingUInt16() __attribute__ ((noinline)) __attribute__ ((naked)); //read a partly prefetched 16-bit word from the current flash location [[gnu::noinline]]
static uint8_t readPendingLastUInt8();
static uint16_t readPendingLastUInt16() __attribute__ ((noinline)) __attribute__ ((naked)); //read a partly prefetched 16-bit word from the current flash location [[gnu::noinline, gnu::naked]]
static uint16_t readPendingUInt16(); //read a partly prefetched 16-bit word from the current flash location
[[gnu::noinline, gnu::naked]]
static uint16_t readPendingLastUInt16(); //read a partly prefetched 16-bit word from the current flash location
static uint24_t readPendingUInt24() ; //read a partly prefetched 24-bit word from the current flash location static uint24_t readPendingUInt24() ; //read a partly prefetched 24-bit word from the current flash location
@ -263,19 +333,96 @@ class FX
static uint32_t readPendingLastUInt32(); //read a partly prefetched a 32-bit word from the current flash location static uint32_t readPendingLastUInt32(); //read a partly prefetched a 32-bit word from the current flash location
/// @brief Reads an object from the current flash location.
/// @tparam Type The type of the object to be read.
/// @param object An object into which the target object will be read.
/// @warning
/// `Type` should be:
/// * _[trivially copyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable)_
/// * a _[standard-layout](https://en.cppreference.com/w/cpp/language/data_members#Standard-layout)_ type
/// Attempting to read an object that does not meet these restrictions will result in _undefined behaviour_.
template<typename Type>
static void readObject(Type & object)
{
readBytes(reinterpret_cast<uint8_t *>(&object), sizeof(object));
}
static void readBytes(uint8_t* buffer, size_t length);// read a number of bytes from the current flash location static void readBytes(uint8_t* buffer, size_t length);// read a number of bytes from the current flash location
static void readBytesEnd(uint8_t* buffer, size_t length); // read a number of bytes from the current flash location and ends the read command static void readBytesEnd(uint8_t* buffer, size_t length); // read a number of bytes from the current flash location and ends the read command
static uint8_t readEnd() __attribute__ ((noinline)); //read the last prefetched byte from the current flash location and ends the read command /// @brief read the last prefetched byte from the current flash location and ends the read command
[[gnu::noinline]]
static uint8_t readEnd();
/// @brief Reads an object from the specified address in the game's data section.
/// @tparam Type The type of the object to be read.
/// @param address The address of the object in flash memory.
/// @param object An object into which the target object will be read.
/// @warning
/// `Type` should be:
/// * _[trivially copyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable)_
/// * a _[standard-layout](https://en.cppreference.com/w/cpp/language/data_members#Standard-layout)_ type
/// Attempting to read an object that does not meet these restrictions will result in _undefined behaviour_.
template<typename Type>
static void readDataObject(uint24_t address, Type & object)
{
readDataBytes(address, reinterpret_cast<uint8_t *>(&object), sizeof(object));
}
static void readDataBytes(uint24_t address, uint8_t* buffer, size_t length); static void readDataBytes(uint24_t address, uint8_t* buffer, size_t length);
/// @brief Reads an object from the specified address in the game's save section.
/// @tparam Type The type of the object to be read.
/// @param address The address of the object in flash memory.
/// @param object An object into which the target object will be read.
/// @warning
/// `Type` should be:
/// * _[trivially copyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable)_
/// * a _[standard-layout](https://en.cppreference.com/w/cpp/language/data_members#Standard-layout)_ type
/// Attempting to read an object that does not meet these restrictions will result in _undefined behaviour_.
template<typename Type>
static void readSaveObject(uint24_t address, Type & object)
{
readSaveBytes(address, reinterpret_cast<uint8_t *>(&object), sizeof(object));
}
static void readSaveBytes(uint24_t address, uint8_t* buffer, size_t length); static void readSaveBytes(uint24_t address, uint8_t* buffer, size_t length);
static uint8_t loadGameState(uint8_t* gameState, size_t size) __attribute__ ((noinline)); //loads GameState from program exclusive 4K save data block. /// @brief Loads a saved game state object from an exclusive 4KB save data block.
/// @tparam Type The type of the object to be loaded.
/// @param object The object into which the saved state will be loaded.
/// @warning
/// `Type` should be:
/// * _[trivially copyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable)_
/// * a _[standard-layout](https://en.cppreference.com/w/cpp/language/data_members#Standard-layout)_ type
/// Attempting to read an object that does not meet these restrictions will result in _undefined behaviour_.
template<typename Type>
static uint8_t loadGameState(Type & object)
{
return loadGameState((uint8_t*)(&object), sizeof(object));
}
static void saveGameState(uint8_t* gameState, size_t size) __attribute__ ((noinline)); // Saves GameState in RAM to programes exclusive 4K save data block. [[gnu::noinline]]
static uint8_t loadGameState(uint8_t* gameState, size_t size); //loads GameState from program exclusive 4K save data block.
/// @brief Saves a game state object into an exclusive 4KB save data block.
/// @tparam Type The type of the object to be saved.
/// @param object The object to be saved.
/// @warning
/// `Type` should be:
/// * _[trivially copyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable)_
/// * a _[standard-layout](https://en.cppreference.com/w/cpp/language/data_members#Standard-layout)_ type
/// Attempting to read an object that does not meet these restrictions will result in _undefined behaviour_.
template<typename Type>
static void saveGameState(const Type & object)
{
saveGameState(reinterpret_cast<const uint8_t *>(&object), sizeof(object));
}
[[gnu::noinline]]
static void saveGameState(const uint8_t* gameState, size_t size); // Saves GameState in RAM to programes exclusive 4K save data block.
static void eraseSaveBlock(uint16_t page); // erases 4K flash block static void eraseSaveBlock(uint16_t page); // erases 4K flash block
@ -283,13 +430,16 @@ class FX
static void waitWhileBusy(); // wait for outstanding erase or write to finish static void waitWhileBusy(); // wait for outstanding erase or write to finish
static void drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8_t mode) __attribute__((noinline)); [[gnu::noinline]]
static void drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8_t mode);
static void setFrame(uint24_t frame, uint8_t repeat)__attribute__ ((noinline)); [[gnu::noinline]]
static void setFrame(uint24_t frame, uint8_t repeat);
static uint8_t drawFrame(); static uint8_t drawFrame();
static uint24_t drawFrame(uint24_t address) __attribute__((noinline)); // draw a list of bitmap images located at address [[gnu::noinline]]
static uint24_t drawFrame(uint24_t address); // draw a list of bitmap images located at address
static void readDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t elementSize, uint8_t* buffer, size_t length); static void readDataArray(uint24_t address, uint8_t index, uint8_t offset, uint8_t elementSize, uint8_t* buffer, size_t length);
@ -350,7 +500,8 @@ class FX
/* general optimized functions */ /* general optimized functions */
static inline uint16_t multiplyUInt8 (uint8_t a, uint8_t b) __attribute__((always_inline)) [[gnu::always_inline]]
static inline uint16_t multiplyUInt8 (uint8_t a, uint8_t b)
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
uint16_t result; uint16_t result;
@ -369,7 +520,8 @@ class FX
#endif #endif
} }
static inline uint8_t bitShiftLeftUInt8(uint8_t bit) __attribute__((always_inline)) //fast (1 << (bit & 7)) [[gnu::always_inline]]
static inline uint8_t bitShiftLeftUInt8(uint8_t bit) //fast (1 << (bit & 7))
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
uint8_t result; uint8_t result;
@ -391,7 +543,8 @@ class FX
#endif #endif
} }
static inline uint8_t bitShiftRightUInt8(uint8_t bit) __attribute__((always_inline)) //fast (0x80 >> (bit & 7)) [[gnu::always_inline]]
static inline uint8_t bitShiftRightUInt8(uint8_t bit) //fast (0x80 >> (bit & 7))
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
uint8_t result; uint8_t result;
@ -413,7 +566,8 @@ class FX
#endif #endif
} }
static inline uint8_t bitShiftLeftMaskUInt8(uint8_t bit) __attribute__((always_inline)) //fast (0xFF << (bit & 7) & 0xFF) [[gnu::always_inline]]
static inline uint8_t bitShiftLeftMaskUInt8(uint8_t bit) //fast (0xFF << (bit & 7) & 0xFF)
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
uint8_t result; uint8_t result;
@ -436,7 +590,8 @@ class FX
#endif #endif
} }
static inline uint8_t bitShiftRightMaskUInt8(uint8_t bit) __attribute__((always_inline)) //fast (0xFF >> (bit & 7)) [[gnu::always_inline]]
static inline uint8_t bitShiftRightMaskUInt8(uint8_t bit) //fast (0xFF >> (bit & 7))
{ {
#ifdef ARDUINO_ARCH_AVR #ifdef ARDUINO_ARCH_AVR
uint8_t result; uint8_t result;
@ -459,6 +614,46 @@ class FX
#endif #endif
} }
[[gnu::always_inline]]
static inline int16_t fastDiv8(int16_t i)
{
#ifdef ARDUINO_ARCH_AVR
asm volatile(
"asr %B[i] \n"
"ror %A[i] \n"
"asr %B[i] \n"
"ror %A[i] \n"
"asr %B[i] \n"
"ror %A[i] \n"
: [i] "+r" (i)
: "0" (i)
);
return i;
#else
return i >> 3;
#endif
};
[[gnu::always_inline]]
static inline uint16_t fastDiv8(uint16_t i)
{
#ifdef ARDUINO_ARCH_AVR
asm volatile(
"lsr %B[i] \n"
"ror %A[i] \n"
"lsr %B[i] \n"
"ror %A[i] \n"
"lsr %B[i] \n"
"ror %A[i] \n"
: [i] "+r" (i)
: "0" (i)
);
return i;
#else
return i >> 3;
#endif
};
static uint16_t programDataPage; // program read only data area in flash memory static uint16_t programDataPage; // program read only data area in flash memory
static uint16_t programSavePage; // program read and write data area in flash memory static uint16_t programSavePage; // program read and write data area in flash memory
static Font font; static Font font;