#ifndef VIDEO_EGA_RENDER_REMAP_H
#define VIDEO_EGA_RENDER_REMAP_H

#define VAR_BYTE_MODE      (0 << 0)
#define VAR_WORD_MODE_MA13 (1 << 0)
#define VAR_WORD_MODE_MA15 (2 << 0)
#define VAR_DWORD_MODE     (3 << 0)
#define VAR_MODE_MASK      (3 << 0)
#define VAR_ROW0_MA13      (1 << 2)
#define VAR_ROW1_MA14      (1 << 3)

#define ADDRESS_REMAP_FUNC(nr)                                                                          \
    static uint32_t address_remap_func_##nr(ega_t *ega, uint32_t in_addr)                               \
    {                                                                                                   \
        uint32_t out_addr;                                                                              \
                                                                                                        \
        switch (nr & VAR_MODE_MASK) {                                                                   \
            case VAR_BYTE_MODE:                                                                         \
                out_addr = in_addr;                                                                     \
                break;                                                                                  \
                                                                                                        \
            case VAR_WORD_MODE_MA13:                                                                    \
                out_addr = ((in_addr << 1) & 0x3fff8) | ((in_addr >> 13) & 0x4) | (in_addr & ~0x3ffff); \
                break;                                                                                  \
                                                                                                        \
            case VAR_WORD_MODE_MA15:                                                                    \
                out_addr = ((in_addr << 1) & 0x3fff8) | ((in_addr >> 15) & 0x4) | (in_addr & ~0x3ffff); \
                break;                                                                                  \
                                                                                                        \
            case VAR_DWORD_MODE:                                                                        \
                out_addr = ((in_addr << 2) & 0x3fff0) | ((in_addr >> 14) & 0xc) | (in_addr & ~0x3ffff); \
                break;                                                                                  \
        }                                                                                               \
                                                                                                        \
        if (nr & VAR_ROW0_MA13)                                                                         \
            out_addr = (out_addr & ~0x8000) | ((ega->sc & 1) ? 0x8000 : 0);                             \
        if (nr & VAR_ROW1_MA14)                                                                         \
            out_addr = (out_addr & ~0x10000) | ((ega->sc & 2) ? 0x10000 : 0);                           \
                                                                                                        \
        return out_addr;                                                                                \
    }

ADDRESS_REMAP_FUNC(0)
ADDRESS_REMAP_FUNC(1)
ADDRESS_REMAP_FUNC(2)
ADDRESS_REMAP_FUNC(3)
ADDRESS_REMAP_FUNC(4)
ADDRESS_REMAP_FUNC(5)
ADDRESS_REMAP_FUNC(6)
ADDRESS_REMAP_FUNC(7)
ADDRESS_REMAP_FUNC(8)
ADDRESS_REMAP_FUNC(9)
ADDRESS_REMAP_FUNC(10)
ADDRESS_REMAP_FUNC(11)
ADDRESS_REMAP_FUNC(12)
ADDRESS_REMAP_FUNC(13)
ADDRESS_REMAP_FUNC(14)
ADDRESS_REMAP_FUNC(15)

static uint32_t (*address_remap_funcs[16])(ega_t *ega, uint32_t in_addr) = {
    address_remap_func_0,
    address_remap_func_1,
    address_remap_func_2,
    address_remap_func_3,
    address_remap_func_4,
    address_remap_func_5,
    address_remap_func_6,
    address_remap_func_7,
    address_remap_func_8,
    address_remap_func_9,
    address_remap_func_10,
    address_remap_func_11,
    address_remap_func_12,
    address_remap_func_13,
    address_remap_func_14,
    address_remap_func_15
};

void
ega_recalc_remap_func(ega_t *ega)
{
    int func_nr;

    if (ega->crtc[0x14] & 0x40)
        func_nr = VAR_DWORD_MODE;
    else if (ega->crtc[0x17] & 0x40)
        func_nr = VAR_BYTE_MODE;
    else if ((ega->crtc[0x17] & 0x20) && ega->vram_limit > 64 * 1024)
        func_nr = VAR_WORD_MODE_MA15;
    else
        func_nr = VAR_WORD_MODE_MA13;

    if (!(ega->crtc[0x17] & 0x01))
        func_nr |= VAR_ROW0_MA13;
    if (!(ega->crtc[0x17] & 0x02))
        func_nr |= VAR_ROW1_MA14;

    ega->remap_required = (func_nr != 0);
    ega->remap_func     = address_remap_funcs[func_nr];
}

#endif /*VIDEO_RENDER_REMAP_H*/
