# HG changeset patch
# User John Tsiombikas <nuclear@member.fsf.org>
# Date 1486796202 -7200
# Node ID 862f8a034caeec49a98fc9f350eb04f6948733d5
# Parent  f99eab59e7dce5142906048e2b262eda28b4a210
expanding the megadrive code

diff -r f99eab59e7dc -r 862f8a034cae Makefile
--- a/Makefile	Wed Feb 01 14:40:19 2017 +0200
+++ b/Makefile	Sat Feb 11 08:56:42 2017 +0200
@@ -1,4 +1,4 @@
-csrc = $(wildcard src/*.c)
+csrc = $(wildcard src/*.c) $(wildcard src/libc/*.c)
 asrc = $(wildcard src/*.s)
 aSsrc = $(wildcard src/*.S)
 obj = $(asrc:.s=.o) $(aSsrc:.S=.o) $(csrc:.c=.o)
@@ -9,7 +9,8 @@
 
 warn = -pedantic -Wall
 dbg = -g
-def = -DGAMENAME=\"testgame\" -DVERSTR=\"01\"
+def = -DGAMENAME=\"testgame\" -DVERSTR=\"01\" -D__NO_CTYPE
+inc = -Isrc/libc
 
 tool_prefix = m68k-linux-gnu-
 
@@ -18,16 +19,17 @@
 LD = $(tool_prefix)ld
 OBJCOPY = $(tool_prefix)objcopy
 
-CFLAGS = -m68000 -fno-builtin $(warn) $(dbg) $(opt) $(def)
+CFLAGS = -m68000 -ffreestanding -fno-builtin $(warn) $(dbg) $(opt) $(def) $(inc)
 CPPFLAGS = $(def)
 ASFLAGS = -m68000
-LDFLAGS = -T megadrive.ldscript -print-gc-sections
+LDFLAGS = -T megadrive.ldscript -print-gc-sections \
+		  -L/usr/lib/gcc-cross/m68k-linux-gnu/6 -lgcc
 
 $(bin): $(elf)
 	$(OBJCOPY) -O binary $< $@
 
 $(elf): $(obj)
-	$(LD) -o $@ $(LDFLAGS) $(obj) -Map link.map
+	$(LD) -o $@ $(obj) -Map link.map $(LDFLAGS)
 
 .PHONY: clean
 clean:
diff -r f99eab59e7dc -r 862f8a034cae src/io.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/io.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,11 @@
+#include "io.h"
+
+int mdg_version(void)
+{
+	return IO_VERSION & IO_VER_MASK;
+}
+
+int mdg_ispal(void)
+{
+	return IO_VERSION & IO_VER_VMOD;
+}
diff -r f99eab59e7dc -r 862f8a034cae src/io.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/io.h	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,16 @@
+#ifndef IO_H_
+#define IO_H_
+
+#define IO_VERSION		(*(unsigned char*)0xa10001)
+
+#define IO_VER_MASK		0x0f
+#define IO_VER_DISK		0x20
+#define IO_VER_VMOD		0x40
+#define IO_VER_MODE_BIT	0x80
+
+#define IO_Z80_MEM_START	0xa00000
+
+int mdg_version(void);
+int mdg_ispal(void);
+
+#endif	/* IO_H_ */
diff -r f99eab59e7dc -r 862f8a034cae src/libc/ctype.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libc/ctype.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,60 @@
+int isalpha(int c);
+int isdigit(int c);
+int islower(int c);
+int isupper(int c);
+
+
+int isalnum(int c)
+{
+	return isalpha(c) || isdigit(c);
+}
+
+int isalpha(int c)
+{
+	return isupper(c) || islower(c);
+}
+
+int isblank(int c)
+{
+	return c == ' ' || c == '\t';
+}
+
+int isdigit(int c)
+{
+	return c >= '0' && c <= '9';
+}
+
+int isupper(int c)
+{
+	return c >= 'A' && c <= 'Z';
+}
+
+int islower(int c)
+{
+	return c >= 'a' && c <= 'z';
+}
+
+int isgraph(int c)
+{
+	return c > ' ' && c <= '~';
+}
+
+int isprint(int c)
+{
+	return isgraph(c) || c == ' ';
+}
+
+int isspace(int c)
+{
+	return isblank(c) || c == '\f' || c == '\n' || c == '\r' || c == '\v';
+}
+
+int toupper(int c)
+{
+	return islower(c) ? (c + ('A' - 'a')) : c;
+}
+
+int tolower(int c)
+{
+	return isupper(c) ? (c + ('A' - 'a')) : c;
+}
diff -r f99eab59e7dc -r 862f8a034cae src/libc/printf.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libc/printf.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,323 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+static void bwrite(char *buf, size_t buf_sz, char *str, int sz);
+static int intern_printf(char *buf, size_t sz, const char *fmt, va_list ap);
+static void itoa(int val, char *buf, int base);
+static void utoa(unsigned int val, char *buf, int base);
+
+
+int putchar(int c)
+{
+	/* TODO */
+	return c;
+}
+
+int puts(const char *s)
+{
+	while(*s) {
+		putchar(*s++);
+	}
+	putchar('\n');
+	return 0;
+}
+
+/* -- printf and friends -- */
+
+static char *convc = "dioxXucsfeEgGpn%";
+
+#define IS_CONV(c)	strchr(convc, c)
+
+int printf(const char *fmt, ...)
+{
+	int res;
+	va_list ap;
+
+	va_start(ap, fmt);
+	res = intern_printf(0, 0, fmt, ap);
+	va_end(ap);
+	return res;
+}
+
+int vprintf(const char *fmt, va_list ap)
+{
+	return intern_printf(0, 0, fmt, ap);
+}
+
+int sprintf(char *buf, const char *fmt, ...)
+{
+	int res;
+	va_list ap;
+
+	va_start(ap, fmt);
+	res = intern_printf(buf, 0, fmt, ap);
+	va_end(ap);
+	return res;
+}
+
+int vsprintf(char *buf, const char *fmt, va_list ap)
+{
+	return intern_printf(buf, 0, fmt, ap);
+}
+
+int snprintf(char *buf, size_t sz, const char *fmt, ...)
+{
+	int res;
+	va_list ap;
+
+	va_start(ap, fmt);
+	res = intern_printf(buf, sz, fmt, ap);
+	va_end(ap);
+	return res;
+}
+
+int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap)
+{
+	return intern_printf(buf, sz, fmt, ap);
+}
+
+
+/* intern_printf provides all the functionality needed by all the printf
+ * variants.
+ * - buf: optional buffer onto which the formatted results are written. If null
+ *   then the output goes to the terminal through putchar calls. This is used
+ *   by the (v)sprintf variants which write to an array of char.
+ * - sz: optional maximum size of the output, 0 means unlimited. This is used
+ *   by the (v)snprintf variants to avoid buffer overflows.
+ * The rest are obvious, format string and variable argument list.
+ */
+
+#define BUF(x)	((x) ? (x) + cnum : (x))
+#define SZ(x)	((x) ? (x) - cnum : (x))
+
+static int intern_printf(char *buf, size_t sz, const char *fmt, va_list ap)
+{
+	char conv_buf[32];
+	char *str;
+	int i, slen;
+	const char *fstart = 0;
+
+	/* state */
+	int cnum = 0;
+	int base = 10;
+	int alt = 0;
+	int fwidth = 0;
+	int padc = ' ';
+	int sign = 0;
+	int left_align = 0;	/* not implemented yet */
+	int hex_caps = 0;
+	int unsig = 0;
+
+	while(*fmt) {
+		if(*fmt == '%') {
+			fstart = fmt++;
+			continue;
+		}
+
+		if(fstart) {
+			if(IS_CONV(*fmt)) {
+				switch(*fmt) {
+				case 'X':
+					hex_caps = 1;
+
+				case 'x':
+				case 'p':
+					base = 16;
+
+					if(alt) {
+						bwrite(BUF(buf), SZ(sz), "0x", 2);
+					}
+
+				case 'u':
+					unsig = 1;
+
+					if(0) {
+				case 'o':
+						base = 8;
+
+						if(alt) {
+							bwrite(BUF(buf), SZ(sz), "0", 1);
+						}
+					}
+
+				case 'd':
+				case 'i':
+					if(unsig) {
+						utoa(va_arg(ap, unsigned int), conv_buf, base);
+					} else {
+						itoa(va_arg(ap, int), conv_buf, base);
+					}
+					if(hex_caps) {
+						for(i=0; conv_buf[i]; i++) {
+							conv_buf[i] = toupper(conv_buf[i]);
+						}
+					}
+
+					slen = strlen(conv_buf);
+					for(i=slen; i<fwidth; i++) {
+						bwrite(BUF(buf), SZ(sz), (char*)&padc, 1);
+						cnum++;
+					}
+
+					bwrite(BUF(buf), SZ(sz), conv_buf, strlen(conv_buf));
+					cnum += slen;
+					break;
+
+				case 'c':
+					{
+						char c = va_arg(ap, int);
+						bwrite(BUF(buf), SZ(sz), &c, 1);
+						cnum++;
+					}
+					break;
+
+				case 's':
+					str = va_arg(ap, char*);
+					slen = strlen(str);
+
+					for(i=slen; i<fwidth; i++) {
+						bwrite(BUF(buf), SZ(sz), (char*)&padc, 1);
+						cnum++;
+					}
+					bwrite(BUF(buf), SZ(sz), str, slen);
+					cnum += slen;
+					break;
+
+				case 'n':
+					*va_arg(ap, int*) = cnum;
+					break;
+
+				default:
+					break;
+				}
+
+				/* restore default conversion state */
+				base = 10;
+				alt = 0;
+				fwidth = 0;
+				padc = ' ';
+				hex_caps = 0;
+
+				fstart = 0;
+				fmt++;
+			} else {
+				switch(*fmt) {
+				case '#':
+					alt = 1;
+					break;
+
+				case '+':
+					sign = 1;
+					break;
+
+				case '-':
+					left_align = 1;
+					break;
+
+				case 'l':
+				case 'L':
+					break;
+
+				case '0':
+					padc = '0';
+					break;
+
+				default:
+					if(isdigit(*fmt)) {
+						const char *fw = fmt;
+						while(*fmt && isdigit(*fmt)) fmt++;
+
+						fwidth = atoi(fw);
+						continue;
+					}
+				}
+				fmt++;
+			}
+		} else {
+			bwrite(BUF(buf), SZ(sz), (char*)fmt++, 1);
+			cnum++;
+		}
+	}
+
+	return 0;
+}
+
+
+/* bwrite is called by intern_printf to transparently handle writing into a
+ * buffer (if buf is non-null) or to the terminal (if buf is null).
+ */
+static void bwrite(char *buf, size_t buf_sz, char *str, int sz)
+{
+	if(buf) {
+		if(buf_sz && buf_sz <= sz) sz = buf_sz - 1;
+		memcpy(buf, str, sz);
+
+		buf[sz] = 0;
+	} else {
+		int i;
+		for(i=0; i<sz; i++) {
+			putchar(*str++);
+		}
+	}
+}
+
+
+static void itoa(int val, char *buf, int base)
+{
+	static char rbuf[16];
+	char *ptr = rbuf;
+	int neg = 0;
+
+	if(val < 0) {
+		neg = 1;
+		val = -val;
+	}
+
+	if(val == 0) {
+		*ptr++ = '0';
+	}
+
+	while(val) {
+		int digit = val % base;
+		*ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a');
+		val /= base;
+	}
+
+	if(neg) {
+		*ptr++ = '-';
+	}
+
+	ptr--;
+
+	while(ptr >= rbuf) {
+		*buf++ = *ptr--;
+	}
+	*buf = 0;
+}
+
+static void utoa(unsigned int val, char *buf, int base)
+{
+	static char rbuf[16];
+	char *ptr = rbuf;
+
+	if(val == 0) {
+		*ptr++ = '0';
+	}
+
+	while(val) {
+		unsigned int digit = val % base;
+		*ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a');
+		val /= base;
+	}
+
+	ptr--;
+
+	while(ptr >= rbuf) {
+		*buf++ = *ptr--;
+	}
+	*buf = 0;
+}
+
diff -r f99eab59e7dc -r 862f8a034cae src/libc/stdlib.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libc/stdlib.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,62 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+int atoi(const char *str)
+{
+	return strtol(str, 0, 10);
+}
+
+long atol(const char *str)
+{
+	return strtol(str, 0, 10);
+}
+
+long strtol(const char *str, char **endp, int base)
+{
+	long acc = 0;
+	int sign = 1;
+
+	while(isspace(*str)) str++;
+
+	if(base == 0) {
+		if(str[0] == '0') {
+			if(str[1] == 'x' || str[1] == 'X') {
+				base = 16;
+			} else {
+				base = 8;
+			}
+		} else {
+			base = 10;
+		}
+	}
+
+	if(*str == '+') {
+		str++;
+	} else if(*str == '-') {
+		sign = -1;
+		str++;
+	}
+
+	while(*str) {
+		long val;
+		char c = tolower(*str);
+
+		if(isdigit(c)) {
+			val = *str - '0';
+		} else if(c >= 'a' || c <= 'f') {
+			val = 10 + c - 'a';
+		}
+		if(val >= base) {
+			break;
+		}
+
+		acc = acc * base + val;
+		str++;
+	}
+
+	if(endp) {
+		*endp = (char*)str;
+	}
+
+	return sign > 0 ? acc : -acc;
+}
diff -r f99eab59e7dc -r 862f8a034cae src/libc/string.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libc/string.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,107 @@
+#include <string.h>
+
+void *memset(void *s, int c, size_t n)
+{
+	char *ptr = s;
+	while(n--) {
+		*ptr++ = c;
+	}
+	return s;
+}
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+	char *dptr = dest;
+	const char *sptr = src;
+
+	while(n--) {
+		*dptr++ = *sptr++;
+	}
+	return dest;
+}
+
+void *memmove(void *dest, const void *src, size_t n)
+{
+	int i;
+	char *dptr;
+	const char *sptr;
+
+	if(dest <= src) {
+		/* forward copy */
+		dptr = dest;
+		sptr = src;
+		for(i=0; i<n; i++) {
+			*dptr++ = *sptr++;
+		}
+	} else {
+		/* backwards copy */
+		dptr = (char*)dest + n - 1;
+		sptr = (char*)src + n - 1;
+		for(i=0; i<n; i++) {
+			*dptr-- = *sptr--;
+		}
+	}
+
+	return dest;
+}
+
+size_t strlen(const char *s)
+{
+	size_t len = 0;
+	while(*s++) len++;
+	return len;
+}
+
+char *strchr(const char *s, int c)
+{
+	while(*s) {
+		if(*s == c) {
+			return (char*)s;
+		}
+		s++;
+	}
+	return 0;
+}
+
+char *strrchr(const char *s, int c)
+{
+	const char *ptr = s;
+
+	/* find the end */
+	while(*ptr) ptr++;
+
+	/* go back checking for c */
+	while(--ptr >= s) {
+		if(*ptr == c) {
+			return (char*)ptr;
+		}
+	}
+	return 0;
+}
+
+char *strstr(const char *str, const char *substr)
+{
+	while(*str) {
+		const char *s1 = str;
+		const char *s2 = substr;
+
+		while(*s1 && *s1 == *s2) {
+			s1++;
+			s2++;
+		}
+		if(!*s2) {
+			return (char*)str;
+		}
+		str++;
+	}
+	return 0;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+	while(*s1 && *s1 == *s2) {
+		s1++;
+		s2++;
+	}
+	return *s1 - *s2;
+}
diff -r f99eab59e7dc -r 862f8a034cae src/main.c
--- a/src/main.c	Wed Feb 01 14:40:19 2017 +0200
+++ b/src/main.c	Sat Feb 11 08:56:42 2017 +0200
@@ -1,12 +1,32 @@
 #include "vdp.h"
 
+static const unsigned char pal[][3] = {
+	{0, 0, 0}, {64, 128, 255}, {255, 128, 32}, {255, 255, 255}
+};
+
+static const unsigned char pat[8] = {
+	0, 0, 0, 0,					/* 0 0 0 0 0 0 0 0 */
+	1, 0x11, 0x11, 0x11,		/* 0 1 1 1 1 1 1 1 */
+	1, 0x11, 0x11, 0x11,		/* 0 1 1 1 1 1 1 1 */
+	1, 0x11, 0x11, 0x12,		/* 0 1 1 1 1 1 1 2 */
+	1, 0x11, 0x12, 0x22,		/* 0 1 1 1 1 2 2 2 */
+	1, 0x11, 0x22, 0x22,		/* 0 1 1 1 2 2 2 2 */
+	1, 0x11, 0x22, 0x22,		/* 0 1 1 1 2 2 2 2 */
+	1, 0x12, 0x22, 0x22			/* 0 1 1 2 2 2 2 2 */
+};
+
 int main(void)
 {
-	VDP_SET_CRAM_ADDR(0);
-	VDP_SET_CRAM_RGB24(64, 128, 255);
+	unsigned char *tmap;
+
+	vdp_init();
+
+	vdp_setpal(0, sizeof pal / sizeof *pal, (unsigned char*)pal);
 	VDP_SET_BGCOLOR(0, 0);
-	/* enable display */
-	VDP_SET_REG(0, VDP_REG0_BASE);
-	VDP_SET_REG(1, VDP_REG1_BASE | VDP_REG1_DISP_BIT);
+
+	vdp_set_tilemap_slot(VDP_PLANE_A, 0);
+	tmap = vdp_tilemap_ptr(VDP_PLANE_A);
+
+
 	return 0;
 }
diff -r f99eab59e7dc -r 862f8a034cae src/misc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include "misc.h"
+
+void panic(const char *fmt, ...)
+{
+	va_list ap;
+
+	printf("~~~~ panic ~~~~\n");
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	va_end(ap);
+
+	/*printf("registers:\n");*/
+
+	halt_cpu();
+}
diff -r f99eab59e7dc -r 862f8a034cae src/misc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc.h	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,9 @@
+#ifndef MISC_H_
+#define MISC_H_
+
+void panic(const char *fmt, ...);
+
+/* defined in startup.s */
+void halt_cpu(void);
+
+#endif	/* MISC_H_ */
diff -r f99eab59e7dc -r 862f8a034cae src/startup.s
--- a/src/startup.s	Wed Feb 01 14:40:19 2017 +0200
+++ b/src/startup.s	Sat Feb 11 08:56:42 2017 +0200
@@ -2,6 +2,7 @@
 	.extern main
 
 	.global start
+	.global halt_cpu
 start:
 	| copy .data section from ROM to RAM
 	move.l #_data_lma, %a0
@@ -24,4 +25,5 @@
 	bne.s 0b
 1:
 	jsr main
+halt_cpu:
 	stop #0x2700
diff -r f99eab59e7dc -r 862f8a034cae src/vdp.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vdp.c	Sat Feb 11 08:56:42 2017 +0200
@@ -0,0 +1,85 @@
+#include <stdint.h>
+#include "vdp.h"
+#include "io.h"
+#include "misc.h"
+
+static void *tilemap_ptr[3];
+
+int vdp_init(void)
+{
+	unsigned int mode1_flags = VDP_REG1_DISP_BIT;
+
+	if(mdg_ispal()) {
+		mode1_flags |= VDP_REG1_30CELL_BIT;
+	}
+
+	VDP_SET_REG(0, VDP_REG0_BASE);
+	VDP_SET_REG(1, VDP_REG1_BASE | mode1_flags);
+
+	return 0;
+}
+
+void vdp_set_tilemap_slot(int plane, int slot)
+{
+	switch(plane) {
+	case VDP_PLANE_A:
+		VDP_SET_REG(VDP_REG_PADDR_A, (slot & 7) << 3);
+		tilemap_ptr[VDP_PLANE_A] = (void*)((uint32_t)slot << 13);
+		break;
+
+	case VDP_PLANE_WIN:
+		VDP_SET_REG(VDP_REG_PADDR_WIN, (slot & 0x1f) << 1);
+		tilemap_ptr[VDP_PLANE_WIN] = (void*)((uint32_t)slot << 11);
+		break;
+
+	case VDP_PLANE_B:
+		VDP_SET_REG(VDP_REG_PADDR_B, slot & 7);
+		tilemap_ptr[VDP_PLANE_B] = (void*)((uint32_t)slot << 13);
+		break;
+	}
+}
+
+void *vdp_tilemap_ptr(int plane)
+{
+	return tilemap_ptr[plane];
+}
+
+void vdp_setpal_rgb24(int idx, int r, int g, int b)
+{
+	VDP_SET_CRAM_ADDR(idx);
+	VDP_SET_CRAM_RGB24(r, g, b);
+}
+
+void vdp_setpal(int idx0, int count, unsigned char *pal)
+{
+	int i;
+
+	VDP_SET_CRAM_ADDR(idx0);
+	for(i=0; i<count; i++) {
+		VDP_SET_CRAM_RGB24(pal[0], pal[1], pal[2]);
+		pal += 3;
+	}
+}
+
+#define SCROLLSIZE(x)	\
+	do { \
+		switch(xtiles) { \
+		case 32: \
+		case 64: \
+			(x) >>= 6; \
+			break; \
+		case 128: \
+			(x) = 3; \
+			break; \
+		default: \
+			panic("invalid argument to %s: %d\n", (x), __func__); \
+		} \
+	} while(0)
+
+void vdp_set_scroll_size(int xtiles, int ytiles)
+{
+	SCROLLSIZE(xtiles);
+	SCROLLSIZE(ytiles);
+
+	VDP_SET_REG(VDP_REG_SCROLL_SIZE, (ytiles << 4) | xtiles);
+}
diff -r f99eab59e7dc -r 862f8a034cae src/vdp.h
--- a/src/vdp.h	Wed Feb 01 14:40:19 2017 +0200
+++ b/src/vdp.h	Sat Feb 11 08:56:42 2017 +0200
@@ -10,6 +10,28 @@
 #define VDP_PORT_HVCOUNT	(*(volatile uint16_t*)0xc00008)
 #define VDP_PORT_PSG		(*(volatile uint16_t*)0xc00010)
 
+/* registers */
+#define VDP_REG_MODE1			0
+#define VDP_REG_MODE2			1
+#define VDP_REG_PADDR_A			2
+#define VDP_REG_PADDR_WIN		3
+#define VDP_REG_PADDR_B			4
+#define VDP_REG_SPRITE			5
+#define VDP_REG_BGCOLOR			7
+#define VDP_REG_HINT			10
+#define VDP_REG_MODE3			11
+#define VDP_REG_MODE4			12
+#define VDP_REG_HSCROLL			13
+#define VDP_REG_AUTOINC			15
+#define VDP_REG_SCROLL_SIZE		16
+#define VDP_REG_WINXPOS			17
+#define VDP_REG_WINYPOS			18
+#define VDP_REG_DMALEN_LOW		19
+#define VDP_REG_DMALEN_HIGH		20
+#define VDP_REG_DMA_SADDR_LOW	21
+#define VDP_REG_DMA_SADDR_MID	22
+#define VDP_REG_DMA_SADDR_HIGH	23
+
 /* control register read flags */
 #define VDP_CTL_PAL_BIT			0x0001
 #define VDP_CTL_HBLANK_BIT		0x0002
@@ -42,12 +64,17 @@
 #define VDP_REG1_DMA_BIT	0x10
 #define VDP_REG1_VINTR_BIT	0x20
 #define VDP_REG1_DISP_BIT	0x40
+#define VDP_REG1_XVRAM_BIT	0x80
 
 #define VDP_MODE_WR_BIT		1
 
 #define VDP_VRAM_WR		1
 #define VDP_CRAM_WR		3
 
+#define VDP_DMA_MEM_TO_VRAM		0
+#define VDP_DMA_VRAM_FILL		2
+#define VDP_DMA_VRAM_COPY		3
+
 #define VDP_ADDRSET(addr, mode) /* TODO */
 
 #define VDP_CRAM_ADDR32(addr) (0xc0000000 | ((uint32_t)(addr) << 16))
@@ -55,15 +82,38 @@
 #define VDP_SET_CRAM_ADDR(addr) \
 	do { VDP_PORT_CTL32 = VDP_CRAM_ADDR32(addr); } while(0)
 
+#define VDP_RGB(r, g, b) \
+	((((uint16_t)(r) << 1) & 0xe) | \
+	 (((uint16_t)(g) << 5) & 0xe0) | \
+	 (((uint16_t)(b) << 9) & 0xe00))
+
 #define VDP_RGB24(r, g, b) \
 	((((uint16_t)(r) >> 4) & 0xe) | \
 	 ((uint16_t)(g) & 0xe0) | \
 	 (((uint16_t)(b) << 4) & 0xe00))
 
+#define VDP_SET_CRAM_RGB(r, g, b) \
+	do { VDP_PORT_DATA = VDP_RGB(r, g, b); } while(0)
+
 #define VDP_SET_CRAM_RGB24(r, g, b) \
 	do { VDP_PORT_DATA = VDP_RGB24(r, g, b); } while(0)
 
 #define VDP_SET_BGCOLOR(pal, col) \
-	do { VDP_SET_REG(7, ((pal) << 4) | (col)); } while(0)
+	do { VDP_SET_REG(VDP_REG_BGCOLOR, ((pal) << 4) | (col)); } while(0)
+
+/* arguments to vdp_tilemap_slot */
+#define VDP_PLANE_A		0
+#define VDP_PLANE_WIN	1
+#define VDP_PLANE_B		2
+
+int vdp_init(void);
+void vdp_set_tilemap_slot(int plane, int slot);
+void *vdp_tilemap_ptr(int plane);
+void vdp_setpal_rgb24(int idx, int r, int g, int b);
+void vdp_setpal(int idx0, int count, unsigned char *pal);
+/* TODO vdp_setpal_dma */
+
+/* xtiles and ytiles can only be 32, 64, or 128 */
+void vdp_set_scroll_size(int xtiles, int ytiles);
 
 #endif	/* VDP_H_ */