From fed21b40cb525286d7021b41c01f299f905cc6f5 Mon Sep 17 00:00:00 2001 From: "Mr.Blinky" Date: Tue, 28 Feb 2023 16:46:51 +0100 Subject: [PATCH] update ArduboyFX library Update Arduboy FX library to v1.0.8 add manufacturer strings to boards.txt --- board-package-source/boards.txt | 7 + .../examples/loadGameState/loadGameState.ino | 27 +- .../libraries/ArduboyFX/library.properties | 2 +- .../libraries/ArduboyFX/src/ArduboyFX.cpp | 199 ++++++++---- .../libraries/ArduboyFX/src/ArduboyFX.h | 283 +++++++++++++++--- 5 files changed, 393 insertions(+), 125 deletions(-) diff --git a/board-package-source/boards.txt b/board-package-source/boards.txt index 507ba09..4c95a2f 100644 --- a/board-package-source/boards.txt +++ b/board-package-source/boards.txt @@ -326,6 +326,7 @@ arduboy-fx.build.board=AVR_ARDUBOY arduboy-fx.build.vid=0x2341 arduboy-fx.build.pid=0x8036 arduboy-fx.build.variant=arduboy +arduboy-fx.build.usb_manufacturer="Arduboy Inc" arduboy-fx.build.usb_product="Arduboy" arduboy-fx.build.board=AVR_ARDUBOY 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.pid=0x8036 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.board=AVR_ARDUBOY arduboy-fx-devkit.build.core=arduino:arduino @@ -488,6 +490,7 @@ arduboy.build.board=AVR_ARDUBOY arduboy.build.vid=0x2341 arduboy.build.pid=0x8036 arduboy.build.variant=arduboy +arduboy.build.usb_manufacturer="Arduboy Inc" arduboy.build.usb_product="Arduboy" arduboy.build.board=AVR_ARDUBOY arduboy.build.core=arduino:arduino @@ -574,6 +577,7 @@ arduboy-devkit.build.board=AVR_ARDUBOY arduboy-devkit.build.vid=0x2341 arduboy-devkit.build.pid=0x8036 arduboy-devkit.build.variant=arduboy-devkit +arduboy-devkit.build.usb_manufacturer="Arduboy Inc" arduboy-devkit.build.usb_product="ABDevKit" arduboy-devkit.build.board=AVR_ARDUBOY_DEVKIT 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.vid=0x2341 8bitcadexl.build.pid=0x8036 +8bitcadexl.build.usb_manufacturer="8bitCADE" 8bitcadexl.build.usb_product="8bitCADE" 8bitcadexl.build.core=arduino:arduino 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.vid=0x2341 8bitcadexlup.build.pid=0x8036 +8bitcadexlup.build.usb_manufacturer="8bitCADE" 8bitcadexlup.build.usb_product="8bitCADE" 8bitcadexlup.build.core=arduino:arduino 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.vid=0x2341 microcade.build.pid=0x8036 +microcade.build.usb_manufacturer="microcade" microcade.build.usb_product="microcade" microcade.build.core=arduino:arduino microcade.build.flash_cs=-DCART_CS_RX diff --git a/board-package-source/libraries/ArduboyFX/examples/loadGameState/loadGameState.ino b/board-package-source/libraries/ArduboyFX/examples/loadGameState/loadGameState.ino index e2aa907..d13dcaf 100644 --- a/board-package-source/libraries/ArduboyFX/examples/loadGameState/loadGameState.ino +++ b/board-package-source/libraries/ArduboyFX/examples/loadGameState/loadGameState.ino @@ -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 @@ -22,9 +22,9 @@ * * 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 * ------------- @@ -32,15 +32,12 @@ * Loads previously saved game data. It returns 0 if there was no data to load * or 1 if the data was successfully loaded. * - * gameState is a pointer to an uint8_t array or to a structure in RAM containing - * your game data. When using a structure for the game data, the structure must be - * cast to an uint8_t array by putting (uint8_t*)& in front of the structures name. + * gameState is an object structure in RAM to where the game data will be loaded. + * If the size of the object in ram differes from the size of previously saved + * data, no data will be loaded. * - * size is the size of the uint8_t array or structure in RAM and is usually - * determined by using the sizeof() operator. - * - * When there is no game data loaded (result 0), the contents of the gameState - * structure remains unchanged. + * When no game data loaded (result 0), the contents of the gameState structure + * remains unchanged. * * saveGameState * ------------- @@ -54,7 +51,7 @@ * 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. * - * 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 * ------------------------------------------------ @@ -135,8 +132,8 @@ void setup() { 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. - if (!FX::loadGameState((uint8_t*) &gameState, sizeof(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. + 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. { // 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 @@ -149,7 +146,7 @@ void setup() { 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.setCursor(0,32 - 8); diff --git a/board-package-source/libraries/ArduboyFX/library.properties b/board-package-source/libraries/ArduboyFX/library.properties index aa30732..40f2f9e 100644 --- a/board-package-source/libraries/ArduboyFX/library.properties +++ b/board-package-source/libraries/ArduboyFX/library.properties @@ -1,5 +1,5 @@ name=ArduboyFX -version=1.0.7 +version=1.0.8 author=Mr.Blinky maintainer=mstr.blinky@gmail.com sentence=The Arduboy FX library. diff --git a/board-package-source/libraries/ArduboyFX/src/ArduboyFX.cpp b/board-package-source/libraries/ArduboyFX/src/ArduboyFX.cpp index b1ac43a..29d0966 100644 --- a/board-package-source/libraries/ArduboyFX/src/ArduboyFX.cpp +++ b/board-package-source/libraries/ArduboyFX/src/ArduboyFX.cpp @@ -11,8 +11,15 @@ FrameControl FX::frameControl; uint8_t FX::writeByte(uint8_t data) { - writeByteBeforeWait(data); - return SPDR; + SPDR = data; + 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(); #ifdef ARDUINO_ARCH_AVR - const uint8_t* vector = FX_DATA_VECTOR_KEY_POINTER; - asm volatile( + const uint8_t* vector = (const uint8_t*)FX_DATA_VECTOR_KEY_POINTER; + asm volatile + ( "lpm r18, z+ \n" "lpm r19, z+ \n" "lpm r21, z+ \n" @@ -68,8 +76,9 @@ void FX::begin(uint16_t developmentDataPage, uint16_t developmentSavePage) { disableOLED(); #ifdef ARDUINO_ARCH_AVR - const uint8_t* vector = FX_DATA_VECTOR_KEY_POINTER; - asm volatile( + const uint8_t* vector = (const uint8_t*)FX_DATA_VECTOR_KEY_POINTER; + asm volatile + ( "lpm r18, z+ \n" "lpm r19, z+ \n" "lpm r21, z+ \n" @@ -118,6 +127,16 @@ void FX::begin(uint16_t developmentDataPage, uint16_t developmentSavePage) 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) { enable(); @@ -180,14 +199,15 @@ void FX::seekCommand(uint8_t command, uint24_t address) enable(); #ifdef ARDUINO_ARCH_AVR register uint8_t cmd asm("r24") = command; //assembly optimizer for AVR platform ~saves 12 bytes - asm volatile( + asm volatile + ( "call %x2 \n" "mov r24, %C[addr] \n" "call %x2 \n" "mov r24, %B[addr] \n" "call %x2 \n" "mov r24, %A[addr] \n" - "call %x2 \n" + "jmp %x2 \n" : [cmd] "+&r" (cmd) : [addr] "r" (address), [write] "i" (writeByte) @@ -204,20 +224,25 @@ void FX::seekCommand(uint8_t command, uint24_t address) void FX::seekData(uint24_t address) { + uint24_t abs_address = address; #ifdef ARDUINO_ARCH_AVR - asm volatile( // assembly optimizer for AVR platform - "lds r0, %[page]+0 \n" - "add %B[addr], r0 \n" - "lds r0, %[page]+1 \n" - "adc %C[addr], r0 \n" - :[addr] "+&r" (address) - :[page] "" (&programDataPage) + asm volatile + ( + "mov %A[abs], %A[addr] \n" + "lds %B[abs], %[page]+0 \n" + "add %B[abs], %B[addr] \n" + "lds %C[abs], %[page]+1 \n" + "adc %C[abs], %C[addr] \n" + : + [abs] "=r" (abs_address) + :[page] "" (&programDataPage), + [addr] "r" (address) : ); #else // C++ version for non AVR platforms - address += (uint24_t)programDataPage << 8; + abs_address = address + (uint24_t)programDataPage << 8; #endif - seekCommand(SFC_READ, address); + seekCommand(SFC_READ, abs_address); 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) { #ifdef ARDUINO_ARCH_AVR - asm volatile ( + asm volatile + ( " mul %[index], %[size] \n" " brne .+2 \n" //treat size 0 as size 256 " 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 %C[address], r21 \n" " clr r1 \n" - : [address] "+&r" (address) + " jmp %x4 \n" //seekData + : : [index] "r" (index), [offset] "r" (offset), - [size] "r" (elementSize) + [size] "r" (elementSize), + [address] "r" (address), + "" (seekData) : "r21" ); #else address += size ? index * size + offset : index * 256 + offset; - #endif seekData(address); + #endif } void FX::seekSave(uint24_t address) { #ifdef ARDUINO_ARCH_AVR - asm volatile( // assembly optimizer for AVR platform - "lds r0, %[page]+0 \n" - "add %B[addr], r0 \n" - "lds r0, %[page]+1 \n" - "adc %C[addr], r0 \n" - :[addr] "+&r" (address) - :[page] "" (&programSavePage) + uint24_t abs_address = address; + asm volatile + ( + "mov %A[abs], %A[addr] \n" + "lds %B[abs], %[page]+0 \n" + "add %B[abs], %B[addr] \n" + "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 - address += (uint24_t)programSavePage << 8; + abs_address = address + (uint24_t)programSavePage << 8; #endif - seekCommand(SFC_READ, address); + seekCommand(SFC_READ, abs_address); SPDR = 0; } uint8_t FX::readPendingUInt8() { - #ifdef ARDUINO_ARCH_AVR - asm volatile("ArduboyFX_cpp_readPendingUInt8:\n"); // create label for calls in FX::readPendingUInt16 - #endif wait(); uint8_t result = SPDR; SPDR = 0; @@ -282,7 +314,8 @@ uint8_t FX::readPendingUInt8() 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 uint16_t result asm("r24"); // we want result to be assigned to r24,r25 asm volatile - ( "ArduboyFX_cpp_readPendingUInt16: \n" - "call ArduboyFX_cpp_readPendingUInt8 \n" - "mov %B[val], r24 \n" - "jmp ArduboyFX_cpp_readPendingUInt8 \n" + ( + "call %x1 \n" + "mov %B[val], r24 \n" + "jmp %x1 \n" : [val] "=&r" (result) : "" (readPendingUInt8) : @@ -311,7 +344,7 @@ uint16_t FX::readPendingLastUInt16() #ifdef ARDUINO_ARCH_AVR // Assembly implementation for AVR platform uint16_t result asm("r24"); // we want result to be assigned to r24,r25 asm volatile - ( "ArduboyFX_cpp_readPendingLastUInt16: \n" + ( "call %x1 \n" "mov %B[val], r24 \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 asm volatile ( - "call ArduboyFX_cpp_readPendingUInt16 \n" - "mov %B[val], r24 \n" - "call ArduboyFX_cpp_readPendingUInt8 \n" - "mov %A[val], r24 \n" - "mov %C[val], r25 \n" + "call %x1 \n" + "mov %B[val], r24 \n" + "call %x2 \n" + "mov %A[val], r24 \n" + "mov %C[val], r25 \n" : [val] "=&r" (result) : "" (readPendingUInt16), "" (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 asm volatile ( - "call ArduboyFX_cpp_readPendingUInt16 \n" - "movw %C[val], r24 \n" - "call ArduboyFX_cpp_readPendingUInt16 \n" + "call %x1 \n" + "movw %C[val], r24 \n" + "call %x1 \n" : [val] "=&r" (result) : "" (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 asm volatile ( - "call ArduboyFX_cpp_readPendingUInt16 \n" - "movw %C[val], r24 \n" - "call ArduboyFX_cpp_readPendingLastUInt16 \n" + "call %x1 \n" + "movw %C[val], r24 \n" + "call %x2 \n" : [val] "=&r" (result) - : "" (readPendingUInt16) + : "" (readPendingUInt16), + "" (readPendingLastUInt16) : ); return result; @@ -415,16 +449,52 @@ uint32_t FX::readPendingLastUInt32() 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++) { buffer[i] = readPendingUInt8(); } + #endif } 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) buffer[i] = readPendingUInt8(); @@ -434,6 +504,7 @@ void FX::readBytesEnd(uint8_t* buffer, size_t length) break; } } + #endif } @@ -517,7 +588,7 @@ uint8_t FX::loadGameState(uint8_t* gameState, size_t size) // ~54 bytes 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 register size_t sz asm("r18") = size; #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 if (height - skiptop <= HEIGHT) renderheight = height - skiptop; else renderheight = HEIGHT + (-y & 7); - skiptop >>= 3;//pixels to displayrows + skiptop = fastDiv8(skiptop); // pixels to displayrows } 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; 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) { 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); address += width; - mode &= ~(_BV(dbfExtraRow)); - if (yshift != 1 && displayrow < (HEIGHT / 8 - 1)) mode |= _BV(dbfExtraRow); + mode &= ~((1 << dbfExtraRow)); + if (yshift != 1 && displayrow < (HEIGHT / 8 - 1)) mode |= (1 << dbfExtraRow); uint8_t rowmask = 0xFF; if (renderheight < 8) rowmask = lastmask; wait(); for (uint8_t c = 0; c < renderwidth; c++) { uint8_t bitmapbyte = readUnsafe(); - if (mode & _BV(dbfReverseBlack)) bitmapbyte ^= rowmask; + if (mode & (1 << dbfReverseBlack)) bitmapbyte ^= rowmask; uint8_t maskbyte = rowmask; - if (mode & _BV(dbfWhiteBlack)) maskbyte = bitmapbyte; - if (mode & _BV(dbfBlack)) bitmapbyte = 0; + if (mode & (1 << dbfWhiteBlack)) maskbyte = bitmapbyte; + if (mode & (1 << dbfBlack)) bitmapbyte = 0; uint16_t bitmap = multiplyUInt8(bitmapbyte, yshift); - if (mode & _BV(dbfMasked)) + if (mode & (1 << dbfMasked)) { wait(); 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 display = Arduboy2Base::sBuffer[displayoffset]; - if ((mode & _BV(dbfInvert)) == 0) pixels ^= display; + if ((mode & (1 << dbfInvert)) == 0) pixels ^= display; pixels &= mask; pixels ^= display; Arduboy2Base::sBuffer[displayoffset] = pixels; } - if (mode & _BV(dbfExtraRow)) + if (mode & (1 << dbfExtraRow)) { uint8_t display = Arduboy2Base::sBuffer[displayoffset + WIDTH]; uint8_t pixels = bitmap >> 8; @@ -1318,5 +1389,3 @@ void FX::drawNumber(uint32_t n, int8_t digits) // while (digits < 0) {++digits; *--str = ' ';} drawString(str); } - - diff --git a/board-package-source/libraries/ArduboyFX/src/ArduboyFX.h b/board-package-source/libraries/ArduboyFX/src/ArduboyFX.h index ed035eb..054f71b 100644 --- a/board-package-source/libraries/ArduboyFX/src/ArduboyFX.h +++ b/board-package-source/libraries/ArduboyFX/src/ArduboyFX.h @@ -1,6 +1,16 @@ #ifndef ARDUBOYFX_H #define ARDUBOYFX_H +// For uint8_t, uint16_t +#include + +// For size_t +#include + +// For ARDUINO_ARCH_AVR, PORTD, ... +#include + +// For Arduboy2Base::sBuffer, WIDTH, HEIGHT, CS_PORT ... #include #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 // 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 - _BV(dbfBlack) | // black pixels in bitmap will not change pixels on display - _BV(dbfWhiteBlack); // (same as sprites drawErase) +constexpr uint8_t dbmBlack = (1 << dbfReverseBlack) | // white pixels in bitmap will be drawn as black pixels on display + (1 << dbfBlack) | // black pixels in bitmap will not change pixels on display + (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 //(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 will be drawn as black // 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 // (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 -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 // (same as sprites drawPlusMask) -constexpr uint8_t dbmEndFrame = _BV(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 dbmEndFrame = (1 << dbfEndFrame); // last bitmap of a frame but more 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) @@ -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 //draw Font character modes -constexpr uint8_t dcmBlack = _BV(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 - _BV(dcfWhiteBlack); // (same as sprites drawErase) +constexpr uint8_t dcmBlack = (1 << dcfReverseBlack) | // white pixels in character will be drawn as black pixels on display + (1 << dcfBlack) | // black pixels in character will not change pixels on display + (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 //(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 will be drawn as black // 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 // (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 -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 // Note above modes may be combined like (dcmMasked | dcmProportional) @@ -157,41 +167,48 @@ struct FrameData class FX { 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); }; - 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); }; - 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); }; - 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); }; - 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 inline void writeByteBeforeWait(uint8_t data) __attribute__((always_inline)) + [[gnu::always_inline]] + static inline void writeByteBeforeWait(uint8_t data) { SPDR = data; asm volatile("nop\n"); wait(); } - static inline void writeByteAfterWait(uint8_t data) __attribute__((always_inline)) + [[gnu::always_inline]] + static inline void writeByteAfterWait(uint8_t data) { wait(); 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 + /// @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 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 + [[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 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 + 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 + 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 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; SPDR = 0; return result; }; - static inline uint8_t readUnsafeEnd() __attribute__((always_inline)) + [[gnu::always_inline]] + static inline uint8_t readUnsafeEnd() { uint8_t result = SPDR; disable(); 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 @@ -263,19 +333,96 @@ class FX 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 + static void readObject(Type & object) + { + readBytes(reinterpret_cast(&object), sizeof(object)); + } + 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 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 + static void readDataObject(uint24_t address, Type & object) + { + readDataBytes(address, reinterpret_cast(&object), sizeof(object)); + } 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 + static void readSaveObject(uint24_t address, Type & object) + { + readSaveBytes(address, reinterpret_cast(&object), sizeof(object)); + } + 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 + 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 + static void saveGameState(const Type & object) + { + saveGameState(reinterpret_cast(&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 @@ -283,13 +430,16 @@ class FX 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 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); @@ -350,7 +500,8 @@ class FX /* 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 uint16_t result; @@ -369,7 +520,8 @@ class FX #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 uint8_t result; @@ -391,7 +543,8 @@ class FX #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 uint8_t result; @@ -413,7 +566,8 @@ class FX #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 uint8_t result; @@ -436,7 +590,8 @@ class FX #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 uint8_t result; @@ -459,6 +614,46 @@ class FX #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 programSavePage; // program read and write data area in flash memory static Font font;