// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020-2022 Antonio Niño Díaz

#ifndef HARDWARE_H__
#define HARDWARE_H__

#include <stdint.h>

#include "definitions.h"

// Device definitions
// ------------------

#define GBA_SCREEN_W    240
#define GBA_SCREEN_H    160

static inline uint16_t RGB15(uint16_t r, uint16_t g, uint16_t b)
{
    return (r & 0x1F) | ((g & 0x1F) << 5) | ((b & 0x1F) << 10);
}

// Memory Regions Definitions
// ==========================

// BIOS (16 KiB)
#define MEM_BIOS_SIZE           (0x00004000)
#define MEM_BIOS                ((void *)MEM_BIOS_ADDR)

// EWRAM (256 KiB)
#define MEM_EWRAM_SIZE          (0x00040000)
#define MEM_EWRAM               ((void *)MEM_EWRAM_ADDR)

// IWRAM (32 KiB)
#define MEM_IWRAM_SIZE          (0x00008000)
#define MEM_IWRAM               ((void *)MEM_IWRAM_ADDR)

#define BIOS_INTR_FLAGS         *((volatile uint16_t *)(MEM_IWRAM_ADDR + 0x7FF8))

// I/O Registers
#define MEM_IO_SIZE             (0x00000400)
#define MEM_IO                  ((void *)MEM_IO_ADDR)

// BG/OBJ Palette RAM (1 KiB)
#define MEM_PALETTE_SIZE        (0x00000400)
#define MEM_PALETTE             ((void *)MEM_PALETTE_ADDR)

#define MEM_PALETTE_BG          ((uint16_t *)MEM_PALETTE)
#define MEM_PALETTE_OBJ         ((uint16_t *)(MEM_PALETTE_ADDR + 0x200))

#define MEM_BACKDROP_COLOR      (MEM_PALETTE_BG[0])

// VRAM - Video RAM (96 KiB)
#define MEM_VRAM_SIZE           (0x00018000)
#define MEM_VRAM                ((void *)MEM_VRAM_ADDR)

#define MEM_VRAM_BG             MEM_VRAM
#define MEM_VRAM_BG_SIZE        (0x00010000)
#define MEM_VRAM_OBJ            ((void *)(MEM_VRAM_ADDR + MEM_VRAM_BG_SIZE))
#define MEM_VRAM_OBJ_SIZE       (0x00008000)

#define MEM_BG_TILES_BLOCK_SIZE (0x4000)
#define MEM_BG_TILES_BLOCK_ADDR(n) \
                (((n) * MEM_BG_TILES_BLOCK_SIZE) + MEM_VRAM_ADDR)

#define MEM_BG_MAP_BLOCK_SIZE   (0x800)
#define MEM_BG_MAP_BLOCK_ADDR(n) \
                (((n) * MEM_BG_MAP_BLOCK_SIZE) + MEM_VRAM_ADDR)

#define MEM_VRAM_MODE3_FB       ((uint16_t *)MEM_VRAM)

#define MEM_VRAM_MODE4_FRAME0   ((uint16_t *)MEM_VRAM_ADDR)
#define MEM_VRAM_MODE4_FRAME1   ((uint16_t *)(MEM_VRAM_ADDR + 40 * 1024))

#define MEM_VRAM_MODE5_FRAME0   ((uint16_t *)MEM_VRAM_ADDR)
#define MEM_VRAM_MODE5_FRAME1   ((uint16_t *)(MEM_VRAM_ADDR + 40 * 1024))

// OAM - OBJ Attributes (1 KiB)
#define MEM_OAM_SIZE            (0x00000400)
#define MEM_OAM                 ((void *)MEM_OAM_ADDR)

// Game Pak ROM/FlashROM (max 32MiB)
#define MEM_ROM_SIZE            (0x02000000)
#define MEM_ROM_WS0             ((void *)MEM_ROM_ADDR_WS0)
#define MEM_ROM_WS1             ((void *)MEM_ROM_ADDR_WS1)
#define MEM_ROM_WS2             ((void *)MEM_ROM_ADDR_WS2)

// Game Pak SRAM (32 KiB)
#define MEM_SRAM_SIZE           (0x00008000)
#define MEM_SRAM                ((void *)MEM_SRAM_ADDR)

// Base addresses of all regions

#ifdef __GBA__

# define MEM_BIOS_ADDR      (0x00000000)
# define MEM_EWRAM_ADDR     (0x02000000)
# define MEM_IWRAM_ADDR     (0x03000000)
# define MEM_IO_ADDR        (0x04000000)
# define MEM_PALETTE_ADDR   (0x05000000)
# define MEM_VRAM_ADDR      (0x06000000)
# define MEM_OAM_ADDR       (0x07000000)
# define MEM_ROM_ADDR_WS0   (0x08000000) // Wait State 0
# define MEM_ROM_ADDR_WS1   (0x0A000000) // Wait State 1
# define MEM_ROM_ADDR_WS2   (0x0C000000) // Wait State 2
# define MEM_SRAM_ADDR      (0x0E000000)

#else

EXPORT_API uintptr_t UGBA_MemBIOS(void);
EXPORT_API uintptr_t UGBA_MemEWRAM(void);
EXPORT_API uintptr_t UGBA_MemIWRAM(void);
EXPORT_API uintptr_t UGBA_MemIO(void);
EXPORT_API uintptr_t UGBA_MemPalette(void);
EXPORT_API uintptr_t UGBA_MemVRAM(void);
EXPORT_API uintptr_t UGBA_MemOAM(void);
EXPORT_API uintptr_t UGBA_MemROM(void);
EXPORT_API uintptr_t UGBA_MemSRAM(void);

# define MEM_BIOS_ADDR      (UGBA_MemBIOS())
# define MEM_EWRAM_ADDR     (UGBA_MemEWRAM())
# define MEM_IWRAM_ADDR     (UGBA_MemIWRAM())
# define MEM_IO_ADDR        (UGBA_MemIO())
# define MEM_PALETTE_ADDR   (UGBA_MemPalette())
# define MEM_VRAM_ADDR      (UGBA_MemVRAM())
# define MEM_OAM_ADDR       (UGBA_MemOAM())
# define MEM_ROM_ADDR_WS0   (UGBA_MemROM())
# define MEM_ROM_ADDR_WS1   MEM_ROM_ADDR_WS0
# define MEM_ROM_ADDR_WS2   MEM_ROM_ADDR_WS0
# define MEM_SRAM_ADDR      (UGBA_MemSRAM())

#endif // __GBA__

// Tilemap definitions
// ===================

// 16 bits per entry
#define MAP_REGULAR_TILE(v)         ((v) & 0x3FF)
#define MAP_REGULAR_HFLIP           (1 << 10)
#define MAP_REGULAR_VFLIP           (1 << 11)
#define MAP_REGULAR_PALETTE(v)      (((v) & 0xF) << 12) // 16 color backgrounds

// 8 bits per entry
#define MAP_AFFINE_TILE(v)          ((v) & 0xFF)

// OAM Definitions
// ===============

typedef struct ALIGNED(8) {
    uint16_t attr0;
    uint16_t attr1;
    uint16_t attr2;
    uint16_t padding;
} oam_entry;

static_assert(sizeof(oam_entry) == 8, "Wrong oam_entry size");

#define MEM_OAM_NUMBER_ENTRIES      (128)
#define MEM_OAM_ENTRIES             ((oam_entry *)MEM_OAM)

// Attribute 0

#define ATTR0_Y(v)                  ((v) & 0xFF)
#define ATTR0_Y_MASK                (0xFF)

#define ATTR0_REGULAR               (0 << 8)
#define ATTR0_AFFINE                (1 << 8)
#define ATTR0_TYPE_MASK             (1 << 8)

#define ATTR0_DOUBLE_SIZE           (1 << 9) // Affine objects
#define ATTR0_DISABLE               (1 << 9) // Regular objects

#define ATTR0_MODE_MASK             (3 << 10)
#define ATTR0_MODE_NORMAL           (0 << 10)
#define ATTR0_MODE_TRANSPARENT      (1 << 10)
#define ATTR0_MODE_WINDOW           (2 << 10)

#define ATTR0_MOSAIC                (1 << 12)

#define ATTR0_16_COLORS             (0 << 13)
#define ATTR0_256_COLORS            (1 << 13)

#define ATTR0_SHAPE_MASK            (3 << 14)
#define ATTR0_SHAPE_SQUARE          (0 << 14)
#define ATTR0_SHAPE_HORIZONTAL      (1 << 14)
#define ATTR0_SHAPE_VERTICAL        (2 << 14)

// Attribute 1

#define ATTR1_X(v)                  ((v) & 0x1FF)
#define ATTR1_X_MASK                (0x1FF)

#define ATTR1_AFFINE_MATRIX(v)      (((v) & 0x1F) << 9) // Affine objects
#define ATTR1_AFFINE_MATRIX_MASK    (0x1F << 9) // Affine objects

#define ATTR1_REGULAR_HFLIP         (1 << 12) // Regular objects
#define ATTR1_REGULAR_VFLIP         (1 << 13) // Regular objects

#define ATTR1_SIZE_MASK             (3 << 14)
#define ATTR1_SIZE_0                (0 << 14)
#define ATTR1_SIZE_1                (1 << 14)
#define ATTR1_SIZE_2                (2 << 14)
#define ATTR1_SIZE_3                (3 << 14)

// Attribute 2

#define ATTR2_16_COLOR_TILE(v)      ((v) & 0x3FF)
#define ATTR2_256_COLOR_TILE(v)     (((v) & 0x1FF) << 1)
#define ATTR2_TILE_MASK             (0x3FF)

#define ATTR2_PRIORITY(v)           (((v) & 0x3) << 10)
#define ATTR2_PRIORITY_MASK         (0x3 << 10)

#define ATTR2_PALETTE(v)            (((v) & 0xF) << 12) // 16 color objects
#define ATTR2_PALETTE_MASK          (0xF << 12)

typedef struct ALIGNED(32) {
    uint16_t padding0[3];
    int16_t pa;
    uint16_t padding1[3];
    int16_t pb;
    uint16_t padding2[3];
    int16_t pc;
    uint16_t padding3[3];
    int16_t pd;
} oam_matrix_entry;

static_assert(sizeof(oam_matrix_entry) == 32, "Wrong oam_matrix_entry size");

#define MEM_OAM_NUMBER_MATRICES     (32)
#define MEM_OAM_MATRICES            ((oam_matrix_entry *)MEM_OAM)

// I/O Registers Definitions
// =========================

// LCD I/O Registers
#define OFFSET_DISPCNT      (0x000) // R/W
#define OFFSET_GREENSWAP    (0x002) // R/W
#define OFFSET_DISPSTAT     (0x004) // R/W
#define OFFSET_VCOUNT       (0x006) // R
#define OFFSET_BG0CNT       (0x008) // R/W
#define OFFSET_BG1CNT       (0x00A) // R/W
#define OFFSET_BG2CNT       (0x00C) // R/W
#define OFFSET_BG3CNT       (0x00E) // R/W
#define OFFSET_BG0HOFS      (0x010) // W
#define OFFSET_BG0VOFS      (0x012) // W
#define OFFSET_BG1HOFS      (0x014) // W
#define OFFSET_BG1VOFS      (0x016) // W
#define OFFSET_BG2HOFS      (0x018) // W
#define OFFSET_BG2VOFS      (0x01A) // W
#define OFFSET_BG3HOFS      (0x01C) // W
#define OFFSET_BG3VOFS      (0x01E) // W
#define OFFSET_BG2PA        (0x020) // W
#define OFFSET_BG2PB        (0x022) // W
#define OFFSET_BG2PC        (0x024) // W
#define OFFSET_BG2PD        (0x026) // W
#define OFFSET_BG2X_L       (0x028) // W
#define OFFSET_BG2X_H       (0x02A) // W
#define OFFSET_BG2Y_L       (0x02C) // W
#define OFFSET_BG2Y_H       (0x02E) // W
#define OFFSET_BG3PA        (0x030) // W
#define OFFSET_BG3PB        (0x032) // W
#define OFFSET_BG3PC        (0x034) // W
#define OFFSET_BG3PD        (0x036) // W
#define OFFSET_BG3X_L       (0x038) // W
#define OFFSET_BG3X_H       (0x03A) // W
#define OFFSET_BG3Y_L       (0x03C) // W
#define OFFSET_BG3Y_H       (0x03E) // W
#define OFFSET_WIN0H        (0x040) // W
#define OFFSET_WIN1H        (0x042) // W
#define OFFSET_WIN0V        (0x044) // W
#define OFFSET_WIN1V        (0x046) // W
#define OFFSET_WININ        (0x048) // R/W
#define OFFSET_WINOUT       (0x04A) // R/W
#define OFFSET_MOSAIC       (0x04C) // W
#define OFFSET_BLDCNT       (0x050) // R/W
#define OFFSET_BLDALPHA     (0x052) // W
#define OFFSET_BLDY         (0x054) // W

// Sound Registers
#define OFFSET_SOUND1CNT_L  (0x060) // R/W
#define OFFSET_SOUND1CNT_H  (0x062) // R/W
#define OFFSET_SOUND1CNT_X  (0x064) // R/W
#define OFFSET_SOUND2CNT_L  (0x068) // R/W
#define OFFSET_SOUND2CNT_H  (0x06C) // R/W
#define OFFSET_SOUND3CNT_L  (0x070) // R/W
#define OFFSET_SOUND3CNT_H  (0x072) // R/W
#define OFFSET_SOUND3CNT_X  (0x074) // R/W
#define OFFSET_SOUND4CNT_L  (0x078) // R/W
#define OFFSET_SOUND4CNT_H  (0x07C) // R/W
#define OFFSET_SOUNDCNT_L   (0x080) // R/W
#define OFFSET_SOUNDCNT_H   (0x082) // R/W
#define OFFSET_SOUNDCNT_X   (0x084) // R/W
#define OFFSET_SOUNDBIAS    (0x088) // BIOS
#define OFFSET_WAVE_RAM     (0x090) // R/W -- 2x10h bytes
#define OFFSET_FIFO_A       (0x0A0) // W
#define OFFSET_FIFO_B       (0x0A4) // W

// DMA Transfer Channels
#define OFFSET_DMA0SAD      (0x0B0) // W
#define OFFSET_DMA0DAD      (0x0B4) // W
#define OFFSET_DMA0CNT_L    (0x0B8) // W
#define OFFSET_DMA0CNT_H    (0x0BA) // R/W
#define OFFSET_DMA1SAD      (0x0BC) // W
#define OFFSET_DMA1DAD      (0x0C0) // W
#define OFFSET_DMA1CNT_L    (0x0C4) // W
#define OFFSET_DMA1CNT_H    (0x0C6) // R/W
#define OFFSET_DMA2SAD      (0x0C8) // W
#define OFFSET_DMA2DAD      (0x0CC) // W
#define OFFSET_DMA2CNT_L    (0x0D0) // W
#define OFFSET_DMA2CNT_H    (0x0D2) // R/W
#define OFFSET_DMA3SAD      (0x0D4) // W
#define OFFSET_DMA3DAD      (0x0D8) // W
#define OFFSET_DMA3CNT_L    (0x0DC) // W
#define OFFSET_DMA3CNT_H    (0x0DE) // R/W

// Timer Registers
#define OFFSET_TM0CNT_L     (0x100) // R/W
#define OFFSET_TM0CNT_H     (0x102) // R/W
#define OFFSET_TM1CNT_L     (0x104) // R/W
#define OFFSET_TM1CNT_H     (0x106) // R/W
#define OFFSET_TM2CNT_L     (0x108) // R/W
#define OFFSET_TM2CNT_H     (0x10A) // R/W
#define OFFSET_TM3CNT_L     (0x10C) // R/W
#define OFFSET_TM3CNT_H     (0x10E) // R/W

// Serial Communication (1)
#define OFFSET_SIODATA32    (0x120) // R/W
#define OFFSET_SIOMULTI0    (0x120) // R/W
#define OFFSET_SIOMULTI1    (0x122) // R/W
#define OFFSET_SIOMULTI2    (0x124) // R/W
#define OFFSET_SIOMULTI3    (0x126) // R/W
#define OFFSET_SIOCNT       (0x128) // R/W
#define OFFSET_SIOMLT_SEND  (0x12A) // R/W
#define OFFSET_SIODATA8     (0x12A) // R/W

// Keypad Input
#define OFFSET_KEYINPUT     (0x130) // R
#define OFFSET_KEYCNT       (0x132) // R/W

// Serial Communication (2)
#define OFFSET_RCNT         (0x134) // R/W
#define OFFSET_JOYCNT       (0x140) // R/W
#define OFFSET_JOY_RECV     (0x150) // R/W
#define OFFSET_JOY_TRANS    (0x154) // R/W
#define OFFSET_JOYSTAT      (0x158) // R/?

// Interrupt, Waitstate, and Power-Down Control
#define OFFSET_IE           (0x200) // R/W
#define OFFSET_IF           (0x202) // R/W
#define OFFSET_WAITCNT      (0x204) // R/W
#define OFFSET_IME          (0x208) // R/W

//------------------------------------------

#define REG_8(r)            *((volatile uint8_t *)(MEM_IO_ADDR + (r)))
#define REG_16(r)           *((volatile uint16_t *)(MEM_IO_ADDR + (r)))
#define REG_32(r)           *((volatile uint32_t *)(MEM_IO_ADDR + (r)))

#define PTR_REG_8(r)        ((volatile uint8_t *)(MEM_IO_ADDR + (r)))
#define PTR_REG_16(r)       ((volatile uint16_t *)(MEM_IO_ADDR + (r)))
#define PTR_REG_32(r)       ((volatile uint32_t *)(MEM_IO_ADDR + (r)))

//------------------------------------------

#define REG_DISPCNT         REG_16(OFFSET_DISPCNT)
#define REG_GREENSWAP       REG_16(OFFSET_GREENSWAP)
#define REG_DISPSTAT        REG_16(OFFSET_DISPSTAT)
#define REG_VCOUNT          REG_16(OFFSET_VCOUNT)
#define REG_BG0CNT          REG_16(OFFSET_BG0CNT)
#define REG_BG1CNT          REG_16(OFFSET_BG1CNT)
#define REG_BG2CNT          REG_16(OFFSET_BG2CNT)
#define REG_BG3CNT          REG_16(OFFSET_BG3CNT)
#define REG_BG0HOFS         REG_16(OFFSET_BG0HOFS)
#define REG_BG0VOFS         REG_16(OFFSET_BG0VOFS)
#define REG_BG1HOFS         REG_16(OFFSET_BG1HOFS)
#define REG_BG1VOFS         REG_16(OFFSET_BG1VOFS)
#define REG_BG2HOFS         REG_16(OFFSET_BG2HOFS)
#define REG_BG2VOFS         REG_16(OFFSET_BG2VOFS)
#define REG_BG3HOFS         REG_16(OFFSET_BG3HOFS)
#define REG_BG3VOFS         REG_16(OFFSET_BG3VOFS)
#define REG_BG2PA           REG_16(OFFSET_BG2PA)
#define REG_BG2PB           REG_16(OFFSET_BG2PB)
#define REG_BG2PC           REG_16(OFFSET_BG2PC)
#define REG_BG2PD           REG_16(OFFSET_BG2PD)
#define REG_BG2X_L          REG_16(OFFSET_BG2X_L)
#define REG_BG2X_H          REG_16(OFFSET_BG2X_H)
#define REG_BG2X            REG_32(OFFSET_BG2X_L)
#define REG_BG2Y_L          REG_16(OFFSET_BG2Y_L)
#define REG_BG2Y_H          REG_16(OFFSET_BG2Y_H)
#define REG_BG2Y            REG_32(OFFSET_BG2Y_L)
#define REG_BG3PA           REG_16(OFFSET_BG3PA)
#define REG_BG3PB           REG_16(OFFSET_BG3PB)
#define REG_BG3PC           REG_16(OFFSET_BG3PC)
#define REG_BG3PD           REG_16(OFFSET_BG3PD)
#define REG_BG3X_L          REG_16(OFFSET_BG3X_L)
#define REG_BG3X_H          REG_16(OFFSET_BG3X_H)
#define REG_BG3X            REG_32(OFFSET_BG3X_L)
#define REG_BG3Y_L          REG_16(OFFSET_BG3Y_L)
#define REG_BG3Y_H          REG_16(OFFSET_BG3Y_H)
#define REG_BG3Y            REG_32(OFFSET_BG3Y_L)
#define REG_WIN0H           REG_16(OFFSET_WIN0H)
#define REG_WIN1H           REG_16(OFFSET_WIN1H)
#define REG_WIN0V           REG_16(OFFSET_WIN0V)
#define REG_WIN1V           REG_16(OFFSET_WIN1V)
#define REG_WININ           REG_16(OFFSET_WININ)
#define REG_WINOUT          REG_16(OFFSET_WINOUT)
#define REG_MOSAIC          REG_16(OFFSET_MOSAIC)
#define REG_BLDCNT          REG_16(OFFSET_BLDCNT)
#define REG_BLDALPHA        REG_16(OFFSET_BLDALPHA)
#define REG_BLDY            REG_16(OFFSET_BLDY)
#define REG_SOUND1CNT_L     REG_16(OFFSET_SOUND1CNT_L)
#define REG_SOUND1CNT_H     REG_16(OFFSET_SOUND1CNT_H)
#define REG_SOUND1CNT_X     REG_16(OFFSET_SOUND1CNT_X)
#define REG_SOUND2CNT_L     REG_16(OFFSET_SOUND2CNT_L)
#define REG_SOUND2CNT_H     REG_16(OFFSET_SOUND2CNT_H)
#define REG_SOUND3CNT_L     REG_16(OFFSET_SOUND3CNT_L)
#define REG_SOUND3CNT_H     REG_16(OFFSET_SOUND3CNT_H)
#define REG_SOUND3CNT_X     REG_16(OFFSET_SOUND3CNT_X)
#define REG_SOUND4CNT_L     REG_16(OFFSET_SOUND4CNT_L)
#define REG_SOUND4CNT_H     REG_16(OFFSET_SOUND4CNT_H)
#define REG_SOUNDCNT_L      REG_16(OFFSET_SOUNDCNT_L)
#define REG_SOUNDCNT_H      REG_16(OFFSET_SOUNDCNT_H)
#define REG_SOUNDCNT_X      REG_16(OFFSET_SOUNDCNT_X)
#define REG_SOUNDBIAS       REG_16(OFFSET_SOUNDBIAS)

#ifdef __GBA__
# define REG_WAVE_RAM       PTR_REG_16(OFFSET_WAVE_RAM)
#else // __GBA__
EXPORT_API volatile uint16_t *UGBA_MemWaveRam(void);
# define REG_WAVE_RAM       (UGBA_MemWaveRam())
#endif // __GBA__

#define REG_FIFO_A          PTR_REG_32(OFFSET_FIFO_A)
#define REG_FIFO_B          PTR_REG_32(OFFSET_FIFO_B)

#define REG_DMA0CNT_L       REG_16(OFFSET_DMA0CNT_L)
#define REG_DMA0CNT_H       REG_16(OFFSET_DMA0CNT_H)
#define REG_DMA0CNT         REG_32(OFFSET_DMA0CNT_L)
#define REG_DMA1CNT_L       REG_16(OFFSET_DMA1CNT_L)
#define REG_DMA1CNT_H       REG_16(OFFSET_DMA1CNT_H)
#define REG_DMA1CNT         REG_32(OFFSET_DMA1CNT_L)
#define REG_DMA2CNT_L       REG_16(OFFSET_DMA2CNT_L)
#define REG_DMA2CNT_H       REG_16(OFFSET_DMA2CNT_H)
#define REG_DMA2CNT         REG_32(OFFSET_DMA2CNT_L)
#define REG_DMA3CNT_L       REG_16(OFFSET_DMA3CNT_L)
#define REG_DMA3CNT_H       REG_16(OFFSET_DMA3CNT_H)
#define REG_DMA3CNT         REG_32(OFFSET_DMA3CNT_L)

#ifdef __GBA__

# define REG_DMA0SAD        REG_32(OFFSET_DMA0SAD)
# define REG_DMA0DAD        REG_32(OFFSET_DMA0DAD)
# define REG_DMA1SAD        REG_32(OFFSET_DMA1SAD)
# define REG_DMA1DAD        REG_32(OFFSET_DMA1DAD)
# define REG_DMA2SAD        REG_32(OFFSET_DMA2SAD)
# define REG_DMA2DAD        REG_32(OFFSET_DMA2DAD)
# define REG_DMA3SAD        REG_32(OFFSET_DMA3SAD)
# define REG_DMA3DAD        REG_32(OFFSET_DMA3DAD)

#else // __GBA__

EXPORT_API uintptr_t *UGBA_RegDMA0SAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA0DAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA1SAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA1DAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA2SAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA2DAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA3SAD(void);
EXPORT_API uintptr_t *UGBA_RegDMA3DAD(void);

# define REG_DMA0SAD        (*UGBA_RegDMA0SAD())
# define REG_DMA0DAD        (*UGBA_RegDMA0DAD())
# define REG_DMA1SAD        (*UGBA_RegDMA1SAD())
# define REG_DMA1DAD        (*UGBA_RegDMA1DAD())
# define REG_DMA2SAD        (*UGBA_RegDMA2SAD())
# define REG_DMA2DAD        (*UGBA_RegDMA2DAD())
# define REG_DMA3SAD        (*UGBA_RegDMA3SAD())
# define REG_DMA3DAD        (*UGBA_RegDMA3DAD())

#endif // __GBA__

#define REG_TM0CNT_L        REG_16(OFFSET_TM0CNT_L)
#define REG_TM0CNT_H        REG_16(OFFSET_TM0CNT_H)
#define REG_TM1CNT_L        REG_16(OFFSET_TM1CNT_L)
#define REG_TM1CNT_H        REG_16(OFFSET_TM1CNT_H)
#define REG_TM2CNT_L        REG_16(OFFSET_TM2CNT_L)
#define REG_TM2CNT_H        REG_16(OFFSET_TM2CNT_H)
#define REG_TM3CNT_L        REG_16(OFFSET_TM3CNT_L)
#define REG_TM3CNT_H        REG_16(OFFSET_TM3CNT_H)
#define REG_SIODATA32       REG_32(OFFSET_SIODATA32)
#define REG_SIOMULTI0       REG_16(OFFSET_SIOMULTI0)
#define REG_SIOMULTI1       REG_16(OFFSET_SIOMULTI1)
#define REG_SIOMULTI2       REG_16(OFFSET_SIOMULTI2)
#define REG_SIOMULTI3       REG_16(OFFSET_SIOMULTI3)
#define REG_SIOCNT          REG_16(OFFSET_SIOCNT)
#define REG_SIOMLT_SEND     REG_16(OFFSET_SIOMLT_SEND)
#define REG_SIODATA8        REG_16(OFFSET_SIODATA8)
#define REG_KEYINPUT        REG_16(OFFSET_KEYINPUT)
#define REG_KEYCNT          REG_16(OFFSET_KEYCNT)
#define REG_RCNT            REG_16(OFFSET_RCNT)

#define REG_JOYCNT          REG_16(OFFSET_JOYCNT)
#define REG_JOY_RECV        REG_32(OFFSET_JOY_RECV)
#define REG_JOY_TRANS       REG_32(OFFSET_JOY_TRANS)
#define REG_JOYSTAT         REG_16(OFFSET_JOYSTAT)
#define REG_IE              REG_16(OFFSET_IE)
#define REG_IF              REG_16(OFFSET_IF)
#define REG_WAITCNT         REG_16(OFFSET_WAITCNT)
#define REG_IME             REG_16(OFFSET_IME)

// The UGBA_RegisterUpdatedOffset() function must be called in some
// circumstances for the values of the registers to be reflected immediately in
// the hardware emulation.
//
// 1) When modifying the following registers dynamically during the screen
//    redraw (in the HBL interrupt handler, for example). It isn't needed if the
//    values are static during the whole frame.
//
//        REG_BG2X_L, REG_BG2X_H, REG_BG2X
//        REG_BG2Y_L, REG_BG2Y_H, REG_BG2Y
//        REG_BG3X_L, REG_BG3X_H, REG_BG3X
//        REG_BG3Y_L, REG_BG3Y_H, REG_BG3Y
//
// 2) When starting a DMA transfer by writing to:
//
//        REG_DMA0CNT_H, REG_DMA1CNT_H, REG_DMA2CNT_H, REG_DMA3CNT_H
//
// 3) When starting a timer by writing to:
//
//        REG_TM0CNT_H, REG_TM1CNT_H, REG_TM2CNT_H, REG_TM3CNT_H
//
// 4) When IME or IE have been 0 for some time, IF isn't 0, and IME or IE are
//    set to a value that would trigger an interrupt:
//
//        REG_IE, REG_IME
//
//   Note that writing to IF doesn't work on the SDL2 port. On the GBA, writing
//   a 1 to a bit sets it to 0. On the SDL2 port, it sets the bit to 1.

#ifdef __GBA__
# define UGBA_RegisterUpdatedOffset(offset) do { (void)(offset); } while (0)
#else
EXPORT_API void UGBA_RegisterUpdatedOffset(uint32_t offset);
#endif

// Per-register fields definitions
// ===============================

// LCD I/O Registers
// -----------------

// DISPCNT

#define DISPCNT_BG_MODE_MASK            (0x7)
#define DISPCNT_BG_MODE(n)              ((n) & DISPCNT_BG_MODE_MASK) // 0 to 5

#define DISPCNT_USE_FRONTBUFFER         (0 << 4)
#define DISPCNT_USE_BACKBUFFER          (1 << 4)

#define DISPCNT_HBL_INTERVAL_FREE       (1 << 5)

#define DISPCNT_OBJ_2D_MAPPING          (0 << 6)
#define DISPCNT_OBJ_1D_MAPPING          (1 << 6)

#define DISPCNT_FORCED_BLANK            (1 << 7)

#define DISPCNT_BG0_ENABLE              (1 << 8)
#define DISPCNT_BG1_ENABLE              (1 << 9)
#define DISPCNT_BG2_ENABLE              (1 << 10)
#define DISPCNT_BG3_ENABLE              (1 << 11)
#define DISPCNT_OBJ_ENABLE              (1 << 12)

#define DISPCNT_WIN0_ENABLE             (1 << 13)
#define DISPCNT_WIN1_ENABLE             (1 << 14)
#define DISPCNT_OBJ_WIN_ENABLE          (1 << 15)

// GREENSWAP

#define GREENSWAP_ENABLE                (1 << 0)

// DISPSTAT

#define DISPSTAT_IN_VBLANK              (1 << 0)
#define DISPSTAT_IN_HBLANK              (1 << 1)
#define DISPSTAT_VCOUNT_MATCH           (1 << 2)

#define DISPSTAT_VBLANK_IRQ_ENABLE      (1 << 3)
#define DISPSTAT_HBLANK_IRQ_ENABLE      (1 << 4)
#define DISPSTAT_VCOUNT_IRQ_ENABLE      (1 << 5)

#define DISPSTAT_VCOUNT_SHIFT           (8)
#define DISPSTAT_VCOUNT(n)              ((n) << DISPSTAT_VCOUNT_SHIFT)
#define DISPSTAT_VCOUNT_MASK            (0xFF << DISPSTAT_VCOUNT_SHIFT)

// VCOUNT

#define VCOUNT_MASK                     (0xFF)

// BG0CNT, BG1CNT, BG2CNT, BG3CNT

#define BGCNT_BG_PRIORITY_MASK          (0x3)
#define BGCNT_BG_PRIORITY(n)            ((n) & BGCNT_BG_PRIORITY_MASK)

// The base address is 0x06000000 + 0x4000 * x (0x06000000 to 0x0600C000)
#define BGCNT_TILES_BLOCK_BASE(n) \
                (((((n) - MEM_VRAM_ADDR) / MEM_BG_TILES_BLOCK_SIZE) & 0x3) << 2)

#define BGCNT_MOSAIC_ENABLE             (1 << 6)

#define BGCNT_16_COLORS                 (0 << 7)
#define BGCNT_256_COLORS                (1 << 7)

// The base address is 0x06000000 + 0x800 * x (0x06000000 to 0x0600F800)
#define BGCNT_MAP_BLOCK_BASE(n) \
                (((((n) - MEM_VRAM_ADDR) / MEM_BG_MAP_BLOCK_SIZE) & 0x1F) << 8)

#define BGCNT_BG2_BG3_WRAP              (1 << 13) // For affine backgrounds

#define BGCNT_REGULAR_256x256           (0 << 14)
#define BGCNT_REGULAR_512x256           (1 << 14)
#define BGCNT_REGULAR_256x512           (2 << 14)
#define BGCNT_REGULAR_512x512           (3 << 14)

#define BGCNT_AFFINE_128x128            (0 << 14)
#define BGCNT_AFFINE_256x256            (1 << 14)
#define BGCNT_AFFINE_512x512            (2 << 14)
#define BGCNT_AFFINE_1024x1024          (3 << 14)

// WIN0H, WIN1H

#define WINH_SET(left, right)   ((((left) & 0xFF) << 8) | ((right) & 0xFF))

// WIN0V, WIN1V

#define WINV_SET(top, bottom)   ((((top) & 0xFF) << 8) | ((bottom) & 0xFF))

// WININ

#define WININ0_BG0_ENABLE               (1 << 0)
#define WININ0_BG1_ENABLE               (1 << 1)
#define WININ0_BG2_ENABLE               (1 << 2)
#define WININ0_BG3_ENABLE               (1 << 3)
#define WININ0_OBJ_ENABLE               (1 << 4)
#define WININ0_EFFECT_ENABLE            (1 << 5)
#define WININ0_ALL_ENABLE               (0x3F)

#define WININ1_BG0_ENABLE               (1 << 8)
#define WININ1_BG1_ENABLE               (1 << 9)
#define WININ1_BG2_ENABLE               (1 << 10)
#define WININ1_BG3_ENABLE               (1 << 11)
#define WININ1_OBJ_ENABLE               (1 << 12)
#define WININ1_EFFECT_ENABLE            (1 << 13)
#define WININ1_ALL_ENABLE               (0x3F00)

// WINOUT

#define WINOUT_BG0_ENABLE               (1 << 0)
#define WINOUT_BG1_ENABLE               (1 << 1)
#define WINOUT_BG2_ENABLE               (1 << 2)
#define WINOUT_BG3_ENABLE               (1 << 3)
#define WINOUT_OBJ_ENABLE               (1 << 4)
#define WINOUT_EFFECT_ENABLE            (1 << 5)
#define WINOUT_ALL_ENABLE               (0x3F)

#define WINOBJ_BG0_ENABLE               (1 << 8)
#define WINOBJ_BG1_ENABLE               (1 << 9)
#define WINOBJ_BG2_ENABLE               (1 << 10)
#define WINOBJ_BG3_ENABLE               (1 << 11)
#define WINOBJ_OBJ_ENABLE               (1 << 12)
#define WINOBJ_EFFECT_ENABLE            (1 << 13)
#define WINOBJ_ALL_ENABLE               (0x3F00)

// MOSAIC

#define MOSAIC_BG_SIZE(h, v)    (((h) & 0xF) | (((v) & 0xF) << 4))
#define MOSAIC_OBJ_SIZE(h, v)   ((((h) & 0xF) << 8) | (((v) & 0xF) << 12))

// BLDCNT

#define BLDCNT_1ST_BG0                  (1 << 0)
#define BLDCNT_1ST_BG1                  (1 << 1)
#define BLDCNT_1ST_BG2                  (1 << 2)
#define BLDCNT_1ST_BG3                  (1 << 3)
#define BLDCNT_1ST_OBJ                  (1 << 4)
#define BLDCNT_1ST_BD                   (1 << 5)

#define BLDCNT_DISABLE                  (0 << 6)
#define BLDCNT_ALPHA_BLENDING           (1 << 6) // 1ST + 2ND
#define BLDCNT_BRIGTHNESS_INCRESE       (2 << 6) // 1ST
#define BLDCNT_BRIGTHNESS_DECREASE      (3 << 6) // 1ST

#define BLDCNT_2ND_BG0                  (1 << 8)
#define BLDCNT_2ND_BG1                  (1 << 9)
#define BLDCNT_2ND_BG2                  (1 << 10)
#define BLDCNT_2ND_BG3                  (1 << 11)
#define BLDCNT_2ND_OBJ                  (1 << 12)
#define BLDCNT_2ND_BD                   (1 << 13)

// BLDALPHA

#define BLDALPHA_EVA(v)                 ((v) & 0x1F)
#define BLDALPHA_EVB(v)                 (((v) & 0x1F) << 8)

// BLDY

#define BLDY_EVY(v)                     ((v) & 0x1F)

// Sound Registers
// ---------------

// SOUND1CNT_L (NR10)

#define SOUND1CNT_L_SWEEP_SHIFT_SET(n)  ((n) & 0x7) // R/W
#define SOUND1CNT_L_SWEEP_SHIFT_GET(n)  ((n) & 0x7)

#define SOUND1CNT_L_SWEEP_DIR_INC       (0 << 3) // R/W
#define SOUND1CNT_L_SWEEP_DIR_DEC       (1 << 3)

#define SOUND1CNT_L_SWEEP_TIME_SET(n)   (((n) & 0x7) << 4) // R/W
#define SOUND1CNT_L_SWEEP_TIME_GET(n)   (((n) >> 4) & 0x7)

// SOUND1CNT_H (NR11, NR12)

#define SOUND1CNT_H_LENGTH_SET(n)       ((n) & 0x3F) // WO
#define SOUND1CNT_H_LENGTH_GET(n)       ((n) & 0x3F) // Internal use

#define SOUND1CNT_H_WAVE_DUTY_GET(n)    (((n) >> 6) & 0x3) // R/W
#define SOUND1CNT_H_WAVE_DUTY_12        (0 << 6)
#define SOUND1CNT_H_WAVE_DUTY_25        (1 << 6)
#define SOUND1CNT_H_WAVE_DUTY_50        (2 << 6)
#define SOUND1CNT_H_WAVE_DUTY_75        (3 << 6)

#define SOUND1CNT_H_ENV_STEP_TIME_SET(n) (((n) & 0x7) << 8) // R/W
#define SOUND1CNT_H_ENV_STEP_TIME_GET(n) (((n) >> 8) & 0x7)

#define SOUND1CNT_H_ENV_DIR_DEC         (0 << 11) // R/W
#define SOUND1CNT_H_ENV_DIR_INC         (1 << 11)

#define SOUND1CNT_H_ENV_VOLUME_SET(n)   (((n) & 0xF) << 12) // R/W
#define SOUND1CNT_H_ENV_VOLUME_GET(n)   (((n) >> 12) & 0xF)

// SOUND1CNT_X (NR13, NR14)

#define SOUND1CNT_X_FREQUENCY_SET(n)    ((n) & 0x7FF) // WO
#define SOUND1CNT_X_FREQUENCY_GET(n)    ((n) & 0x7FF) // Internal use

#define SOUND1CNT_X_ONE_SHOT            (1 << 14) // R/W

#define SOUND1CNT_X_RESTART             (1 << 15) // WO

// SOUND2CNT_L (NR21, NR22)

#define SOUND2CNT_L_LENGTH_SET(n)       ((n) & 0x3F) // WO
#define SOUND2CNT_L_LENGTH_GET(n)       ((n) & 0x3F) // Internal use

#define SOUND2CNT_L_WAVE_DUTY_GET(n)    (((n) >> 6) & 0x3) // R/W
#define SOUND2CNT_L_WAVE_DUTY_12        (0 << 6)
#define SOUND2CNT_L_WAVE_DUTY_25        (1 << 6)
#define SOUND2CNT_L_WAVE_DUTY_50        (2 << 6)
#define SOUND2CNT_L_WAVE_DUTY_75        (3 << 6)

#define SOUND2CNT_L_ENV_STEP_TIME_SET(n) (((n) & 0x7) << 8) // R/W
#define SOUND2CNT_L_ENV_STEP_TIME_GET(n) (((n) >> 8) & 0x7)

#define SOUND2CNT_L_ENV_DIR_DEC         (0 << 11) // R/W
#define SOUND2CNT_L_ENV_DIR_INC         (1 << 11)

#define SOUND2CNT_L_ENV_VOLUME_SET(n)   (((n) & 0xF) << 12) // R/W
#define SOUND2CNT_L_ENV_VOLUME_GET(n)   (((n) >> 12) & 0xF)

// SOUND2CNT_H (NR23, NR24)

#define SOUND2CNT_H_FREQUENCY_SET(n)    ((n) & 0x7FF) // WO
#define SOUND2CNT_H_FREQUENCY_GET(n)    ((n) & 0x7FF) // Internal use

#define SOUND2CNT_H_ONE_SHOT            (1 << 14) // R/W

#define SOUND2CNT_H_RESTART             (1 << 15) // WO

// SOUND3CNT_L (NR30)

#define SOUND3CNT_L_SIZE_32             (0 << 5)
#define SOUND3CNT_L_SIZE_64             (1 << 5)

#define SOUND3CNT_L_BANK_SET(n)         (((n) & 1) << 6)
#define SOUND3CNT_L_BANK_GET(n)         (((n) >> 6) & 1)

#define SOUND3CNT_L_ENABLE              (1 << 7)
#define SOUND3CNT_L_DISABLE             (0 << 7)

// SOUND3CNT_H (NR31, NR32)

#define SOUND3CNT_H_LENGTH_SET(n)       ((n) & 0xFF) // R/W
#define SOUND3CNT_H_LENGTH_GET(n)       ((n) & 0xFF)

#define SOUND3CNT_H_VOLUME_0            (0x0 << 13) // R/W
#define SOUND3CNT_H_VOLUME_25           (0x3 << 13)
#define SOUND3CNT_H_VOLUME_50           (0x2 << 13)
#define SOUND3CNT_H_VOLUME_75           (0x4 << 13)
#define SOUND3CNT_H_VOLUME_100          (0x1 << 13)
#define SOUND3CNT_H_VOLUME_MASK         (0x7 << 13)

// SOUND3CNT_X (NR33, NR34)

#define SOUND3CNT_X_SAMPLE_RATE_SET(n)  ((n) & 0x7FF) // WO
#define SOUND3CNT_X_SAMPLE_RATE_GET(n)  ((n) & 0x7FF) // Internal use

#define SOUND3CNT_X_ONE_SHOT            (1 << 14) // R/W

#define SOUND3CNT_X_RESTART             (1 << 15) // WO

// SOUND4CNT_L (NR41, NR42)

#define SOUND4CNT_L_LENGTH_SET(n)       ((n) & 0x3F) // WO
#define SOUND4CNT_L_LENGTH_GET(n)       ((n) & 0x3F) // Internal use

#define SOUND4CNT_L_ENV_STEP_TIME_SET(n) (((n) & 0x7) << 8) // R/W
#define SOUND4CNT_L_ENV_STEP_TIME_GET(n) (((n) >> 8) & 0x7)

#define SOUND4CNT_L_ENV_DIR_DEC         (0 << 11) // R/W
#define SOUND4CNT_L_ENV_DIR_INC         (1 << 11)

#define SOUND4CNT_L_ENV_VOLUME_SET(n)   (((n) & 0xF) << 12) // R/W
#define SOUND4CNT_L_ENV_VOLUME_GET(n)   (((n) >> 12) & 0xF)

// SOUND4CNT_H (NR43, NR44)

#define SOUND4CNT_H_DIV_RATIO_SET(n)    ((n) & 0x7) // R/W
#define SOUND4CNT_H_DIV_RATIO_GET(n)    ((n) & 0x7)

#define SOUND4CNT_H_WIDTH_15_BITS       (0 << 3) // R/W
#define SOUND4CNT_H_WIDTH_7_BITS        (1 << 3)

#define SOUND4CNT_H_FREQUENCY_SET(n)    (((n) & 0xF) << 4) // R/W
#define SOUND4CNT_H_FREQUENCY_GET(n)    (((n) >> 4) & 0xF)

#define SOUND4CNT_H_ONE_SHOT            (1 << 14) // R/W

#define SOUND4CNT_H_RESTART             (1 << 15) // WO

// SOUNDCNT_L

#define SOUNDCNT_L_PSG_VOL_RIGHT_SET(v) (((v) & 0x7) << 0)
#define SOUNDCNT_L_PSG_VOL_RIGHT_GET(v) (((v) >> 0) & 0x7)
#define SOUNDCNT_L_PSG_VOL_LEFT_SET(v)  (((v) & 0x7) << 4)
#define SOUNDCNT_L_PSG_VOL_LEFT_GET(v)  (((v) >> 4) & 0x7)

#define SOUNDCNT_L_PSG_1_ENABLE_RIGHT   (1 << 8)
#define SOUNDCNT_L_PSG_2_ENABLE_RIGHT   (1 << 9)
#define SOUNDCNT_L_PSG_3_ENABLE_RIGHT   (1 << 10)
#define SOUNDCNT_L_PSG_4_ENABLE_RIGHT   (1 << 11)

#define SOUNDCNT_L_PSG_1_ENABLE_LEFT    (1 << 12)
#define SOUNDCNT_L_PSG_2_ENABLE_LEFT    (1 << 13)
#define SOUNDCNT_L_PSG_3_ENABLE_LEFT    (1 << 14)
#define SOUNDCNT_L_PSG_4_ENABLE_LEFT    (1 << 15)

// SOUNDCNT_H

#define SOUNDCNT_H_PSG_VOLUME_25        (0 << 0)
#define SOUNDCNT_H_PSG_VOLUME_50        (1 << 0)
#define SOUNDCNT_H_PSG_VOLUME_100       (2 << 0)
#define SOUNDCNT_H_PSG_VOLUME_MASK      (3 << 0)

#define SOUNDCNT_H_DMA_A_VOLUME_50      (0 << 2)
#define SOUNDCNT_H_DMA_A_VOLUME_100     (1 << 2)

#define SOUNDCNT_H_DMA_B_VOLUME_50      (0 << 3)
#define SOUNDCNT_H_DMA_B_VOLUME_100     (1 << 3)

#define SOUNDCNT_H_DMA_A_ENABLE_RIGHT   (1 << 8)
#define SOUNDCNT_H_DMA_A_ENABLE_LEFT    (1 << 9)

#define SOUNDCNT_H_DMA_A_TIMER0         (0 << 10)
#define SOUNDCNT_H_DMA_A_TIMER1         (1 << 10)

#define SOUNDCNT_H_DMA_A_RESET          (1 << 11)

#define SOUNDCNT_H_DMA_B_ENABLE_RIGHT   (1 << 12)
#define SOUNDCNT_H_DMA_B_ENABLE_LEFT    (1 << 13)

#define SOUNDCNT_H_DMA_B_TIMER0         (0 << 14)
#define SOUNDCNT_H_DMA_B_TIMER1         (1 << 14)

#define SOUNDCNT_H_DMA_B_RESET          (1 << 15)

// SOUNDCNT_X

#define SOUNDCNT_X_PSG_1_IS_ON          (1 << 0) // Read-only flags
#define SOUNDCNT_X_PSG_2_IS_ON          (1 << 1)
#define SOUNDCNT_X_PSG_3_IS_ON          (1 << 2)
#define SOUNDCNT_X_PSG_4_IS_ON          (1 << 3)

#define SOUNDCNT_X_MASTER_ENABLE        (1 << 7)
#define SOUNDCNT_X_MASTER_DISABLE       (0 << 7)

// SOUNDBIAS

#define SOUNDBIAS_BIAS_LEVEL_SET(v)     (((v) & 0x1FF) << 1)
#define SOUNDBIAS_BIAS_LEVEL_GET(v)     (((v) >> 1) & 0x1FF)
#define SOUNDBIAS_BIAS_MASK             (0x1FF << 1)

#define SOUNDBIAS_SAMPLE_RATE_32KHZ     (0 << 14) // Best for DMA channels A,B
#define SOUNDBIAS_SAMPLE_RATE_65KHZ     (1 << 14)
#define SOUNDBIAS_SAMPLE_RATE_131KHZ    (2 << 14)
#define SOUNDBIAS_SAMPLE_RATE_262KHZ    (3 << 14) // Best for PSG channels 1-4

// DMA Transfer Channels
// ---------------------

// DMA0CNT_H, DMA1CNT_H, DMA2CNT_H, DMA3CNT_H

#define DMACNT_DST_INCREMENT            (0 << 5)
#define DMACNT_DST_DECREMENT            (1 << 5)
#define DMACNT_DST_FIXED                (2 << 5)
#define DMACNT_DST_RELOAD               (3 << 5)

#define DMACNT_SRC_INCREMENT            (0 << 7)
#define DMACNT_SRC_DECREMENT            (1 << 7)
#define DMACNT_SRC_FIXED                (2 << 7)

#define DMACNT_REPEAT_ON                (1 << 9)

#define DMACNT_TRANSFER_16_BITS         (0 << 10)
#define DMACNT_TRANSFER_32_BITS         (1 << 10)

#define DMACNT_GAME_PAK_NORMAL          (0 << 11) // DMA3 only
#define DMACNT_GAME_PAK_DRQ             (1 << 11) // DMA3 only

#define DMACNT_START_NOW                (0 << 12)
#define DMACNT_START_VBLANK             (1 << 12)
#define DMACNT_START_HBLANK             (2 << 12)
#define DMACNT_START_SPECIAL            (3 << 12) // DMA1, DMA2, DMA3

#define DMACNT_IRQ_ENABLE               (1 << 14)

#define DMACNT_DMA_ENABLE               (1 << 15)

// Timer Registers
// ---------------

// TM0CNT_H, TM1CNT_H, TM2CNT_H, TM3CNT_H

#define TMCNT_PRESCALER_F_DIV_1         (0 << 0)
#define TMCNT_PRESCALER_F_DIV_64        (1 << 0)
#define TMCNT_PRESCALER_F_DIV_256       (2 << 0)
#define TMCNT_PRESCALER_F_DIV_1024      (3 << 0)

#define TMCNT_STANDALONE                (0 << 2)
#define TMCNT_CASCADE                   (1 << 2) // Not used in TM0CNT_H

#define TMCNT_IRQ_DISABLE               (0 << 6)
#define TMCNT_IRQ_ENABLE                (1 << 6)

#define TMCNT_STOP                      (0 << 7)
#define TMCNT_START                     (1 << 7)

// Serial Communication (1)
// ------------------------

#if 0
  4000120h  4    R/W  SIODATA32 SIO Data (Normal-32bit Mode; shared with below)
  4000120h  2    R/W  SIOMULTI0 SIO Data 0 (Parent)    (Multi-Player Mode)
  4000122h  2    R/W  SIOMULTI1 SIO Data 1 (1st Child) (Multi-Player Mode)
  4000124h  2    R/W  SIOMULTI2 SIO Data 2 (2nd Child) (Multi-Player Mode)
  4000126h  2    R/W  SIOMULTI3 SIO Data 3 (3rd Child) (Multi-Player Mode)
  4000128h  2    R/W  SIOCNT    SIO Control Register
  400012Ah  2    R/W  SIOMLT_SEND SIO Data (Local of MultiPlayer; shared below)
  400012Ah  2    R/W  SIODATA8  SIO Data (Normal-8bit and UART Mode)
#endif

// Keypad Input
// ------------

//  KEYINPUT

#define KEY_A                           (1 << 0)
#define KEY_B                           (1 << 1)
#define KEY_SELECT                      (1 << 2)
#define KEY_START                       (1 << 3)
#define KEY_RIGHT                       (1 << 4)
#define KEY_LEFT                        (1 << 5)
#define KEY_UP                          (1 << 6)
#define KEY_DOWN                        (1 << 7)
#define KEY_R                           (1 << 8)
#define KEY_L                           (1 << 9)

// KEYCNT (Use the definitions from KEYINPUT too)

#define KEYCNT_IRQ_ENABLE               (1 << 14)
#define KEYCNT_IRQ_CONDITION_OR         (0 << 15)
#define KEYCNT_IRQ_CONDITION_AND        (1 << 15)

// Serial Communication (2)
// ------------------------

#if 0
  4000134h  2    R/W  RCNT      SIO Mode Select/General Purpose Data
  4000140h  2    R/W  JOYCNT    SIO JOY Bus Control
  4000150h  4    R/W  JOY_RECV  SIO JOY Bus Receive Data
  4000154h  4    R/W  JOY_TRANS SIO JOY Bus Transmit Data
  4000158h  2    R/?  JOYSTAT   SIO JOY Bus Receive Status
#endif

// Interrupt Control
// -----------------

// IE, IF

#define IRQF_VBLANK                     (1 << 0)
#define IRQF_HBLANK                     (1 << 1)
#define IRQF_VCOUNT                     (1 << 2)
#define IRQF_TIMER0                     (1 << 3)
#define IRQF_TIMER1                     (1 << 4)
#define IRQF_TIMER2                     (1 << 5)
#define IRQF_TIMER3                     (1 << 6)
#define IRQF_SERIAL                     (1 << 7)
#define IRQF_DMA0                       (1 << 8)
#define IRQF_DMA1                       (1 << 9)
#define IRQF_DMA2                       (1 << 10)
#define IRQF_DMA3                       (1 << 11)
#define IRQF_KEYPAD                     (1 << 12)
#define IRQF_GAMEPAK                    (1 << 13)

// WAITCNT

#define WAITCNT_SRAM_4                  (0 << 0)
#define WAITCNT_SRAM_3                  (1 << 0)
#define WAITCNT_SRAM_2                  (2 << 0)
#define WAITCNT_SRAM_8                  (3 << 0)

#define WAITCNT_WS0_1ST_4               (0 << 2)
#define WAITCNT_WS0_1ST_3               (1 << 2)
#define WAITCNT_WS0_1ST_2               (2 << 2)
#define WAITCNT_WS0_1ST_8               (3 << 2)

#define WAITCNT_WS0_2ST_2               (0 << 4)
#define WAITCNT_WS0_2ST_1               (1 << 4)

#define WAITCNT_WS1_1ST_4               (0 << 5)
#define WAITCNT_WS1_1ST_3               (1 << 5)
#define WAITCNT_WS1_1ST_2               (2 << 5)
#define WAITCNT_WS1_1ST_8               (3 << 5)

#define WAITCNT_WS1_2ST_4               (0 << 7)
#define WAITCNT_WS1_2ST_1               (1 << 7)

#define WAITCNT_WS2_1ST_4               (0 << 8)
#define WAITCNT_WS2_1ST_3               (1 << 8)
#define WAITCNT_WS2_1ST_2               (2 << 8)
#define WAITCNT_WS2_1ST_8               (3 << 8)

#define WAITCNT_WS2_2ST_8               (0 << 10)
#define WAITCNT_WS2_2ST_1               (1 << 10)

#define WAITCNT_PHI_DISABLE             (0 << 11)
#define WAITCNT_PHI_4MHZ                (1 << 11)
#define WAITCNT_PHI_8MHZ                (2 << 11)
#define WAITCNT_PHI_16MHZ               (3 << 11)

#define WAITCNT_PREFETCH_DISABLE        (0 << 14)
#define WAITCNT_PREFETCH_ENABLE         (1 << 14)

#define WAITCNT_OPTIMIZED                                           \
                (WAITCNT_SRAM_8 |                                   \
                 WAITCNT_WS0_1ST_3 | WAITCNT_WS0_2ST_1 |            \
                 WAITCNT_WS1_1ST_4 | WAITCNT_WS1_2ST_4 |            \
                 WAITCNT_WS2_1ST_8 | WAITCNT_WS2_2ST_8 |            \
                 WAITCNT_PHI_DISABLE | WAITCNT_PREFETCH_ENABLE)

#define WAITCNT_SCSD                                                \
                (WAITCNT_SRAM_8 |                                   \
                 WAITCNT_WS0_1ST_4 | WAITCNT_WS0_2ST_2 |            \
                 WAITCNT_WS1_1ST_4 | WAITCNT_WS1_2ST_4 |            \
                 WAITCNT_WS2_1ST_8 | WAITCNT_WS2_2ST_8 |            \
                 WAITCNT_PHI_DISABLE | WAITCNT_PREFETCH_ENABLE)

// The value used by most games is 0x4317 (WS0 3.1), but the SuperCard SD needs
// 0x4303 (WS0 4.2). Use the SCSD one, as it is more conservative.
#define WAITCNT_DEFAULT_STARTUP     WAITCNT_SCSD

// IME

#define IME_DISABLE                     (0 << 0)
#define IME_ENABLE                      (1 << 0)

#endif // HARDWARE_H__
