AVR EEPROM read, write and update routines for Arduino.

This is a demonstration of inline assembly functions for Arduino. As well there is disassembly of each function get by avr-objdump tool.
There are many similar functions like these around. Update function tries to limit writes to EEPROM as much possible and write or erase each
cell only when it is necessary. The details are in ATmega datasheets.

void setup()
{
    asm volatile(
        "   .equ EECR,   0x1f     \n"
        "   .equ EEDR,   0x20     \n"
        "   .equ EEARL,  0x21     \n"
        "   .equ EEARH,  0x22     \n"
        "   .equ SREG,   0x3f     \n"

        "   .equ EERE,   0        \n"
        "   .equ EEPE,   1        \n"
        "   .equ EEMPE,  2        \n"
        "   .equ EERIE,  3        \n"
        "   .equ EEPM0,  4        \n"
        "   .equ EEPM1,  5        \n"
        :::
    );
}

// =====================================
// ========== EEPROM routines ==========
// =====================================

byte EEPROM_Read(word address)
{
    byte value;
    asm volatile(
        "0: sbic EECR, EEPE       \n"
        "   rjmp 0b               \n"
        "   out  EEARH, %B1       \n"
        "   out  EEARL, %A1       \n"
        "   sbi  EECR, EERE       \n"
        "   in   %0, EEDR         \n"
        : "=r" (value)
        : "r" (address)
        :
    );
    return value;
}

/*
0000012a <_Z11EEPROM_Readj>:
     12a: f9 99       sbic 0x1f, 1    ; 31
     12c: fe cf       rjmp .-4        ; 0x12a <_Z11EEPROM_Readj>
     12e: 92 bd       out  0x22, r25  ; 34
     130: 81 bd       out  0x21, r24  ; 33
     132: f8 9a       sbi  0x1f, 0    ; 31
     134: 80 b5       in   r24, 0x20  ; 32
     136: 08 95       ret
*/

void EEPROM_WriteEx(word address, byte value, byte flags)
{
    asm volatile(
        "0: sbic EECR, EEPE        \n"
        "   rjmp 0b                \n"
        "   out  EEARH, %B0        \n"
        "   out  EEARL, %A0        \n"
        "   out  EEDR, %1          \n"
        "   in   __tmp_reg__, SREG \n"
        "   cli                    \n"
        "   out  EECR, %2          \n"
        "   sbi  EECR, EEMPE       \n"
        "   sbi  EECR, EEPE        \n"
        "   out  SREG, __tmp_reg__ \n"
        :
        : "r" (address), "r" (value), "r" (flags)
        :
    );
}

/*
00000138 <_Z14EEPROM_WriteExjhh>:
     138: f9 99       sbic 0x1f, 1    ; 31
     13a: fe cf       rjmp .-4        ; 0x138 <_Z14EEPROM_WriteExjhh>
     13c: 92 bd       out  0x22, r25  ; 34
     13e: 81 bd       out  0x21, r24  ; 33
     140: 60 bd       out  0x20, r22  ; 32
     142: 0f b6       in   r0, 0x3f   ; 63
     144: f8 94       cli
     146: 4f bb       out  0x1f, r20  ; 31
     148: fa 9a       sbi  0x1f, 2    ; 31
     14a: f9 9a       sbi  0x1f, 1    ; 31
     14c: 0f be       out  0x3f, r0   ; 63
     14e: 08 95       ret
*/

void EEPROM_Erase(word address)
{
    EEPROM_WriteEx(address, 0, 1 << EEPM0);
}

/*
00000150 <_Z12EEPROM_Erasej>:
     150: 40 e1       ldi  r20, 0x10  ; 16
     152: 60 e0       ldi  r22, 0x00  ; 0
     154: 0c 94 9c 00 jmp  0x138      ; 0x138 <_Z14EEPROM_WriteExjhh>
*/

void EEPROM_WriteOnly(word address, byte value)
{
    EEPROM_WriteEx(address, value, 1 << EEPM1);
}

/*
00000158 <_Z16EEPROM_WriteOnlyjh>:
     158: 40 e2       ldi  r20, 0x20  ; 32
     15a: 0c 94 9c 00 jmp  0x138      ; 0x138 <_Z14EEPROM_WriteExjhh>
*/

void EEPROM_Write(word address, byte value)
{
    EEPROM_WriteEx(address, value, 0);
}

/*
0000015e <_Z12EEPROM_Writejh>:
     15e: 40 e0       ldi  r20, 0x00  ; 0
     160: 0c 94 9c 00 jmp  0x138      ; 0x138 <_Z14EEPROM_WriteExjhh>
*/

// if following routine calls EEPROM_WriteEx directly with relevant arguments
// it'd be less illustrative - but the compiled code would be shorter

void EEPROM_Update(word address, byte value)
{
    byte oldvalue = EEPROM_Read(address);
    if (oldvalue != value)
    {
        if (value == 0xff)
        {
            EEPROM_Erase(address);                // Erase
        }
        else
        {
            if ((oldvalue & value) == value)      // only zeroes are to be written
                EEPROM_WriteOnly(address, value); // Write only
            else
                EEPROM_Write(address, value);     // Erase & write
        }
    }
}

/*
00000164 <_Z13EEPROM_Updatejh>:
     164: 1f 93       push r17
     166: cf 93       push r28
     168: df 93       push r29
     16a: ec 01       movw r28, r24
     16c: 16 2f       mov  r17, r22
     16e: 0e 94 95 00 call 0x12a      ; 0x12a <_Z11EEPROM_Readj>
     172: 81 17       cp   r24, r17
     174: c9 f0       breq .+50       ; 0x1a8 <_Z13EEPROM_Updatejh+0x44>
     176: 1f 3f       cpi  r17, 0xFF  ; 255
     178: 31 f4       brne .+12       ; 0x186 <_Z13EEPROM_Updatejh+0x22>
     17a: ce 01       movw r24, r28
     17c: df 91       pop  r29
     17e: cf 91       pop  r28
     180: 1f 91       pop  r17
     182: 0c 94 a8 00 jmp  0x150      ; 0x150 <_Z12EEPROM_Erasej>
     186: 68 2f       mov  r22, r24
     188: 61 23       and  r22, r17
     18a: 61 13       cpse r22, r17
     18c: 06 c0       rjmp .+12       ; 0x19a <_Z13EEPROM_Updatejh+0x36>
     18e: ce 01       movw r24, r28
     190: df 91       pop  r29
     192: cf 91       pop  r28
     194: 1f 91       pop  r17
     196: 0c 94 ac 00 jmp  0x158      ; 0x158 <_Z16EEPROM_WriteOnlyjh>
     19a: 61 2f       mov  r22, r17
     19c: ce 01       movw r24, r28
     19e: df 91       pop  r29
     1a0: cf 91       pop  r28
     1a2: 1f 91       pop  r17
     1a4: 0c 94 af 00 jmp  0x15e      ; 0x15e <_Z12EEPROM_Writejh>
     1a8: df 91       pop  r29
     1aa: cf 91       pop  r28
     1ac: 1f 91       pop  r17
     1ae: 08 95       ret
*/

// =====================================
// ====== end of EEPROM routines =======
// =====================================