starting to implement lib/eeprom-macro

partial-rewrite
Ben Blazak 2013-06-01 17:32:05 -07:00
parent 8b5daa9d92
commit 8867e1b9d2
2 changed files with 228 additions and 146 deletions

View File

@ -11,16 +11,16 @@
*
* This file is meant to be included and used by the keyboard layout
* implementation.
*/
/*
* TODO: everything: still in the planning stages
*/
/*
*
*
* TODO: might need a "get_size_total()" and "get_size_free()" later, for
* information display purposes
*
*
* TODO: rewrite the following
* notes:
*
* - these functions will play back keypresses, not functions or (layer, row,
* - these functions will play back keystrokes, not functions or (layer, row,
* column) tuples, or anything like that. this means that if you press a key
* that has a macro assigned to it while defining a new macro, the former
* macro will be activated each time the new macro is run (unless it is
@ -44,91 +44,6 @@
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// - "block"s are 4 bytes, aligned on the 4 byte boundary
// - all pointers are 1 byte (until converted for use), representing the offset
// of a block
// - the value of padding bits is undefined, as is the value of all but the
// `next` and `run_length` fields of "deleted" macros; undefined values must
// be ignored
/*
* example for a [3][3] matrix
*
*
* header block 0x00
* .--------+--------+--------+--------. addresses 0x0000..0x0003
* 0 |version | free | padding | = 0x00<<2..(0x00<<2)+3
* '--------+--------+--------+--------'
*
* table blocks 0x01..0x03
* .--------. .--------. .--------. addresses 0x0004..0x000F
* 1 |pointer | |pointer | |pointer |
* '--------' '--------' '--------'
* .--------. .--------. .--------.
* |pointer | 2 |pointer | |pointer |
* '--------' '--------' '--------'
* .--------. .--------. .--------.
* |pointer | |pointer | 3 |pointer |
* '--------' '--------' '--------'
* .--------. .--------. .--------.
* |padding | |padding | |padding |
* '--------' '--------' '--------'
*
*
* macros blocks 0x04..0xFF
* addresses 0x0016..0x03FF
* .--------+--------. .--------+--------.
* 4 | next |run_len | | index |
* '--------+--------' '-----------------'
* .--------+--------.
* 5 | action |
* '--------+--------'
* .--------+--------.
* | action |
* '--------+--------'
*
* 6 ...
*/
#define ROWS OPT__KB__ROWS // for '.c' file
#define COLS OPT__KB__COLUMNS // for '.c' file
#define START_HEADER 0x00
#define START_TABLE 0x01
#define START_MACROS (((ROWS * COLS + 0x3) >> 2) + 1) // > 1
#define END_EEPROM 0xFF
// flags have meaning only when assigned to `next` pointers within macro
// headers; they are invalid `next` addresses, but they are valild block
// addresses
#define FLAG_DELETED 0x01
#define FLAG_2 0x02 // reserved
struct header {
uint8_t version; // of this layout; 0x00 or 0xFF => uninitialized
uint8_t start_free; // pointer to the first unallocated block
uint16_t padding;
};
struct table {
uint8_t pointers[ ROWS * COLS ]; // to `macro`s
uint8_t padding[ 3 - ((ROWS * COLS) & 0x3) ]; // may be length = 0
};
struct macro {
uint8_t next; // 0x00 => last for this [row][col]
// 0x01 => "deleted" macro
uint8_t run_length;
eeprom_macro__index_t index;
};
struct action { // `run_length` of these will follow `macro`
eeprom_macro__index_t index; // `layer` will be unset and ignored
};
// ----------------------------------------------------------------------------
#define EEPROM_MACRO__VERSION 1
typedef struct {
bool pressed : 1;
@ -136,63 +51,145 @@ typedef struct {
uint8_t row : 5;
uint8_t column : 5;
} eeprom_macro__index_t;
/* - this format artificially limits the number of layers, rows, and columns to
* 2^5 = 32 each, which seems like it'd be hard to practically exceed. this
* is done because it's much easier to extract the values we're going to use
* if we separate them this way than if we reverse the array offset
* calculations for `[layer][row][column]`.
*
* - note that 15 bits in *some* format will always be enough to store our
* index, since 2^15 = 32768 positions are unlikely to be taken up by any
* layout matrix ever; on the ATMega32U4 it would be impossible, since
* there's only 32256 bytes of PROGMEM to begin with.
*/
uint8_t eeprom_macro__init(void);
/* - check version
* - "zero" EEPROM if necessary (i.e. if uninitialized for the current version)
*/
// ----------------------------------------------------------------------------
uint8_t eeprom_macro__get_size_free(void);
/* - return free space, in blocks
*/
void eeprom_macro__play(eeprom_macro__index_t index);
/* - play back keystrokes (loop: call `kb__layout__exec_key()`)
*/
uint8_t eeprom_macro__record__start(uint8_t skip);
/* - skip `skip` keypresses
* - (`...exec_key()` may need these to determine what key to assign the
* macro to
* - we don't need to worry about them, since the keyboard may want to assign
* the macro to a different key; and if the keyboard is using layers we
* can't tell what key to assign it to ourselves without a bunch of pain
* anyway
* - start recording
* - return 0 on success, other on failure
*/
uint8_t eeprom_macro__record__stop(uint8_t skip, eeprom_macro__index_t index);
/* - stop recording
* - discard `skip` keypresses
* - update all header and "filesystem" information
* - return 0 on success, other on failure
*/
void eeprom_macro__compress(void);
/* - remove all "deleted" macros, shifting active ones toward address 0
*/
void eeprom_macro__clear(eeprom_macro__index_t index);
/* - set the `next` pointer for this index to `FLAG_DELETED`
*/
void eeprom_macro__clear_all(void);
/* - put EEPROM into a valid and intialized, but "zeroed" state
*/
uint8_t eeprom_macro__init (void);
uint8_t eeprom_macro__record__start (uint8_t skip);
uint8_t eeprom_macro__record__stop (uint8_t skip, eeprom_macro__index_t index);
uint8_t eeprom_macro__play (eeprom_macro__index_t index);
void eeprom_macro__clear (eeprom_macro__index_t index);
void eeprom_macro__clear_all (void);
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
#endif // ERGODOX_FIRMWARE__FIRMWARE__LIB__LAYOUT__EEPROM_MACRO__H
// ============================================================================
// === documentation ==========================================================
// ============================================================================
// ----------------------------------------------------------------------------
// types ----------------------------------------------------------------------
// === eeprom_macro__index_t ===
/** types/eeprom_macro__index_t/description
* A convenient way to specify a position in the layer matrix
*
* Used here to uniquely identify macros, and to group them for optimizations.
*
* Notes:
* - This format artificially limits the number of layers, rows, and columns
* that can be specified to 2^5 = 32 each. This seems like it'd be hard to
* reach practically, but it's much less than the limit of 2^8 possible
* values each imposed by most of the rest of the firmware, so it's worth
* noting.
* - An alternate method would be to use all 15 bits (since 1 of the 16
* available bits is allocated to `pressed`) to represent a layer matrix
* index calculated according to the normal rules of array indexing for
* some `[layer][row][column]`. The current method is preferred because
* it makes it much easier to extract the component values (which is
* helpful in grouping the macros, which is helpful in macro seek time
* optimizations).
* - 15 bits in *some* format should always be enough to uniquely identify
* every possible macro. On the ATMega32U4 it would be impossible to
* exceed this, since it only has 32256 bytes of PROGMEM to begin with.
* Even if there was more, however, it seems unlikely that more than
* 2^15 = 32768 positions would be specified by any layer matrix.
*/
// ----------------------------------------------------------------------------
// functions ------------------------------------------------------------------
// === eeprom_macro__init ===
/** functions/eeprom_macro__init/description
* Perform any necessary initializations
*
* Returns:
* - success: `0`
* - failure: [other]
*
* Meant to be called exactly once by `kb__init()`
*
* Notes:
* - This function should zero the EEPROM to the current format if the version
* of the data stored is different than what we expect.
*/
// === eeprom_macro__record__start ===
/** functions/eeprom_macro__record__start/description
* Start recording keystrokes to the EEPROM, for the creation of a macro
*
* Arguments:
* - `skip`: The number of keystrokes to skip before beginning to record
*
* Returns:
* - success: `0`
* - failure: [other] (not enough memory left to record)
*
* Notes:
* - Skipping keystrokes may be useful, for example, if the using function
* wants to define the first key pressed after this function is called as the
* key to assign the macro to, rather than as part of the macro.
*/
// === eeprom_macro__record__stop ===
/** functions/eeprom_macro__record__stop/description
* Stop recording keystrokes, and finalize the macro
*
* Arguments:
* - `skip`: The number of keystrokes at the end of our recording to ignore
* - `index`: The unique ID of this macro
*
* Returns:
* - success: `0`
* - failure: [other] (macro was too long to fit in EEPROM)
*
* Notes:
* - Before this function is called, the macro should not be referenced
* anywhere in the EEPROM.
*/
// === eeprom_macro__play ===
/** functions/eeprom_macro__play/description
* Play back recorded keystrokes for the macro with unique ID `index`
*
* Arguments:
* - `index`: The unique ID of the macro to play
*
* Returns:
* - `0`: Macro successfully played
* - [other]: Error (macro does not exist)
*
* Notes:
* - Keystrokes will be played back as if the same sequence of keys were being
* pressed by the user (regardless of whether the current state of the
* keyboard is the same), except as fast as possible (since timing is not
* recorded).
*/
// === eeprom_macro__clear ===
/** functions/eeprom_macro__clear/description
* Clear the macro with unique ID `index`
*
* Arguments:
* - `index`: The unique ID of the macro to clear
*/
// === eeprom_macro__clear_all ===
/** functions/eeprom_macro__clear_all/description
* Clear all macros in the EEPROM
*
* Notes:
* - For the purposes of this function, "clearing" the EEPROM means to put it
* in such a state that none of the functions declared here will be able to
* find a macro for any `index`. This does not necessarily imply that the
* EEPROM is in a fully known state.
*/

View File

@ -8,6 +8,91 @@
* Implements the eeprom-macro functionality defined in "../eeprom-macro.h" for
* the ATMega32U4
*
* TODO: implement
* Implementation notes:
* - "block"s are 4 bytes long, aligned on the 4 byte boundary
* - blocks are pointed to by `uint8_t`s
* - the EEPROM contains 1024 bytes = 256 blocks
* (i.e. 2^10 bytes, 2^8 blocks)
* - the beginning of a block is pointed to by
* `eeprom_address = block_address<<2`
*/
#include <stdint.h>
#include <avr/eeprom.h>
// ----------------------------------------------------------------------------
// for convenience
#define ROWS OPT__KB__ROWS
#define COLUMNS OPT__KB__COLUMNS
// ----------------------------------------------------------------------------
/** macros/HEADER/description
* The beginning of the `HEADER` section (the lowest block of the EEPROM that
* we're allowed to use)
*
* Size (in blocks): 1
*
* Format:
* - byte 0: the version of this layout
* - `0x00`, `0xFF` => uninitialized
* - byte 1: the number of rows in the table
* - byte 2: the number of columns in the table
* - byte 3: the first free block not yet used by `MACROS`
*
* Notes:
* - The number of rows and columns is significant to the offsets of some of
* the sections, and so is important to keep track of here, along with the
* version number.
*/
#define HEADER 0x00
/** macros/TABLE/description
* The beginning of the `TABLE` section
*
* Size (in blocks): `ceil( (ROWS * COLUMNS) / 4 )`
* - This is conveniently calculated by taking `(ROWS * COLUMNS + 0b11) >> 2`
*
* Notes:
* - Each entry is a block pointer to the first macro with the corresponding
* `row` and `column` in its unique ID
*
* Format:
* - A `[ROWS][COLUMNS]` table, where the `[row][column]` entry can be accessed
* by taking the `[ (row * COLUMNS) + column ]` entry (as is normal with
* arrays in C).
*/
#define TABLE (HEADER + 1)
/** macros/TABLE/description
* The beginning of the `MACROS` section
*
* Size (in blocks): `END - MACROS + 1` (since `END` is included)
*
* Format: macro header:
* - byte 0: a block pointer to the next macro with the same `row` and `column`
* in its unique ID
* - `0x00` => this macro is the last in the linked list for this `row`,
* `column` pair
* - `0xFF` => this macro has been deleted
* - byte 1: the run length of the macro, in number of key actions
* - byte 2..3: the unique ID of this macro (its `index`)
*
* Format: key action
* - The same as the format of `eeprom_macro__index_t` (which is the format of
* the unique IDs), except that `layer` will be ignored
*/
#define MACROS (TABLE + ((ROWS * COLUMNS + 0b11) >> 2))
/** macros/END/description
* The end of the `MACROS` section (the highest block of the EEPROM that we're
* allowed to use)
*/
#define END 0xFF
// ----------------------------------------------------------------------------
// TODO: functions