## Guided Overview

Let's say you have a couple LEDs that are controlled by memory-mapped
registers, a common case for embedded microcontrollers. To begin, you'll need
to define the register used to control the LEDs.

For an 8-bit register at memory address `0x0021`, you would write:

```cpp
using PORTA_OUT = fr::MemRegister<uint8_t, 0x0021>;
```

`MemRegister` is an `Register` that uses `MemoryIO` access. Registers
have static functions for interacting with their contents; for example, we
could now do `PORTA_OUT::write(0x10)` or `auto state = PORTA::read()`.

A lot more can be done with registers once we define some register masks. A
`RegisterMask` lets us name one or more bits within a register.

To name our two LEDs, which are only controlled by single bits, we write:

```cpp
using LED_1 = fr::RegisterMask<PORTA_OUT, (1 << 0)>; // bit zero
using LED_2 = fr::RegisterMask<PORTA_OUT, (1 << 2)>; // bit two
```

We can also give a name to both LEDs:

```cpp
using LED_ALL = fr::RegisterMask<PORTA_OUT, (1 << 0) | (1 << 2)>;
```

Now, we can control the LEDs either directly or through the register. Direct
calls would be like `LED_1::set()`; there is also `clear`, `toggle`, `read`,
`write`, and `test`.

These calls can also be made through the register using template parameters:

```cpp
PORTA_OUT::set<LED_2>();
```

Registers can take multiple masks at once too. The masks will be merged so that
the register is only read and written once:

```cpp
PORTA_OUT::toggle<LED_1, LED_2>();
```

A `modify` function is also supported, which takes a list of mask operations as
shown below. This allows the different operations to be carried out together,
still keeping to a single register read and write:

```cpp
PORTA_OUT::modify<LED_1::set, LED_2::clear>(); // Only have LED_1 turned on
```

What if we need to add a third LED? And what if that LED is on a different
register, PORTB?

This is where `RegisterGroup` comes in handy:

```cpp
using PORTB_OUT = fr::MemRegister<uint8_t, 0x0041>;

using LED_3 = fr::RegisterMask<PORTB_OUT, (1 << 5)>;

// Group the output ports together:
using LEDS = fr::RegisterGroup<PORTA_OUT, PORTB_OUT>;
```

By grouping the two registers, we can carry out our modification calls without
worrying about which mask is for what register. The `RegisterGroup` will take
of that, while still merging operations when possible to maintain minimal I/O:

```cpp
LEDS::clear<LED_1, LED_2, LED_3>();

LEDS::modify<LED_1::set,
             LED_2::clear,
             LED_3::toggle>(); // You get the idea...
```

## Other features

### Multi-bit masks

Say bits two through five in a register select a clock's prescaler:

```cpp
using CLOCK_DIV = fr::RegisterMask<CLOCK_CONTROL, 0x3C>;
```

The mask's `write` function will let you write values to the field:

```cpp
CLOCK_DIV::write<0x03>();
```

This will read the register's current value, clear all bits selected by the
mask, set the new value `0x03` in the mask's location, then update the
register.

`write` can also be included in `modify` chains:

```cpp
CLOCK_CONTROL::modify<CLOCK_DIV::write<0x03>, CLOCK_ENABLE::set>();
```

A `RegisterMaskValue` can also be defined to identify specific values:

```cpp
using CLOCK_DIV4 = fr::RegisterMaskValue<CLOCK_DIV, 0x03>;
```

`RegisterMaskValue` supports three functions: `set`, which would call
`CLOCK_DIV::write<0x03>()`; `clear`, which clears the masked bits; and `test`,
which would confirm that the register contains the value `0x03` in the masked
bits' location.

### External registers

"External" registers are registers that are not memory-mapped. These are also
supported in *funreg*, and can even be placed in `RegisterGroup`s with
other register types.

An "access type" must be defined to specify how the register is accessed. Here
is the definition of `MemoryIO`, which is used for memory-mapped registers:

```cpp
template<typename T, uintptr_t Addr>
struct MemoryIO {
    using type = T;
    constexpr static auto addr = Addr;

    /**
     * Reads the register's value.
     */
    constexpr static T read() {
        return *reinterpret_cast<volatile T*>(Addr);
    }

    /**
     * Overwrites the register's value.
     */
    constexpr static void write(const T& value) {
        *reinterpret_cast<volatile T*>(Addr) = value;
    }
};
```

Custom access types should use `MemoryIO` as a template. For example, here is
an access type for ports on x86 processors:

```cpp
template<typename T, uintptr_t Addr>
struct PortIO {
    using type = T;
    constexpr static auto addr = Addr;

    static T read() {
        T ret;
        asm volatile("in %0, %1" : "=r" (ret) : "r" (Addr));
        return ret; 
    }

    static void write(const T& value) {
        asm volatile("out %0, %1" :: "r" (value), "r" (Addr));
    }
};
```

Now, just define your register(s) using `ExtRegister`:

```cpp
using KEYBOARD = fr::ExtRegister<PortIO, uint8_t, 0x60>;
```