diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..708a6e01 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,332 @@ +# Doxyfile 1.8.18 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "Arduboy2 Library" +PROJECT_NUMBER = 6.0.0 +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = doxygen +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +OUTPUT_TEXT_DIRECTION = None +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = YES +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = src +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +JAVADOC_BANNER = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 5 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PRIV_VIRTUAL = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = NO +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_AS_ERROR = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = src \ + README.md \ + LICENSE.txt +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.cpp \ + *.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = README.md +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = +CLANG_DATABASE_PATH = +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 245 +HTML_COLORSTYLE_SAT = 90 +HTML_COLORSTYLE_GAMMA = 95 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_MENUS = NO +HTML_DYNAMIC_SECTIONS = YES +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = YES +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +HTML_FORMULA_FORMAT = png +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = +MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = YES +LATEX_EMOJI_DIRECTORY = +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = YES +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/examples/ArduBreakout/ArduBreakout.ino b/examples/ArduBreakout/ArduBreakout.ino index daf55d1d..bbe66773 100644 --- a/examples/ArduBreakout/ArduBreakout.ino +++ b/examples/ArduBreakout/ArduBreakout.ino @@ -479,8 +479,8 @@ boolean displayHighScores(byte file) arduboy.setCursor(x, y+(i*8)); arduboy.print(text_buffer); arduboy.display(); - hi = EEPROM.read(address + (5*i)); - lo = EEPROM.read(address + (5*i) + 1); + hi = Arduboy2EEPROM::readByte(address + (5*i)); + lo = Arduboy2EEPROM::readByte(address + (5*i) + 1); if ((hi == 0xFF) && (lo == 0xFF)) { @@ -491,9 +491,9 @@ boolean displayHighScores(byte file) score = (hi << 8) | lo; } - initials[0] = (char)EEPROM.read(address + (5*i) + 2); - initials[1] = (char)EEPROM.read(address + (5*i) + 3); - initials[2] = (char)EEPROM.read(address + (5*i) + 4); + initials[0] = (char)Arduboy2EEPROM::readByte(address + (5*i) + 2); + initials[1] = (char)Arduboy2EEPROM::readByte(address + (5*i) + 3); + initials[2] = (char)Arduboy2EEPROM::readByte(address + (5*i) + 4); if (score > 0) { @@ -678,8 +678,8 @@ void enterHighScore(byte file) // High score processing for(byte i = 0; i < 7; i++) { - hi = EEPROM.read(address + (5*i)); - lo = EEPROM.read(address + (5*i) + 1); + hi = Arduboy2EEPROM::readByte(address + (5*i)); + lo = Arduboy2EEPROM::readByte(address + (5*i) + 1); if ((hi == 0xFF) && (lo == 0xFF)) { // The values are uninitialized, so treat this entry @@ -695,8 +695,8 @@ void enterHighScore(byte file) enterInitials(); for(byte j = i; j < 7; j++) { - hi = EEPROM.read(address + (5*j)); - lo = EEPROM.read(address + (5*j) + 1); + hi = Arduboy2EEPROM::readByte(address + (5*j)); + lo = Arduboy2EEPROM::readByte(address + (5*j) + 1); if ((hi == 0xFF) && (lo == 0xFF)) { @@ -707,16 +707,16 @@ void enterHighScore(byte file) tmpScore = (hi << 8) | lo; } - tmpInitials[0] = (char)EEPROM.read(address + (5*j) + 2); - tmpInitials[1] = (char)EEPROM.read(address + (5*j) + 3); - tmpInitials[2] = (char)EEPROM.read(address + (5*j) + 4); + tmpInitials[0] = (char)Arduboy2EEPROM::readByte(address + (5*j) + 2); + tmpInitials[1] = (char)Arduboy2EEPROM::readByte(address + (5*j) + 3); + tmpInitials[2] = (char)Arduboy2EEPROM::readByte(address + (5*j) + 4); // write score and initials to current slot - EEPROM.update(address + (5*j), ((score >> 8) & 0xFF)); - EEPROM.update(address + (5*j) + 1, (score & 0xFF)); - EEPROM.update(address + (5*j) + 2, initials[0]); - EEPROM.update(address + (5*j) + 3, initials[1]); - EEPROM.update(address + (5*j) + 4, initials[2]); + Arduboy2EEPROM::writeByte(address + (5*j), ((score >> 8) & 0xFF)); + Arduboy2EEPROM::writeByte(address + (5*j) + 1, (score & 0xFF)); + Arduboy2EEPROM::writeByte(address + (5*j) + 2, initials[0]); + Arduboy2EEPROM::writeByte(address + (5*j) + 3, initials[1]); + Arduboy2EEPROM::writeByte(address + (5*j) + 4, initials[2]); // tmpScore and tmpInitials now hold what we want to //write in the next slot. diff --git a/examples/SetSystemEEPROM/SetSystemEEPROM.ino b/examples/SetSystemEEPROM/SetSystemEEPROM.ino index 560c07f7..cb27fda4 100644 --- a/examples/SetSystemEEPROM/SetSystemEEPROM.ino +++ b/examples/SetSystemEEPROM/SetSystemEEPROM.ino @@ -717,7 +717,7 @@ void saveFlags() { // Reset the system EEPROM area and display the confirmation message void resetSysEEPROM() { for (unsigned int i = EEPROMstart; i < EEPROM_STORAGE_SPACE_START; i++) { - EEPROM.update(i, 0xFF); + Arduboy2EEPROM::writeByte(i, 0xFF); } arduboy.clear(); printStrLargeRev_P(RESET_SYS_CONFIRMED_1_X, RESET_SYS_CONFIRMED_1_Y, StrSystem); @@ -735,7 +735,7 @@ void resetUserEEPROM() { arduboy.setTextSize(1); arduboy.display(CLEAR_BUFFER); for (unsigned int i = EEPROM_STORAGE_SPACE_START; i <= EEPROMend; i++) { - EEPROM.update(i, 0xFF); + Arduboy2EEPROM::writeByte(i, 0xFF); } printStrLargeRev_P(RESET_USER_CONFIRMED_1_X, RESET_USER_CONFIRMED_1_Y, StrUser); printStrLargeRev_P(RESET_USER_CONFIRMED_2_X, RESET_USER_CONFIRMED_2_Y, StrEEPROM); diff --git a/keywords.txt b/keywords.txt index 0cba2eb0..53ae85c5 100644 --- a/keywords.txt +++ b/keywords.txt @@ -8,6 +8,8 @@ Arduboy2 KEYWORD1 Arduboy2Base KEYWORD1 +Arduboy2EEPROM KEYWORD1 +hash_type KEYWORD1 BeepPin1 KEYWORD1 BeepPin2 KEYWORD1 Point KEYWORD1 @@ -125,6 +127,17 @@ writeShowUnitNameFlag KEYWORD2 writeUnitID KEYWORD2 writeUnitName KEYWORD2 +# Arduboy2EEPROM class +begin KEYWORD2 +commit KEYWORD2 +writeByte KEYWORD2 +readByte KEYWORD2 +write KEYWORD2 +read KEYWORD2 +hash KEYWORD2 +writeWithHash KEYWORD2 +readWithHash KEYWORD2 + # Arduboy2Beep classes freq KEYWORD2 noTone KEYWORD2 diff --git a/src/Arduboy2.cpp b/src/Arduboy2.cpp index 552828db..4a82164d 100644 --- a/src/Arduboy2.cpp +++ b/src/Arduboy2.cpp @@ -98,7 +98,7 @@ void Arduboy2Base::sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal) digitalWriteRGB(BLUE_LED, RGB_OFF); // turn off blue LED delayShort(200); digitalWriteRGB(led, RGB_ON); // turn on "acknowledge" LED - EEPROM.update(eepromAudioOnOff, eeVal); + Arduboy2EEPROM::writeByte(eepromAudioOnOff, eeVal); delayShort(500); digitalWriteRGB(led, RGB_OFF); // turn off "acknowledge" LED @@ -1056,14 +1056,14 @@ bool Arduboy2Base::collide(Rect rect1, Rect rect2) uint16_t Arduboy2Base::readUnitID() { - return EEPROM.read(eepromUnitID) | - (((uint16_t)(EEPROM.read(eepromUnitID + 1))) << 8); + return Arduboy2EEPROM::readByte(eepromUnitID) | + (((uint16_t)(Arduboy2EEPROM::readByte(eepromUnitID + 1))) << 8); } void Arduboy2Base::writeUnitID(uint16_t id) { - EEPROM.update(eepromUnitID, (uint8_t)(id & 0xff)); - EEPROM.update(eepromUnitID + 1, (uint8_t)(id >> 8)); + Arduboy2EEPROM::writeByte(eepromUnitID, (uint8_t)(id & 0xff)); + Arduboy2EEPROM::writeByte(eepromUnitID + 1, (uint8_t)(id >> 8)); } uint8_t Arduboy2Base::readUnitName(char* name) @@ -1074,7 +1074,7 @@ uint8_t Arduboy2Base::readUnitName(char* name) for (dest = 0; dest < ARDUBOY_UNIT_NAME_LEN; dest++) { - val = EEPROM.read(src); + val = Arduboy2EEPROM::readByte(src); name[dest] = val; src++; if (val == 0x00 || (byte)val == 0xFF) { @@ -1097,48 +1097,48 @@ void Arduboy2Base::writeUnitName(const char* name) done = true; } // write character or 0 pad if finished - EEPROM.update(dest, done ? 0x00 : name[src]); + Arduboy2EEPROM::writeByte(dest, done ? 0x00 : name[src]); dest++; } } bool Arduboy2Base::readShowBootLogoFlag() { - return (EEPROM.read(eepromSysFlags) & sysFlagShowLogoMask); + return (Arduboy2EEPROM::readByte(eepromSysFlags) & sysFlagShowLogoMask); } void Arduboy2Base::writeShowBootLogoFlag(bool val) { - uint8_t flags = EEPROM.read(eepromSysFlags); + uint8_t flags = Arduboy2EEPROM::readByte(eepromSysFlags); bitWrite(flags, sysFlagShowLogoBit, val); - EEPROM.update(eepromSysFlags, flags); + Arduboy2EEPROM::writeByte(eepromSysFlags, flags); } bool Arduboy2Base::readShowUnitNameFlag() { - return (EEPROM.read(eepromSysFlags) & sysFlagUnameMask); + return (Arduboy2EEPROM::readByte(eepromSysFlags) & sysFlagUnameMask); } void Arduboy2Base::writeShowUnitNameFlag(bool val) { - uint8_t flags = EEPROM.read(eepromSysFlags); + uint8_t flags = Arduboy2EEPROM::readByte(eepromSysFlags); bitWrite(flags, sysFlagUnameBit, val); - EEPROM.update(eepromSysFlags, flags); + Arduboy2EEPROM::writeByte(eepromSysFlags, flags); } bool Arduboy2Base::readShowBootLogoLEDsFlag() { - return (EEPROM.read(eepromSysFlags) & sysFlagShowLogoLEDsMask); + return (Arduboy2EEPROM::readByte(eepromSysFlags) & sysFlagShowLogoLEDsMask); } void Arduboy2Base::writeShowBootLogoLEDsFlag(bool val) { - uint8_t flags = EEPROM.read(eepromSysFlags); + uint8_t flags = Arduboy2EEPROM::readByte(eepromSysFlags); bitWrite(flags, sysFlagShowLogoLEDsBit, val); - EEPROM.update(eepromSysFlags, flags); + Arduboy2EEPROM::writeByte(eepromSysFlags, flags); } void Arduboy2Base::swapInt16(int16_t& a, int16_t& b) @@ -1288,7 +1288,7 @@ void Arduboy2::bootLogoExtra() return; } - c = EEPROM.read(eepromUnitName); + c = Arduboy2EEPROM::readByte(eepromUnitName); if (c != 0xFF && c != 0x00) { @@ -1299,7 +1299,7 @@ void Arduboy2::bootLogoExtra() do { write(c); - c = EEPROM.read(++i); + c = Arduboy2EEPROM::readByte(++i); } while (i < eepromUnitName + ARDUBOY_UNIT_NAME_LEN); diff --git a/src/Arduboy2.h b/src/Arduboy2.h index 3b1cd44c..fd56b9f3 100644 --- a/src/Arduboy2.h +++ b/src/Arduboy2.h @@ -12,6 +12,7 @@ #include "Arduboy2Core.h" #include "Arduboy2Audio.h" #include "Arduboy2Beep.h" +#include "Arduboy2EEPROM.h" #include "Sprites.h" #include "SpritesB.h" #include diff --git a/src/Arduboy2Audio.cpp b/src/Arduboy2Audio.cpp index ed23248f..40322b4a 100644 --- a/src/Arduboy2Audio.cpp +++ b/src/Arduboy2Audio.cpp @@ -42,12 +42,12 @@ void Arduboy2Audio::toggle() void Arduboy2Audio::saveOnOff() { - EEPROM.update(Arduboy2Base::eepromAudioOnOff, audio_enabled); + Arduboy2EEPROM::writeByte(Arduboy2Base::eepromAudioOnOff, audio_enabled); } void Arduboy2Audio::begin() { - if (EEPROM.read(Arduboy2Base::eepromAudioOnOff)) + if (Arduboy2EEPROM::readByte(Arduboy2Base::eepromAudioOnOff)) on(); else off(); diff --git a/src/Arduboy2EEPROM.h b/src/Arduboy2EEPROM.h new file mode 100644 index 00000000..6073f7d0 --- /dev/null +++ b/src/Arduboy2EEPROM.h @@ -0,0 +1,583 @@ +#pragma once + +/// @file Arduboy2EEPROM.h +/// @brief The `Arduboy2EEPROM` class. +/// @details An API for manipulating EEPROM. +/// @author [Pharap](https://github.com/Pharap) + +// For size_t +#include + +// For uintptr_t, uint32_t +#include + +// For eeprom_read_byte and eeprom_update_byte +#include + +/// @brief +/// A `class` containing EEPROM-manipulating `static` functions. +/// +/// @warning +/// The Arduboy has 1KiB of EEPROM, which spans the consecutive range +/// of addresses from 0 to 1023 inclusive. Attempting to write to or +/// read from any address beyond that range that range +/// shall result in _undefined behaviour_. +/// +/// @warning +/// Violation of any of the preconditions or postconditions specified +/// herein shall result in _undefined behaviour_. +/// +/// @warning +/// The consequences of failing to adhere to the +/// preconditions and postconditions shall be the responsibility +/// of the programmer using the library. +class Arduboy2EEPROM +{ +public: + /// @brief + /// Initialises EEPROM for use. + /// + /// @par Complexity + /// `O(1)`. + /// + /// @pre + /// @li `begin()` has **not** been called previously in the program. + /// + /// @note + /// This function exists to support devices that do not have native EEPROM. + /// On devices that do have native EEPROM, such as the Arduboy, + /// this function is technically unneccessary and performs no work, + /// thus allowing it to be optimised away by the compiler. + /// + /// @warning + /// @li `begin()` **must** be called before attempting to perform any + /// EEPROM read or write operations, + /// e.g. `readByte()`, `writeByte()`, `read()`, `write()`. + /// @li Once `begin()` has been called, it **must not** be called again. + static void begin() + { + // This function is intentionally left blank + } + + /// @brief + /// Finalises the changes made by previous write operations by + /// committing them to memory. + /// + /// @par Complexity + /// `O(1)`. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// + /// @post + /// @li All data written via write operations + /// (e.g. `writeByte()`, `write()`) has been committed to memory. + /// + /// @note + /// This function exists to support devices that do not have native EEPROM. + /// On devices that do have native EEPROM, such as the Arduboy, + /// this function is technically unneccessary and performs no work, + /// thus allowing it to be optimised away by the compiler. + /// + /// @warning + /// @li `commit()` **must** be called to finalise the modifications made by + /// any previous write operations, e.g. `writeByte()`, `write()`. + /// @li Failure to call `commit()` **may** result in the discarding of + /// any or all of the data written by previous write operations. + /// Discarded data is **not** committed to memory. + /// + /// @note + /// When some data is written and other data is not, this is + /// known as a 'partial write'. + static void commit() + { + // This function is intentionally left blank + } + + /// @brief + /// Writes a byte to EEPROM at the specified address. + /// + /// @par Complexity + /// `O(1)`. + /// + /// @param[in] address + /// The address at which the provided byte is to be written. + /// + /// @param[in] byte + /// The value of the byte to be written to EEPROM. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// + /// @note + /// If the value to be written is the same as the value + /// already stored at the specified address then this + /// function will _not_ overwrite the already stored value. + /// This behaviour avoids unnecessarily wasting EEPROM + /// write-erase cycles, which are a limited resource. + static void writeByte(uintptr_t address, unsigned char byte) + { + eeprom_update_byte(reinterpret_cast(address), byte); + } + + /// @brief + /// Reads a byte from EEPROM at the specified address. + /// + /// @par Complexity + /// `O(1)`. + /// + /// @param[in] address + /// The address of the byte to be read. + /// + /// @return + /// The value of the byte at the specified address in EEPROM. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + static unsigned char readByte(uintptr_t address) + { + return eeprom_read_byte(reinterpret_cast(address)); + } + + /// @brief + /// Writes any object to EEPROM at the specified address. + /// + /// @par Complexity + /// `O(n)`, where `n` is `sizeof(object)`. + /// + /// @param[in] address + /// The address at which the provided object is to be written. + /// + /// @param[in] object + /// A reference to an object that is to be written to EEPROM. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// @li `((address + sizeof(object)) <= 1024)` — + /// The value of the expression `(address + sizeof(object))` + /// **must not** exceed a value of `1024`. + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// + /// @note + /// If the value to be written is the same as the value + /// already stored at the specified address then this + /// function will _not_ overwrite the already stored value. + /// This behaviour avoids unnecessarily wasting EEPROM + /// write-erase cycles, which are a limited resource. + /// + /// @warning + /// @parblock + /// Whilst pointers are guaranteed to retain the value they had when they + /// were saved, there are many circumstances in which the value of a + /// stored pointer may become invalid before its retrieval. + /// + /// E.g. a pointer that points to a global variable may be invalidated + /// if the program is recompiled, with or without a change to + /// compiler settings. + /// @endparblock + /// + /// @details + /// This function writes the provided `object`'s + /// + /// object representation + /// into EEPROM by taking a pointer to the `object`, + /// converting it to a `const unsigned char *`, + /// and writing the derived sequence of bytes into EEPROM. + template + static void write(uintptr_t address, const Type & object) + { + auto pointer = reinterpret_cast(&object); + + for(size_t index = 0; index < sizeof(object); ++index) + writeByte(address + index, pointer[index]); + } + + /// @brief + /// Reads any object from EEPROM at the specified address. + /// + /// @par Complexity + /// `O(n)`, where `n` is + /// [`sizeof(object)`](https://en.cppreference.com/w/cpp/language/sizeof). + /// + /// @param[in] address + /// The address of the object to be read. + /// + /// @param[in] object + /// A reference to an object that shall receive the data + /// read from EEPROM. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// @li `((address + sizeof(object)) <= 1024)` — + /// The value of the expression `(address + sizeof(object))` + /// **must not** exceed a value of `1024`. + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// + /// @warning + /// @parblock + /// Whilst pointers are guaranteed to retain the value they had when they + /// were saved, there are many circumstances in which the value of a + /// stored pointer may become invalid before its retrieval. + /// + /// E.g. a pointer that points to a global variable may be invalidated + /// if the program is recompiled, with or without a change to + /// compiler settings. + /// @endparblock + /// + /// @details + /// This function overwrites the provided `object`'s + /// + /// object representation + /// with an _object representation_ stored in EEPROM (i.e. by `write()`). + /// It does this by taking a pointer to the `object`, + /// converting it to an `unsigned char *`, + /// and reading a suitably-sized sequence of bytes + /// (i.e. a sequence of `sizeof(object)` bytes) from EEPROM. + template + static void read(uintptr_t address, Type & object) + { + auto pointer = reinterpret_cast(&object); + + for(size_t index = 0; index < sizeof(object); ++index) + pointer[index] = readByte(address + index); + } + + /// @brief + /// The type used to represent the hash code produced + /// by the `hash` function. + using hash_type = uint32_t; + + /// @brief + /// Calculates a hash code from the specified sequence of bytes. + /// + /// @par Complexity + /// `O(n)`, where `n` is `size`. + /// + /// @param[in] data + /// A pointer to a contiguous sequence of bytes that are + /// to be hashed to produce a hash code. + /// + /// @param[in] size + /// The quantity of bytes present in the sequence. + /// + /// @return + /// The hash code calculated from the provided sequence of bytes. + /// + /// @pre + /// @li `data != nullptr` — + /// `data` **must not** have a value of `nullptr`. + /// + /// @note + /// If `size` is `0`, the returned hash code will also be `0`. + static hash_type hash(const unsigned char * data, size_t size) + { + hash_type value = size; + + for(size_t index = 0; index < size; ++index) + value = (((value << 5) ^ (value >> 27)) ^ data[index]); + + return value; + } + + /// @brief + /// Calculates a hash code from the bytes of the specified object. + /// + /// @par Complexity + /// `O(n)`, where `n` is `sizeof(object)`. + /// + /// @param[in] object + /// An object from which a hash code is to be calculated. + /// + /// @return + /// The hash code calculated from the provided sequence of bytes. + /// + /// @pre + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// + /// @details + /// This function calculates a hash code of the provided `object` + /// by hashing the bytes of the `object`'s + /// + /// object representation. + /// It does this by taking a pointer to the `object`, + /// converting it to a `const unsigned char *`, + /// and calculating the hash of the resulting sequence of bytes. + template + static hash_type hash(const Type & object) + { + return hash(reinterpret_cast(&object), sizeof(object)); + } + + /// @brief + /// Writes both an object and a hash code + /// to EEPROM at the specified address. + /// + /// @par Complexity + /// `O(n)`, where `n` is `sizeof(object)`. + /// + /// @param[in] address + /// The address at which the provided object and its hash code + /// are to be written. + /// + /// @param[in] object + /// A reference to an object that is to be written to EEPROM. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// @li `((address + sizeof(hash_type) + sizeof(object)) <= 1024)` — + /// The value of the expression + /// `(address + sizeof(hash_type) + sizeof(object))` + /// **must not** exceed a value of `1024`. + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// **should not** have any `virtual` base classes. + /// + /// @note + /// If the value to be written is the same as the value + /// already stored at the specified address then this + /// function will not overwrite the already stored value. + /// This behaviour avoids unnecessarily wasting EEPROM + /// write-erase cycles, which are a limited resource. + /// + /// @see hash() write() + template + static void writeWithHash(uintptr_t address, const Type & object) + { + write(address, hash(object)); + write(address + sizeof(hash_type), object); + } + + /// @brief + /// Reads both an object and a hash code from EEPROM + /// at the specified address, and determines if the + /// hash of the object matches the stored hash code. + /// + /// @par Complexity + /// `O(n)`, where `n` is `sizeof(object)`. + /// + /// @param[in] address + /// The address of the hash code and object to be read. + /// + /// @param[out] object + /// A reference to an object that shall receive the data + /// read from EEPROM. + /// + /// @returns + /// @li `true` if the hash of the object matched the stored hash code. + /// @li `false` if the hash code did not match. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// @li `((address + sizeof(hash_type) + sizeof(object)) <= 1024)` — + /// The value of the expression + /// `(address + sizeof(hash_type) + sizeof(object))` + /// **must not** exceed a value of `1024`. + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// + /// @see hash() read() + template + static bool readWithHash(uintptr_t address, Type & object) + { + hash_type storedHash; + + read(address, storedHash); + read(address + sizeof(hash_type), object); + + return (storedHash == hash(object)); + } + + /// @brief + /// Writes both an object and a hash code + /// to EEPROM at the specified address. + /// This overload accepts a custom hash provider. + /// + /// @par Complexity + /// The complexity of this function is equivalent to + /// the complexity of the expression `hash(object)`, + /// where `hash` and `object` are the `hash` and `object` + /// parameters of this function. + /// + /// @param[in] address + /// The address at which the provided object and its hash code + /// are to be written. + /// + /// @param[in] object + /// A reference to an object that is to be written to EEPROM. + /// + /// @param[in] hash + /// A hash provider. + /// Can be any type that supports an `operator()`, + /// thus functions, function pointers, lambda expressions, + /// and `class`es and `struct`s with `operator()`s are all valid options. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// @li `((address + sizeof(hash_type) + sizeof(object)) <= 1024)` — + /// The value of the expression + /// `(address + sizeof(hash_type) + sizeof(object))` + /// **must not** exceed a value of `1024`. + /// @li The expression `hash(object)` **must** be a valid expression. + /// @li The type of `hash(object)` **should** satisfy the same + /// requirements as `Type`. + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// + /// @note + /// If the value to be written is the same as the value + /// already stored at the specified address then this + /// function will _not_ overwrite the already stored value. + /// This behaviour avoids unnecessarily wasting EEPROM + /// write-erase cycles, which are a limited resource. + /// + /// @see hash() write() + template + static void writeWithHash(uintptr_t address, const Type & object, Hash && hash) + { + using hash_type = decltype(hash(object)); + + const hash_type hashValue = static_cast(hash)(object); + + write(address, hashValue); + write(address + sizeof(hash_type), object); + } + + /// @brief + /// Reads both an object and a hash code from EEPROM + /// at the specified address, and determines if the + /// hash of the object matches the stored hash code. + /// This overload accepts a custom hash provider. + /// + /// @par Complexity + /// The complexity of this function is equivalent to + /// the complexity of the expression `hash(object)`, + /// where `hash` and `object` are the `hash` and `object` + /// parameters of this function. + /// + /// @param[in] address + /// The address of the hash code and object to be read. + /// + /// @param[out] object + /// A reference to an object that shall receive the data + /// read from EEPROM. + /// + /// @param[in] hash + /// A hash provider. + /// Can be any type that supports an `operator()`, + /// thus functions, function pointers, lambda expressions, + /// and `class`es and `struct`s with `operator()`s are all valid options. + /// + /// @returns + /// @li `true` if the hash of the object matched the stored hash code. + /// @li `false` if the hash code did not match. + /// + /// @pre + /// @li `begin()` has been called previously in the program. + /// @li `(address <= 1023)` — + /// `address` **must not** exceed a value of `1023`. + /// @li `((address + sizeof(hash_type) + sizeof(object)) <= 1024)` — + /// The value of the expression + /// `(address + sizeof(hash_type) + sizeof(object))` + /// **must not** exceed a value of `1024`. + /// @li The expression `hash(object)` **must** be a valid expression. + /// @li The type of `hash(object)` **should** satisfy the same + /// requirements as `Type`. + /// @li `Type` **should not** be a pointer type. + /// @li `Type` **should not** have any member variables of pointer type. + /// @li `Type` **should** be a + /// + /// trivial type. + /// @li If `Type` is a `struct` or `class` type, it **should** be a + /// + /// standard-layout class. — + /// In particular, that type + /// **should not** have any `virtual` functions and + /// **should not** have any `virtual` base classes. + /// + /// @see hash() read() + template + static bool readWithHash(uintptr_t address, Type & object, Hash && hash) + { + using hash_type = decltype(hash(object)); + + hash_type storedHash; + + read(address, storedHash); + read(address + sizeof(hash_type), object); + + const hash_type hashValue = static_cast(hash)(object); + + return (storedHash == hashValue); + } +}; \ No newline at end of file