working on eemprom-macro :)

partial-rewrite
Ben Blazak 2013-12-27 17:29:19 -08:00
parent f0fcfca4ef
commit b41a716631
2 changed files with 190 additions and 101 deletions

View File

@ -237,8 +237,8 @@ void eeprom_macro__clear_all (void);
* - `column`: The column of the key action
*
* Returns:
* - `true`: if a macro with the given UID exists
* - `false`: if a macro with the given UID does not exist
* - `true`: if a macro remapping the given key-action exists
* - `false`: if a macro remapping the given key-action does not exist
*/
// === eeprom_macro__clear() ===

View File

@ -20,11 +20,10 @@
* primarily little endian: in avr-gcc, multi-byte data types are allocated
* with the least significant byte occupying the lowest address. Protocols,
* data formats (including UTF-8), and such are primarily big endian. I like
* little endianness better -- it feels more mathematically consistent to me
* -- but after writing a bit of code, it seems that big endian
* serializations are easier to work with, at least in C. For that reason,
* this code organizes bytes in a big endian manner whenever it has a choice
* between the two.
* little endianness better -- it just feels nicer to me -- but after writing
* a bit of code, it seems that big endian serializations are easier to work
* with, at least in C. For that reason, this code organizes bytes in a big
* endian manner whenever it has a choice between the two.
*
* - For a long time, I was going to try to make this library robust in the
* event of power loss, but in the end I decided not to. This feature is
@ -52,6 +51,10 @@
#error "OPT__EEPROM__EEPROM_MACRO__END must not be greater than 1023"
#endif
#if EEMEM_END - EEMEM_START < 300
#warn "Only a small space has been allocated for macros"
#endif
// ----------------------------------------------------------------------------
// macros ---------------------------------------------------------------------
@ -81,20 +84,97 @@
* - `EEMEM_MACROS_END`
* - `EEMEM_END`: The address of the last byte of our block of EEMEM
*
*
* EEMEM sections:
*
* - START_ADDRESS:
* - byte 0: MSB of `EEMEM_START`
* - byte 1: LSB of `EEMEM_START`
*
* - END_ADDRESS:
* - byte 0: MSB of `EEMEM_END`
* - byte 1: LSB of `EEMEM_END`
*
* - VERSION:
* - This byte will all be set to `VERSION` as the last step of
* initializing our portion of the EEPROM.
* - Upon initialization, if this value is not equal to the current
* `VERSION`, our portion of the EEPROM should be reinitialized.
* - MACROS:
* - byte 0..`(EEMEM_END - EEMEM_VERSION_END - 1)`: TODO
* - byte 0:
* - This byte will all be set to `VERSION` as the last step of
* initializing our portion of the EEPROM.
* - Upon initialization, if this value is not equal to the current
* `VERSION`, our portion of the EEPROM should be reinitialized.
*
* - MACROS: byte 0..`(EEMEM_END - EEMEM_VERSION_END - 1)`:
* - This section will contain a series of zero or more macros, each with
* the following format:
* - byte 0: `type == TYPE_DELETED`
* - byte 1: `length`: the total number of bytes used by this
* macro, including the bytes for `type` and `length`
* - byte 2...: (optional) undefined
* - byte 0: `type == TYPE_VALID_MACRO`
* - byte 1: `length`: the total number of bytes used by this
* macro, including the bytes for `type` and `length`
* - `key-action` 0: the key-action which this macro remaps
* - See below for a description of the bytes making up a
* key-action
* - `key-action` 1...: (optional) the key-actions to which
* `key-action` 0 is remapped
* - byte 0: `type == TYPE_END`
* - byte 1...: (optional) undefined
*
* - The last key-action in this series will have `type == TYPE_END`.
*
* - A key-action is a variable length encoding of the information in a
* `key_action_t`, with the following format:
*
* byte 0
* .----------------------------------------------.
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* |----------------------------------------------|
* | continued | pressed | layer | row | column |
* '----------------------------------------------'
*
* byte 1..3 (optional)
* .----------------------------------------------.
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* |----------------------------------------------|
* | continued | n/a | layer | row | column |
* '----------------------------------------------'
*
* - `continued`:
* - `1`: The next byte is part of this key-action
* - `0`: The next byte is not part of this key-action (i.e. this
* is the last byte in this key-action)
*
* - `layer`, `row`, `column`:
* - In the first byte of this key-action, these fields contain the
* two most significant bits of their respective values such that
* these bits are nonzero in *any* of `layer`, `row`, or
* `column`. In subsequent bytes of this key-action, these
* fields contain the pair of bits to the right of the pair of
* bits in the previous key-action byte (the next less
* significant pair of bits). If `layer`, `row`, and `column`
* all equal `0`, then these three fields will all equal `0`, and
* there will only be 1 byte written for this key-action.
*
* - Example of an encoded key-action (where `*` means "undefined"):
*
* --- as a key_action_t ---
* pressed = true
* layer = 0 b 00 00 01 00
* row = 0 b 00 01 10 01
* column = 0 b 00 10 00 11
* | '- least significant pair of bits
* '- most significant pair of bits
*
* --- in EEMEM ---
* byte 0 = 0 b 1 1 00 01 10
* byte 1 = 0 b 1 * 01 10 00
* byte 2 = 0 b 0 * 00 01 11
* | | | | '- column bit pair
* | | | '- row bit pair
* | | '- layer bit pair
* | '- pressed / n/a
* '- continued
*
*
* Notes:
* - `START_ADDRESS` and `END_ADDRESS` are written as part of our effort to
@ -105,14 +185,14 @@
* of the EEPROM have the same values for each.
*/
#define EEMEM_START ((void *)OPT__EEPROM__EEPROM_MACRO__START)
#define EEMEM_START_ADDRESS_START EEMEM_START
#define EEMEM_START_ADDRESS_START EEMEM_START + 0
#define EEMEM_START_ADDRESS_END EEMEM_START_ADDRESS_START + 1
#define EEMEM_END_ADDRESS_START EEMEM_START_ADDRESS_END + 1
#define EEMEM_END_ADDRESS_END EEMEM_END_ADDRESS_START + 1
#define EEMEM_VERSION_START EEMEM_END_ADDRESS_END + 1
#define EEMEM_VERSION_END EEMEM_VERSION_START + 0
#define EEMEM_MACROS_START EEMEM_VERSION_END + 1
#define EEMEM_MACROS_END EEMEM_END
#define EEMEM_MACROS_END EEMEM_END - 0
#define EEMEM_END ((void *)OPT__EEPROM__EEPROM_MACRO__END)
/** macros/(group) type/description
@ -131,7 +211,18 @@
// types ----------------------------------------------------------------------
/** types/key_action_t/description
* TODO
* To hold everything needed to represent a single key-action (the press or
* release of a specific key on a specific layer of the layout matrix).
*
* Struct members:
* - `pressed`: Whether the key is pressed (`true`) or not (`false`)
* - `layer`: The layer of the key, in the layout matrix
* - `row`: The row of the key, in the layout matrix
* - `column`: The column of the key, in the layout matrix
*
* Notes:
* - Since these fields together can reference any key (on any layer)
* unambiguously, a `key_action_t` may also serve as a UID for a key.
*/
typedef struct {
bool pressed;
@ -143,23 +234,29 @@ typedef struct {
// ----------------------------------------------------------------------------
// local functions ------------------------------------------------------------
// - if we go back to little endian, need to modify the comments at the top of
// the file, as well as the documented order of the bytes for the stored
// start and end addresses of the EEPROM
//
// - sizes (in bytes) (with optimizations on):
//
// function frame stack
// read big 154 3 10
// read little 200 2 10
// write big 160 0 6
// write little 172 0 5
/**
* TODO
/** functions/read_key_action/description
* Read and return the key-action beginning at `*from` in the EEPROM, and
* advance `*from` to one byte past the key-action.
*
* Arguments:
* - `from`: A pointer to a pointer to the location in EEPROM from which to
* begin reading
*
* Returns:
* - success: The key-action, as a `key_action_t`
*
* Notes:
* - See the documentation for "(group) EEMEM layout" above for a description
* of the layout of key-actions in EEMEM.
*/
key_action_t read_key_action_big_endian(void * from) {
uint8_t byte = eeprom__read(from++);
key_action_t read_key_action(void ** from) {
uint8_t byte;
// handle the first byte
// - since this byte (and no others) stores the value of `k.pressed`
// - also, this allows us to avoid `|=` in favor of `=` for this byte
byte = eeprom__read((*from)++);
key_action_t k = {
.pressed = byte >> 6 & 0b01,
@ -168,13 +265,21 @@ key_action_t read_key_action_big_endian(void * from) {
.column = byte >> 0 & 0b11,
};
while (byte >> 7) {
byte = eeprom__read(from++);
// handle all subsequent bytes
// - we assume the stream is valid. especially, we do not check to make
// sure that the key-action is no more than 4 bytes long.
while (byte >> 7) {
byte = eeprom__read((*from)++);
// shift up (make more significant) the bits we have so far, to make
// room for the bits we just read
k.layer <<= 2;
k.row <<= 2;
k.column <<= 2;
// logical or the bits we just read into the lowest (least significant)
// positions
k.layer |= byte >> 4 & 0b11;
k.row |= byte >> 2 & 0b11;
k.column |= byte >> 0 & 0b11;
@ -182,46 +287,54 @@ key_action_t read_key_action_big_endian(void * from) {
return k; // success
}
key_action_t read_key_action_little_endian(void * from) {
uint8_t byte = eeprom__read(from++);
key_action_t k = {
.pressed = ( byte & 0x40 ),
.layer = ( byte & 0x30 ) << 2,
.row = ( byte & 0x0C ) << 4,
.column = ( byte & 0x03 ) << 6,
};
uint8_t i = 0;
for (; byte>>7; i++) {
byte = eeprom__read(from++);
k.layer >>= 2;
k.row >>= 2;
k.column >>= 2;
k.layer |= ( byte & 0x30 ) << 2;
k.row |= ( byte & 0x0C ) << 4;
k.column |= ( byte & 0x03 ) << 6;
}
for (; i<4; i++) {
k.layer >>= 2;
k.row >>= 2;
k.column >>= 2;
}
return k; // success
}
/**
* TODO
/** functions/write_key_action/description
* Write the given information to a key-action beginning at `*to` in the
* EEPROM, and advance `*to` to one byte past the newly written key-action.
*
* Arguments:
* - `to`: A pointer to a pointer to the location in EEPROM at which to begin
* writing
* - `k`: The key-action to write
*
* Returns:
* - success: `0`
* - failure: [other]
*
* Notes:
* - See the documentation for "(group) EEMEM layout" above for a description
* of the layout of key-actions in EEMEM.
*
* Implementation notes:
* - We handle the `layer`, `row`, and `column` variables (inside `k`) as being
* made up of 4 pairs of bits.
* - We deal with these bits beginning with the high (most significant) pair,
* and shifting left (towards the most significant end of the byte) to
* discard bit pairs we're done with.
* - This method seemed faster (i.e. generated less assembly code) when I
* was testing than leaving the `layer`, `row`, and `column` bytes as
* they were and using a variable mask (as in `k.layer & 0b11 << i*2`).
* It's probably worthwhile to note that I was looking at the assembly
* (though not closely) and function size with optimizations turned on.
*/
uint8_t write_key_action_big_endian(void * to, key_action_t k) {
if (to > EEMEM_END-3)
uint8_t write_key_action(void ** to, key_action_t k) {
// - we need to leave room after this macro (and therefore after this
// key-action) for the `TYPE_END` byte
if ((*to) > EEMEM_END-4)
return 1; // error: might not be enough space
// ignore the bits we don't need to write
// - if the leading two bits of all three variables are `0b00`, we don't
// need to write a key-action byte containing that pair of bits
// - the maximum number of pairs of bits we can ignore is 3; the last pair
// (the least significant) must be written to the EEPROM regardless of
// its value
// - we set `i` here (and make it global to the function) because we need
// to make sure to *consider writing* exactly 4 pairs of bits. some may
// be skipped, some or all may be written, but the total of both must be
// 4.
uint8_t i = 0;
for (; i<3 && !((k.layer|k.row|k.column) & 0xC0); i++) {
@ -230,6 +343,11 @@ uint8_t write_key_action_big_endian(void * to, key_action_t k) {
k.column <<= 2;
}
// write key-action bytes for all bit pairs that weren't ignored
// - the first byte contains the value of `k.pressed`
// - all bytes except the last one written (containing the least
// significant bits) have their first bit set to `1`
uint8_t byte = (k.pressed ? 1 : 0) << 6;
for (; i<4; i++) {
@ -237,8 +355,9 @@ uint8_t write_key_action_big_endian(void * to, key_action_t k) {
| ( k.layer & 0xC0 ) >> 2
| ( k.row & 0xC0 ) >> 4
| ( k.column & 0xC0 ) >> 6 ;
eeprom__write(to++, byte);
eeprom__write((*to)++, byte);
byte = 0;
k.layer <<= 2;
k.row <<= 2;
k.column <<= 2;
@ -246,36 +365,6 @@ uint8_t write_key_action_big_endian(void * to, key_action_t k) {
return 0; // success
}
uint8_t write_key_action_little_endian(void * to, key_action_t k) {
if (to > EEMEM_END-3)
return 1; // error: might not be enough space
uint8_t byte = ( k.pressed ? 1 : 0 ) << 6
| ( k.layer & 0b11 ) << 4
| ( k.row & 0b11 ) << 2
| ( k.column & 0b11 ) << 0 ;
k.layer >>= 2;
k.row >>= 2;
k.column >>= 2;
while (k.layer|k.row|k.column) {
byte |= 1 << 7;
eeprom__write(to++, byte);
byte = ( k.layer & 0b11 ) << 4
| ( k.row & 0b11 ) << 2
| ( k.column & 0b11 ) << 0 ;
k.layer >>= 2;
k.row >>= 2;
k.column >>= 2;
}
eeprom__write(to, byte);
return 0; // success
}
// TODO: rewriting (yet again) - stopped here
#if 0