#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 #if defined (CART_CS_RX) #define FX_PORT PORTD #define FX_BIT PORTD2 #elif defined (CART_CS_HWB) #define FX_PORT PORTE #define FX_BIT PORTE2 #else #define FX_PORT PORTD #define FX_BIT PORTD1 #endif // progam data and save data pages(set by PC manager tool) constexpr uint16_t FX_VECTOR_KEY_VALUE = 0x9518; /* RETI instruction used as magic key */ constexpr uint16_t FX_DATA_VECTOR_KEY_POINTER = 0x0014; /* reserved interrupt vector 5 area */ constexpr uint16_t FX_DATA_VECTOR_PAGE_POINTER = 0x0016; constexpr uint16_t FX_SAVE_VECTOR_KEY_POINTER = 0x0018; /* reserved interrupt vector 6 area */ constexpr uint16_t FX_SAVE_VECTOR_PAGE_POINTER = 0x001A; // Serial Flash Commands constexpr uint8_t SFC_JEDEC_ID = 0x9F; constexpr uint8_t SFC_READSTATUS1 = 0x05; constexpr uint8_t SFC_READSTATUS2 = 0x35; constexpr uint8_t SFC_READSTATUS3 = 0x15; constexpr uint8_t SFC_READ = 0x03; constexpr uint8_t SFC_WRITE_ENABLE = 0x06; constexpr uint8_t SFC_WRITE = 0x02; constexpr uint8_t SFC_ERASE = 0x20; constexpr uint8_t SFC_RELEASE_POWERDOWN = 0xAB; constexpr uint8_t SFC_POWERDOWN = 0xB9; // drawbitmap bit flags (used by modes below and internally) constexpr uint8_t dbfWhiteBlack = 0; // bitmap is used as mask constexpr uint8_t dbfInvert = 1; // bitmap is exclusive or-ed with display constexpr uint8_t dbfBlack = 2; // bitmap will be blackened constexpr uint8_t dbfReverseBlack = 3; // reverses bitmap data constexpr uint8_t dbfMasked = 4; // bitmap contains mask data constexpr uint8_t dbfFlip = 5; // mirror bitmap constexpr uint8_t dbfExtraRow = 7; // ignored (internal use) 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 = (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 = (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 = (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 constexpr uint8_t dbmFlip = (1 << dbfFlip); // 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 constexpr uint8_t dbmNormal = 0; // White pixels in bitmap will be drawn as white pixels on display 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 = (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 = (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 = (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) // drawChar bit flags (used by modes below) constexpr uint8_t dcfWhiteBlack = 0; // character is used as mask constexpr uint8_t dcfInvert = 1; // character is exclusive or-ed with display constexpr uint8_t dcfBlack = 2; // character will be blackened constexpr uint8_t dcfReverseBlack = 3; // reverses character data 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 = (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 = (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 = (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 constexpr uint8_t dcmNormal = 0; // White pixels in character will be drawn as white pixels on display 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 = (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 = (1 << dcfMasked); // The character contains a mask that will determine which pixels are 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) using uint24_t = __uint24; struct JedecID { uint8_t manufacturer; uint8_t device; uint8_t size; }; struct FXAddress { uint16_t page; uint8_t offset; }; struct Font { uint24_t address; uint8_t mode; uint8_t width; uint8_t height; }; struct Cursor { int16_t x; int16_t y; int16_t left; int16_t wrap; }; struct FrameControl { uint24_t start; uint24_t current; uint8_t repeat; uint8_t count; }; struct FrameData { int16_t x; int16_t y; uint24_t bmp; uint8_t frame; uint8_t mode; }; class FX { public: [[gnu::always_inline]] static inline void enableOLED() // selects OLED display. { CS_PORT &= ~(1 << CS_BIT); }; [[gnu::always_inline]] static inline void disableOLED() // deselects OLED display. { CS_PORT |= (1 << CS_BIT); }; [[gnu::always_inline]] static inline void enable() // selects external flash memory and allows new commands { FX_PORT &= ~(1 << FX_BIT); }; [[gnu::always_inline]] static inline void disable() // deselects external flash memory and ends the last command { FX_PORT |= (1 << FX_BIT); }; [[gnu::always_inline]] static inline void wait() // wait for a pending flash transfer to complete { while ((SPSR & (1 << SPIF)) == 0); } static uint8_t writeByte(uint8_t data); // write a single byte to flash memory. [[gnu::always_inline]] static inline void writeByteBeforeWait(uint8_t data) { SPDR = data; asm volatile("nop\n"); wait(); } [[gnu::always_inline]] static inline void writeByteAfterWait(uint8_t data) { wait(); SPDR = data; } static uint8_t readByte(); // read a single byte from flash memory static void displayPrefetch(uint24_t address, uint8_t* target, uint16_t len, bool clear); static void display(); // display screen buffer static void display(bool clear); // display screen buffer with clear static void begin(); // Initializes flash memory. Use only when program does not require data and save areas in flash memory static void begin(uint16_t programDataPage); // Initializes flash memory. Use when program depends on 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 bool detect(); //detect presence of initialized flash memory static void noFXReboot(); // flash RGB LED red and wait for DOWN button to exit to bootloader when no initialized external flash memory is present static void writeCommand(uint8_t command); // write a single byte flash command static void wakeUp(); // Wake up flash memory from power down mode static void sleep(); // Put flash memory in power down mode for low power 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 /// @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); [[gnu::noinline]] static void seekSave(uint24_t address); // selects flashaddress of program save area for reading and starts the first 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; }; [[gnu::always_inline]] static inline uint8_t readUnsafeEnd() { uint8_t result = SPDR; disable(); return result; }; [[gnu::noinline]] static uint8_t readPendingUInt8(); //read a prefetched byte from the current flash location /// @brief read the last prefetched byte from the current flash location and ends the read command. /// This function performs the same action as readEnd() [[gnu::noinline]] static uint8_t readPendingLastUInt8(); [[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 readPendingLastUInt24() ; //read a partly prefetched 24-bit word from the current flash location static uint32_t readPendingUInt32(); //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 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 /// @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); /// @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)); } [[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 static void writeSavePage(uint16_t page, uint8_t* buffer); static void waitWhileBusy(); // wait for outstanding erase or write to finish [[gnu::noinline]] static void drawBitmap(int16_t x, int16_t y, uint24_t address, uint8_t frame, uint8_t mode); [[gnu::noinline]] static void setFrame(uint24_t frame, uint8_t repeat); static uint8_t drawFrame(); [[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 uint8_t readIndexedUInt8(uint24_t address, uint8_t index); static uint16_t readIndexedUInt16(uint24_t address, uint8_t index); static uint24_t readIndexedUInt24(uint24_t address, uint8_t index); static uint32_t readIndexedUInt32(uint24_t address, uint8_t index); /* Draw character functions */ static void setFont(uint24_t address, uint8_t mode); static void setFontMode(uint8_t mode); static void setCursor(int16_t x, int16_t y); static void setCursorX(int16_t x); static void setCursorY(int16_t y); static void setCursorRange(int16_t left, int16_t wrap); static void setCursorLeft(int16_t x); static void setCursorWrap(int16_t y); static void drawChar(uint8_t c); static void drawString(const uint8_t* buffer); static void drawString(const char* str); static void drawString(uint24_t address); //static void drawNumber(int8_t n, int8_t digits = 0); // draw a signed 8-bit number // //static void drawNumber(unt8_t n, int8_t digits = 0); // draw a unsigned 8-bit number // static void drawNumber(int16_t n, int8_t digits = 0); // draw a signed 16-bit number static void drawNumber(uint16_t n, int8_t digits = 0); // draw a unsigned 16-bit number static void drawNumber(int32_t n, int8_t digits = 0); // draw signed 32-bit number with a fixed number of digits // digits == 0: No leading characters are added, all digits are drawn // digits > 0: Leading zeros are added if the number has less than 'digit' digits // digits < 0: Leading spaces are added if the number has less than 'digit' digits // Note: Only 'digits' number of digits are drawn regardless if the number has more digits // The sign character is not counted as a digit. static void drawNumber(uint32_t n, int8_t digits = 0); // draw unsigned 32-bit number with a fixed number of digits // digits == 0: No leading characters are added, all digits are drawn // digits > 0: Leading zeros are added if the number has less than 'digit' digits // digits < 0: Leading spaces are added if the number has less than 'digit' digits // Note: Only 'digits' number of digits are drawn regardless if the number has more digits /* general optimized functions */ [[gnu::always_inline]] static inline uint16_t multiplyUInt8 (uint8_t a, uint8_t b) { #ifdef ARDUINO_ARCH_AVR uint16_t result; asm volatile( "mul %[a], %[b] \n" "movw %A[result], r0 \n" "clr r1 \n" : [result] "=&r" (result) : [a] "r" (a), [b] "r" (b) : ); return result; #else return (a * b); #endif } [[gnu::always_inline]] static inline uint8_t bitShiftLeftUInt8(uint8_t bit) //fast (1 << (bit & 7)) { #ifdef ARDUINO_ARCH_AVR uint8_t result; asm volatile( "ldi %[result], 1 \n" // 0 = 000 => 0000 0001 "sbrc %[bit], 1 \n" // 1 = 001 => 0000 0010 "ldi %[result], 4 \n" // 2 = 010 => 0000 0100 "sbrc %[bit], 0 \n" // 3 = 011 => 0000 1000 "lsl %[result] \n" "sbrc %[bit], 2 \n" // 4 = 100 => 0001 0000 "swap %[result] \n" // 5 = 101 => 0010 0000 :[result] "=&d" (result) // 6 = 110 => 0100 0000 :[bit] "r" (bit) // 7 = 111 => 1000 0000 : ); return result; #else return 1 << (bit & 7); #endif } [[gnu::always_inline]] static inline uint8_t bitShiftRightUInt8(uint8_t bit) //fast (0x80 >> (bit & 7)) { #ifdef ARDUINO_ARCH_AVR uint8_t result; asm volatile( "ldi %[result], 1 \n" // 0 = 000 => 1000 0000 "sbrs %[bit], 1 \n" // 1 = 001 => 0100 0000 "ldi %[result], 4 \n" // 2 = 010 => 0010 0000 "sbrs %[bit], 0 \n" // 3 = 011 => 0001 0000 "lsl %[result] \n" "sbrs %[bit], 2 \n" // 4 = 100 => 0000 1000 "swap %[result] \n" // 5 = 101 => 0000 0100 :[result] "=&d" (result) // 6 = 110 => 0000 0010 :[bit] "r" (bit) // 7 = 111 => 0000 0001 : ); return result; #else return 0x80 >> (bit & 7); #endif } [[gnu::always_inline]] static inline uint8_t bitShiftLeftMaskUInt8(uint8_t bit) //fast (0xFF << (bit & 7) & 0xFF) { #ifdef ARDUINO_ARCH_AVR uint8_t result; asm volatile( "ldi %[result], 1 \n" // 0 = 000 => 1111 1111 = -1 "sbrc %[bit], 1 \n" // 1 = 001 => 1111 1110 = -2 "ldi %[result], 4 \n" // 2 = 010 => 1111 1100 = -4 "sbrc %[bit], 0 \n" // 3 = 011 => 1111 1000 = -8 "lsl %[result] \n" "sbrc %[bit], 2 \n" // 4 = 100 => 1111 0000 = -16 "swap %[result] \n" // 5 = 101 => 1110 0000 = -32 "neg %[result] \n" // 6 = 110 => 1100 0000 = -64 :[result] "=&d" (result) // 7 = 111 => 1000 0000 = -128 :[bit] "r" (bit) : ); return result; #else return (0xFF << (bit & 7)) & 0xFF; #endif } [[gnu::always_inline]] static inline uint8_t bitShiftRightMaskUInt8(uint8_t bit) //fast (0xFF >> (bit & 7)) { #ifdef ARDUINO_ARCH_AVR uint8_t result; asm volatile( "ldi %[result], 2 \n" // 0 = 000 => 1111 1111 = 0x00 - 1 "sbrs %A[bit], 1 \n" // 1 = 001 => 0111 1111 = 0x80 - 1 "ldi %[result], 8 \n" // 2 = 010 => 0011 1111 = 0x40 - 1 "sbrs %A[bit], 2 \n" // 3 = 011 => 0001 1111 = 0x20 - 1 "swap %[result] \n" "sbrs %A[bit], 0 \n" // 4 = 100 => 0000 1111 = 0x10 - 1 "lsl %[result] \n" // 5 = 101 => 0000 0111 = 0x08 - 1 "dec %[result] \n" // 6 = 110 => 0000 0011 = 0x04 - 1 :[result] "=&d" (result) // 7 = 111 => 0000 0001 = 0x02 - 1 :[bit] "r" (bit) : ); return result; #else return 0xFF >> (bit & 7); #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; static Cursor cursor; static FrameControl frameControl; }; #endif