+#ifndef VDP_H_
+#define VDP_H_
+
+#include <stdint.h>
+
+#define VDP_PORT_DATA *((volatile uint16_t*)0xc00000)
+#define VDP_PORT_DATA32 *((volatile uint32_t*)0xc00000)
+#define VDP_PORT_CTL *((volatile uint16_t*)0xc00004)
+#define VDP_PORT_CTL32 *((volatile uint32_t*)0xc00004)
+#define VDP_PORT_HVCNT *((volatile uint16_t*)0xc00008)
+#define VDP_PORT_PSG *((volatile uint16_t*)0xc00010)
+
+#define VDP_PORT_STATUS *((volatile uint16_t*)0xc00004)
+
+enum {
+ VDP_REG_MODE1 = 0,
+ VDP_REG_MODE2 = 1,
+ VDP_REG_NAMETAB_A = 2,
+ VDP_REG_NAMETAB_WIN = 3,
+ VDP_REG_NAMETAB_B = 4,
+ VDP_REG_SPRITE_TAB = 5,
+ VDP_REG_BGCOL = 7,
+ VDP_REG_HINTR = 10,
+ VDP_REG_MODE3 = 11,
+ VDP_REG_MODE4 = 12,
+ VDP_REG_SCROLL_TAB = 13,
+ VDP_REG_AUTOINC = 15,
+ VDP_REG_SCROLL_SIZE = 16,
+ VDP_REG_WIN_XPOS = 17,
+ VDP_REG_WIN_YPOS = 18,
+ VDP_REG_DMA_LEN_LOW = 19,
+ VDP_REG_DMA_LEN_HIGH = 20,
+ VDP_REG_DMA_SRC_LOW = 21,
+ VDP_REG_DMA_SRC_MID = 22,
+ VDP_REG_DMA_SRC_HIGH = 23,
+
+ VDP_NUM_REGS
+};
+
+uint16_t vdp_reg_shadow[VDP_NUM_REGS];
+
+/* access VDP memory */
+enum { VDP_MEM_READ, VDP_MEM_WRITE };
+enum {
+ VDP_MEM_VRAM = 0,
+ VDP_MEM_CRAM = 0xa, /* CD5->CD0: 0 0 r 0 w 0 */
+ VDP_MEM_VSRAM = 4 /* CD5->CD0: 0 0 0 1 0 0 */
+};
+
+enum {
+ VDP_PLANE_A,
+ VDP_PLANE_B,
+ VDP_PLANE_WIN
+};
+
+enum {
+ VDP_TILE_LOW_PRIO = 0,
+ VDP_TILE_HFLIP = 0x0800,
+ VDP_TILE_VFLIP = 0x1000,
+ VDP_TILE_HIGH_PRIO = 0x8000
+};
+
+enum {
+ VDP_STAT_PAL = 0x0001,
+ VDP_STAT_DMABUSY = 0x0002,
+ VDP_STAT_HBLANK = 0x0004,
+ VDP_STAT_VBLANK = 0x0008,
+ VDP_STAT_ODDFIELD = 0x0010,
+ VDP_STAT_SPRITE_COL = 0x0020,
+ VDP_STAT_SPRITE_OVF = 0x0040,
+ VDP_STAT_VINTR = 0x0080,
+ VDP_STAT_FIFO_FULL = 0x0100,
+ VDP_STAT_FIFO_EMPTY = 0x0200
+};
+
+
+static inline void vdp_setup_access(uint16_t addr, int rw, int memid)
+{
+ uint32_t type;
+ if(rw == VDP_MEM_WRITE) {
+ type = (memid & 7) | 1;
+ } else {
+ type = memid & 0xc;
+ }
+
+ VDP_PORT_CTL32 = (((uint32_t)addr & 0x3fff) << 16) | ((addr >> 14) & 3) |
+ ((type << 2) & 0xf0) | (type << 30);
+}
+
+
+/* mode register 1 */
+enum {
+ VDP_MODE1_BASE = 0x4,
+ VDP_MODE1_HVCNT = 0x2,
+ VDP_MODE1_HINTR = 0x10
+};
+
+/* mode register 2 */
+enum {
+ VDP_MODE2_BASE = 0x4,
+ VDP_MODE2_V30CELL = 0x8,
+ VDP_MODE2_DMA = 0x10,
+ VDP_MODE2_VINTR = 0x20,
+ VDP_MODE2_DISP = 0x40
+};
+
+/* mode register 3 */
+enum {
+ VDP_MODE3_BASE = 0,
+ VDP_MODE3_HSCROLL_CELL = 2,
+ VDP_MODE3_HSCROLL_LINE = 3,
+ VDP_MODE3_VSCROLL_2CELL = 4,
+ VDP_MODE3_EXTINTR = 8
+};
+
+/* mode register 4 */
+enum {
+ VDP_MODE4_BASE = 0,
+ VDP_MODE4_H40CELL = 0x81,
+ VDP_MODE4_ILACE = 2,
+ VDP_MODE4_ILACE_2XRES = 6,
+ VDP_MODE4_SH = 8 /* shadow/highlight enable */
+};
+
+/* scroll size register */
+enum {
+ VDP_SCROLL_H32 = 0,
+ VDP_SCROLL_H64 = 1,
+ VDP_SCROLL_H128 = 3,
+ VDP_SCROLL_V32 = 0,
+ VDP_SCROLL_V64 = 0x10,
+ VDP_SCROLL_V128 = 0x30
+};
+
+/* window X/Y position register */
+enum {
+ VDP_WIN_LEFT = 0,
+ VDP_WIN_UP = 0,
+ VDP_WIN_RIGHT = 0x80,
+ VDP_WIN_DOWN = 0x80,
+ VDP_WIN_POSMASK = 0x1f
+};
+
+#define VDP_PACK_RGB(r, g, b) \
+ ((((uint16_t)(r) & 7) << 1) | (((uint16_t)(g) & 7) << 5) | (((uint16_t)(b) & 7) << 9))
+
+
+static inline void vdp_setreg(int reg, uint8_t value)
+{
+ vdp_reg_shadow[reg] = value;
+ VDP_PORT_CTL = (uint16_t)value | (reg << 8) | (uint16_t)0x8000;
+}
+
+static inline uint16_t vdp_getreg(int reg)
+{
+ return vdp_reg_shadow[reg];
+}
+
+static inline uint16_t vdp_status(void)
+{
+ return VDP_PORT_CTL;
+}
+
+static inline void vdp_set_bgcolor(int palidx, int colidx)
+{
+ vdp_setreg(VDP_REG_BGCOL, (colidx & 0xf) | (palidx << 4));
+}
+
+static inline void vdp_set_autoinc(int stride)
+{
+ vdp_setreg(VDP_REG_AUTOINC, stride);
+}
+
+static inline void vdp_begin_palette(int pidx, int cidx)
+{
+ uint16_t paddr = (pidx << 5) | (cidx << 1);
+ vdp_setup_access(paddr, VDP_MEM_WRITE, VDP_MEM_CRAM);
+}
+
+static inline void vdp_set_pal_entry(int pidx, int cidx, int r, int g, int b)
+{
+ vdp_begin_palette(pidx, cidx);
+ VDP_PORT_DATA = VDP_PACK_RGB(r, g, b);
+}
+
+static inline void vdp_enable_vintr(void)
+{
+ vdp_setreg(VDP_REG_MODE2, vdp_getreg(VDP_REG_MODE2) | VDP_MODE2_VINTR);
+}
+
+static inline void vdp_disable_vintr(void)
+{
+ vdp_setreg(VDP_REG_MODE2, vdp_getreg(VDP_REG_MODE2) & ~VDP_MODE2_VINTR);
+}
+
+static inline void vdp_enable_hintr(int counter)
+{
+ vdp_setreg(VDP_REG_HINTR, counter);
+ vdp_setreg(VDP_REG_MODE1, vdp_getreg(VDP_REG_MODE1) | VDP_MODE1_HINTR);
+}
+
+static inline void vdp_disable_hintr(void)
+{
+ vdp_setreg(VDP_REG_MODE1, vdp_getreg(VDP_REG_MODE1) & ~VDP_MODE1_HINTR);
+}
+
+static inline void vdp_set_nametab_addr(int plane, uint16_t addr)
+{
+ switch(plane) {
+ case VDP_PLANE_A:
+ vdp_setreg(VDP_REG_NAMETAB_A, (addr >> 10) & 0x38);
+ break;
+ case VDP_PLANE_B:
+ vdp_setreg(VDP_REG_NAMETAB_B, (addr >> 13) & 7);
+ break;
+ case VDP_PLANE_WIN:
+ vdp_setreg(VDP_REG_NAMETAB_WIN, (addr >> 10) & 0x7e);
+ default:
+ break;
+ }
+}
+
+static inline uint16_t vdp_nametab_addr(int idx)
+{
+ return (uint16_t)idx << 13;
+}
+
+static inline void vdp_set_nametab_idx(int plane, int idx)
+{
+ vdp_set_nametab_addr(plane, vdp_nametab_addr(idx));
+}
+
+static inline void vdp_disable_layer(int plane)
+{
+ switch(plane) {
+ case VDP_PLANE_A:
+ vdp_setreg(VDP_REG_NAMETAB_A, 0x40);
+ break;
+ case VDP_PLANE_B:
+ vdp_setreg(VDP_REG_NAMETAB_B, 0x08);
+ break;
+ case VDP_PLANE_WIN:
+ vdp_setreg(VDP_REG_NAMETAB_WIN, 0x40);
+ default:
+ break;
+ }
+}
+
+static inline uint16_t vdp_nametab_entry(int tileidx, int palidx, uint16_t flags)
+{
+ return tileidx | (((uint16_t)palidx & 0x3) << 13) | flags;
+}
+
+static inline void vdp_set_sprite_table(uint16_t addr)
+{
+ vdp_setreg(VDP_REG_SPRITE_TAB, addr >> 9);
+}
+
+static inline void vdp_set_scroll_table(uint16_t addr)
+{
+ vdp_setreg(VDP_REG_SCROLL_TAB, addr >> 10);
+}
+
+static inline void vdp_wait_vblank_start(void)
+{
+ while(!(VDP_PORT_STATUS & VDP_STAT_VBLANK));
+}
+
+static inline void vdp_wait_vblank_end(void)
+{
+ while(VDP_PORT_STATUS & VDP_STAT_VBLANK);
+}
+
+static inline void vdp_wait_vblank(void)
+{
+ vdp_wait_vblank_start();
+ vdp_wait_vblank_end();
+}
+
+void vdp_init(void);
+
+#endif /* VDP_H_ */