changeset 20:f664eeb55cb4

Mostly broken VDP core and savestate viewer
author Mike Pavone <pavone@retrodev.com>
date Sat, 08 Dec 2012 02:00:54 -0800
parents 4717146a7606
children 72ce60cb1711
files Makefile dis.c fib.s68 render.h render_sdl.c stateview.c vdp.c vdp.h
diffstat 8 files changed, 1120 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Dec 04 19:25:54 2012 -0800
+++ b/Makefile	Sat Dec 08 02:00:54 2012 -0800
@@ -1,5 +1,6 @@
+LIBS=sdl
 
-all : dis trans
+all : dis trans stateview
 
 dis : dis.o 68kinst.o
 	$(CC) -o dis dis.o 68kinst.o
@@ -7,6 +8,9 @@
 trans : trans.o 68kinst.o gen_x86.o m68k_to_x86.o runtime.o mem.o
 	$(CC) -o trans trans.o 68kinst.o gen_x86.o m68k_to_x86.o runtime.o mem.o
 
+stateview : stateview.o vdp.o render_sdl.o
+	$(CC) -o stateview stateview.o vdp.o render_sdl.o `pkg-config --libs $(LIBS)`
+
 test_x86 : test_x86.o gen_x86.o
 	$(CC) -o test_x86 test_x86.o gen_x86.o
 
@@ -17,10 +21,10 @@
 	$(CC) -c -o $@ $<
 
 %.o : %.c
-	$(CC) -ggdb -std=gnu99 -c -o $@ $<
+	$(CC) -ggdb -std=gnu99 `pkg-config --cflags-only-I $(LIBS)` -c -o $@ $<
 
 %.bin : %.s68
 	vasmm68k_mot -Fbin -m68000 -spaces -o $@ $<
 
 clean :
-	rm -rf dis trans test_x86 gen_fib *.o
+	rm -rf dis trans stateview test_x86 gen_fib *.o
--- a/dis.c	Tue Dec 04 19:25:54 2012 -0800
+++ b/dis.c	Sat Dec 08 02:00:54 2012 -0800
@@ -20,7 +20,7 @@
 	{
 		*cur = (*cur >> 8) | (*cur << 8);
 	}
-	for(cur = filebuf; (cur - filebuf) < (filesize/2); )
+	for(cur = filebuf + 0x100; (cur - filebuf) < (filesize/2); )
 	{
 		//printf("cur: %p: %x\n", cur, *cur);
 		unsigned short * start = cur;
--- a/fib.s68	Tue Dec 04 19:25:54 2012 -0800
+++ b/fib.s68	Sat Dec 08 02:00:54 2012 -0800
@@ -1,6 +1,6 @@
     dc.l $0, start
 start:
-	moveq #10, d0
+	moveq #36, d0
 	bsr fib
 	illegal
 fib:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render.h	Sat Dec 08 02:00:54 2012 -0800
@@ -0,0 +1,10 @@
+#ifndef RENDER_SDL_H_
+#define RENDER_SDL_H_
+
+#include "vdp.h"
+void render_init();
+void render_context(vdp_context * context);
+void render_wait_quit();
+
+#endif //RENDER_SDL_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_sdl.c	Sat Dec 08 02:00:54 2012 -0800
@@ -0,0 +1,102 @@
+#include <SDL.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "render.h"
+
+SDL_Surface *screen;
+
+void render_init()
+{
+	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+        fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
+        exit(1);
+    }
+    atexit(SDL_Quit);
+    screen = SDL_SetVideoMode(320, 240, 32, SDL_SWSURFACE | SDL_ANYFORMAT);
+    if (!screen) {
+    	fprintf(stderr, "Unable to get SDL surface: %s\n", SDL_GetError());
+        exit(1);
+    }
+    if (screen->format->BytesPerPixel < 2) {
+    	fprintf(stderr, "BlastEm requires at least a 16-bit surface, SDL returned a %d-bit surface\n", screen->format->BytesPerPixel * 8);
+    	exit(1);
+    }
+}
+
+void render_context(vdp_context * context)
+{
+	uint8_t *buf_8;
+	uint16_t *buf_16;
+	uint32_t *buf_32; 
+	uint8_t b,g,r;
+	if (SDL_MUSTLOCK(screen)) {
+		if (SDL_LockSurface(screen) < 0) {
+			return;
+		}
+    }
+    switch (screen->format->BytesPerPixel) {
+    case 2:
+        buf_16 = (uint16_t *)screen->pixels;
+        for (int y = 0; y < 240; y++, buf_16 += (screen->pitch/2 - 320)) {
+        	for (int x = 0; x < 320; x++, buf_16++) {
+        		uint16_t gen_color = context->framebuf[y * 320 + x];
+        		b = ((gen_color >> 8) & 0xE) * 18;
+        		g = ((gen_color >> 4) & 0xE) * 18;
+        		r = (gen_color& 0xE) * 18;
+        		*buf_16 = SDL_MapRGB(screen->format, r, g, b);
+        	}
+        }
+    	break;
+    case 3:
+        buf_8 = (uint8_t *)screen->pixels;
+        for (int y = 0; y < 240; y++, buf_8 += (screen->pitch - 320)) {
+        	for (int x = 0; x < 320; x++, buf_8 += 3) {
+        		uint16_t gen_color = context->framebuf[y * 320 + x];
+        		b = ((gen_color >> 8) & 0xE) * 18;
+        		g = ((gen_color >> 4) & 0xE) * 18;
+        		r = (gen_color& 0xE) * 18;
+				*(buf_8+screen->format->Rshift/8) = r;
+				*(buf_8+screen->format->Gshift/8) = g;
+				*(buf_8+screen->format->Bshift/8) = b;
+        	}
+        }
+    	break;
+    case 4:
+        buf_32 = (uint32_t *)screen->pixels;
+        for (int y = 0; y < 224; y++, buf_32 += (screen->pitch/4 - 320)) {
+        	for (int x = 0; x < 320; x++, buf_32++) {
+        		uint16_t gen_color = context->framebuf[y * 320 + x];
+        		b = ((gen_color >> 8) & 0xE) * 18;
+        		g = ((gen_color >> 4) & 0xE) * 18;
+        		r = (gen_color& 0xE) * 18;
+        		*buf_32 = SDL_MapRGB(screen->format, r, g, b);
+        	}
+        }
+        for (int y = 224; y < 240; y++, buf_32 += (screen->pitch/4 - 320)) {
+        	for (int x = 0; x < 320; x++, buf_32++) {
+        		uint16_t gen_color = context->cram[x/10 + ((y-224)/8)*32];
+        		b = ((gen_color >> 8) & 0xE) * 18;
+        		g = ((gen_color >> 4) & 0xE) * 18;
+        		r = (gen_color& 0xE) * 18;
+        		*buf_32 = SDL_MapRGB(screen->format, r, g, b);
+        	}
+        }
+    	break;
+    }
+    if ( SDL_MUSTLOCK(screen) ) {
+        SDL_UnlockSurface(screen);
+    }
+    SDL_UpdateRect(screen, 0, 0, 320, 240);
+}
+
+void render_wait_quit()
+{
+	SDL_Event event;
+	while(SDL_WaitEvent(&event)) {
+		switch (event.type) {
+		case SDL_QUIT:
+			return;
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stateview.c	Sat Dec 08 02:00:54 2012 -0800
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "vdp.h"
+#include "render.h"
+
+int main(int argc, char ** argv)
+{
+	if (argc < 2) {
+		fprintf(stderr, "Usage: stateview FILENAME\n");
+		exit(1);
+	}
+	FILE * state_file = fopen(argv[1], "rb");
+	if (!state_file) {
+		fprintf(stderr, "Failed to open %s\n", argv[1]);
+		exit(1);
+	}
+	vdp_context context;
+	init_vdp_context(&context);
+	vdp_load_savestate(&context, state_file);
+	vdp_run_to_vblank(&context);
+    render_init();
+    render_context(&context);
+    render_wait_quit();
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vdp.c	Sat Dec 08 02:00:54 2012 -0800
@@ -0,0 +1,894 @@
+#include "vdp.h"
+#include <stdlib.h>
+#include <string.h>
+
+#define MCLKS_LINE 3420
+#define NTSC_ACTIVE 225
+#define PAL_ACTIVE 241
+#define BUF_BIT_PRIORITY 0x40
+#define MAP_BIT_PRIORITY 0x8000
+#define MAP_BIT_H_FLIP 0x800
+#define MAP_BIT_V_FLIP 0x1000
+
+#define BIT_PAL 0x8
+#define BIT_H40 0x1
+
+void init_vdp_context(vdp_context * context)
+{
+	memset(context, 0, sizeof(context));
+	context->vdpmem = malloc(VRAM_SIZE);
+	context->framebuf = malloc(FRAMEBUF_SIZE);
+	context->linebuf = malloc(LINEBUF_SIZE + 48);
+	context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
+	context->tmp_buf_b = context->tmp_buf_a + 24;
+}
+
+void render_sprite_cells(uint32_t linecyc, vdp_context * context)
+{
+	if (linecyc < context->sprite_draws) {
+		sprite_draw * d = context->sprite_draw_list + linecyc;
+		uint16_t dir;
+		int16_t x;
+		if (d->h_flip) {
+			x = d->x_pos + 7;
+			dir = -1;
+		} else {
+			x = d->x_pos;
+			dir = 1;
+		}
+		for (uint16_t address = d->address; address < d->address+4; address++) {
+			if (x >= 0 && x < 320) {
+				context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority;
+			}
+			x += dir;
+			if (x >= 0 && x < 320) {
+				context->linebuf[x] = (context->vdpmem[address] & 0xF)  | d->pal_priority;
+			}
+			x += dir;
+		}
+	}
+}
+
+void scan_sprite_table(uint32_t line, vdp_context * context)
+{
+	if (context->sprite_index && context->slot_counter) {
+		line += 1;
+		line &= 0xFF;
+		line += 128;
+		context->sprite_index &= 0x7F;
+		//TODO: Read from SAT cache rather than from VRAM
+		uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9;
+		uint16_t address = context->sprite_index * 8 + sat_address;
+		uint16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) - 128;
+		uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * 8;
+		if (y >= line && (y + height) < line) {
+			context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2];
+			context->sprite_info_list[context->slot_counter].index = context->sprite_index;
+			context->sprite_info_list[context->slot_counter].y = y;
+		}
+		context->sprite_index = context->vdpmem[address+3] & 0x7F;
+		if (context->sprite_index && context->slot_counter)
+		{
+			address = context->sprite_index * 8 + sat_address;
+			y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) - 128;
+			height = ((context->vdpmem[address+2] & 0x3) + 1) * 8;
+			if (y >= line && y < (line + height)) {
+				context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2];
+				context->sprite_info_list[context->slot_counter].index = context->sprite_index;
+				context->sprite_info_list[context->slot_counter].y = y;
+			}
+		}
+	}
+}
+
+void read_sprite_x(uint32_t line, vdp_context * context)
+{
+	if (context->slot_counter && context->sprite_draws) {
+		context->slot_counter--;
+		uint8_t width = (context->sprite_info_list[context->slot_counter].size & 0x3) + 1;
+		uint8_t height = (((context->sprite_info_list[context->slot_counter].size >> 2) & 0x3) + 1) * 8;
+		uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->slot_counter].index * 8 + 4;
+		uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];		
+		uint8_t pal_priority = (tileinfo >> 9) & 0x70;
+		uint8_t row;
+		if (tileinfo & MAP_BIT_V_FLIP) {
+			row = (context->sprite_info_list[context->slot_counter].y + height - 1) - line;
+		} else {
+			row = line-context->sprite_info_list[context->slot_counter].y;
+		}
+		uint16_t address = ((tileinfo & 0x7FF) << 5) + row * width * 4;
+		int16_t x = ((context->vdpmem[att_addr+ 6] & 0x3) << 8) | context->vdpmem[att_addr + 7]; 
+		if (x) {
+			x -= 128;
+			for (;width && context->sprite_draws; --width, --context->sprite_draws, address += 4, x += 8) {
+				context->sprite_draw_list[context->sprite_draws].address = address;
+				context->sprite_draw_list[context->sprite_draws].x_pos = x;
+				context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority;
+				context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0;
+			}
+		} else {
+			//sprite masking enabled, no more sprites on this line
+			context->slot_counter = 0;
+		}
+	}
+}
+
+void external_slot(vdp_context * context)
+{
+	//TODO: Implement me
+}
+
+void read_map_scroll(uint16_t column, uint32_t line, uint32_t scroll_reg, uint16_t hscroll_val, vdp_context * context)
+{
+	uint16_t address = (context->regs[scroll_reg] & 0x38) << 10;
+	uint16_t vscroll;
+	switch(context->regs[REG_SCROLL] & 0x30)
+	{
+	case 0:
+		vscroll = 0xFF;
+		break;
+	case 0x10:
+		vscroll = 0x1FF;
+		break;
+	case 0x20:
+		//TODO: Verify this behavior
+		vscroll = 0;
+		break;
+	case 0x30:
+		vscroll = 0x3FF;
+		break;
+	}
+	vscroll &= (context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0] + line);
+	context->v_offset = vscroll & 0x7;
+	vscroll /= 8;
+	uint16_t hscroll_mask;
+	uint16_t v_mul;
+	switch(context->regs[REG_SCROLL] & 0x3)
+	{
+	case 0:
+		hscroll_mask = 0xF8;
+		v_mul = 64;
+		break;
+	case 0x1:
+		hscroll_mask = 0x1F8;
+		v_mul = 128;
+		break;
+	case 0x2:
+		//TODO: Verify this behavior
+		hscroll_mask = 0;
+		v_mul = 0;
+		break;
+	case 0x3:
+		hscroll_mask = 0x3F8;
+		v_mul = 256;
+		break;
+	}
+	uint16_t hscroll = (hscroll_val + (column-1) * 8) & hscroll_mask;
+	uint16_t offset = address + ((vscroll * v_mul + hscroll/4) & 0x1FFF);
+	//printf("A | line: %d, col: %d, x: %d, hs_mask %X, v_mul: %d, scr reg: %X, tbl addr: %X\n", line, column, hscroll, hscroll_mask, v_mul, context->regs[REG_SCROLL], offset);
+	context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
+	hscroll = (hscroll_val + column * 8) & hscroll_mask;
+	offset = address + ((vscroll * v_mul + hscroll/4) & 0x1FFF);
+	context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
+}
+
+void read_map_scroll_a(uint16_t column, uint32_t line, vdp_context * context)
+{
+	read_map_scroll(column, line, REG_SCROLL_A, context->hscroll_a, context);
+}
+
+void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context)
+{
+	read_map_scroll(column, line, REG_SCROLL_B, context->hscroll_b, context);
+}
+
+void render_map(uint16_t col, uint8_t * tmp_buf, vdp_context * context)
+{
+	uint16_t address = ((col & 0x3FF) << 5);
+	if (col & MAP_BIT_V_FLIP) {
+		address +=  24 - 4 * context->v_offset;
+	} else {
+		address += 4 * context->v_offset;
+	}
+	uint16_t pal_priority = (col >> 9) & 0x70;
+	int32_t dir;
+	if (col & MAP_BIT_H_FLIP) {
+		tmp_buf += 7;
+		dir = -1;
+	} else {
+		dir = 1;
+	}
+	for (uint32_t i=0; i < 4; i++, address++)
+	{
+		*tmp_buf = pal_priority | (context->vdpmem[address] >> 4);
+		tmp_buf += dir;
+		*tmp_buf = pal_priority | (context->vdpmem[address] & 0xF);
+		tmp_buf += dir;
+	}
+}
+
+void render_map_1(vdp_context * context)
+{
+	render_map(context->col_1, context->tmp_buf_a+8, context);
+}
+
+void render_map_2(vdp_context * context)
+{
+	render_map(context->col_2, context->tmp_buf_a+16, context);
+}
+
+void render_map_3(vdp_context * context)
+{
+	render_map(context->col_1, context->tmp_buf_b+8, context);
+}
+
+void render_map_output(uint32_t line, int32_t col, vdp_context * context)
+{
+	if (line >= 240) {
+		return;
+	}
+	render_map(context->col_2, context->tmp_buf_b+16, context);
+	uint16_t *dst, *end;
+	uint8_t *sprite_buf, *plane_a, *plane_b;
+	if (col)
+	{
+		col-=2;
+		dst = context->framebuf + line * 320 + col * 8;
+		sprite_buf = context->linebuf + col * 8;
+		plane_a = context->tmp_buf_a + 8 - (context->hscroll_a & 0x7);
+		plane_b = context->tmp_buf_b + 8 - (context->hscroll_b & 0x7);
+		/*if (col == 40)
+		{
+			end = dst + 8;
+		} else {*/
+			end = dst + 16;
+		//}
+		for (; dst < end; ++plane_a, ++plane_b, ++sprite_buf, ++dst) {
+			uint8_t pixel;
+			if (*sprite_buf & BUF_BIT_PRIORITY) {
+				pixel = *sprite_buf;
+			} else if (*plane_a & BUF_BIT_PRIORITY) {
+				pixel = *plane_a;
+			} else if (*plane_b & BUF_BIT_PRIORITY) {
+				pixel = *plane_b;
+			} else if (*sprite_buf & 0xF) {
+				pixel = *sprite_buf;
+			} else if (*plane_a & 0xF) {
+				pixel = *plane_a;
+			} else if (*plane_b & 0xF){
+				pixel = *plane_b;
+			} else {
+				pixel = context->regs[REG_BG_COLOR] & 0x3F;
+			}
+			*dst = context->cram[pixel & 0x3F] | ((pixel & BUF_BIT_PRIORITY) ? 0x1000 : 0);
+		}
+	} else {
+		//dst = context->framebuf + line * 320;
+		//sprite_buf = context->linebuf + col * 8;
+		//plane_a = context->tmp_buf_a + 16 - (context->hscroll_a & 0x7);
+		//plane_b = context->tmp_buf_b + 16 - (context->hscroll_b & 0x7);
+		//end = dst + 8;
+	}
+	
+	uint16_t remaining = context->hscroll_a & 0x7;
+	memcpy(context->tmp_buf_a + 8 - remaining, context->tmp_buf_a + 24 - remaining, remaining);
+	remaining = context->hscroll_b & 0x7;
+	memcpy(context->tmp_buf_b + 8 - remaining, context->tmp_buf_a + 24 - remaining, remaining);
+}
+
+#define COLUMN_RENDER_BLOCK(column, startcyc) \
+	case startcyc:\
+		read_map_scroll_a(column, line, context);\
+		break;\
+	case (startcyc+1):\
+		external_slot(context);\
+		break;\
+	case (startcyc+2):\
+		render_map_1(context);\
+		break;\
+	case (startcyc+3):\
+		render_map_2(context);\
+		break;\
+	case (startcyc+4):\
+		read_map_scroll_b(column, line, context);\
+		break;\
+	case (startcyc+5):\
+		read_sprite_x(line, context);\
+		break;\
+	case (startcyc+6):\
+		render_map_3(context);\
+		break;\
+	case (startcyc+7):\
+		render_map_output(line, column, context);\
+		break;
+
+#define COLUMN_RENDER_BLOCK_REFRESH(column, startcyc) \
+	case startcyc:\
+		read_map_scroll_a(column, line, context);\
+		break;\
+	case (startcyc+1):\
+		break;\
+	case (startcyc+2):\
+		render_map_1(context);\
+		break;\
+	case (startcyc+3):\
+		render_map_2(context);\
+		break;\
+	case (startcyc+4):\
+		read_map_scroll_b(column, line, context);\
+		break;\
+	case (startcyc+5):\
+		read_sprite_x(line, context);\
+		break;\
+	case (startcyc+6):\
+		render_map_3(context);\
+		break;\
+	case (startcyc+7):\
+		render_map_output(line, column, context);\
+		break;
+
+void vdp_h40(uint32_t line, uint32_t linecyc, vdp_context * context)
+{
+	uint16_t address;
+	uint32_t mask;
+	switch(linecyc)
+	{
+	//sprite render to line buffer starts
+	case 0:
+		memset(context->linebuf, 0, LINEBUF_SIZE);
+		render_sprite_cells(linecyc, context);
+		break;
+	case 1:
+	case 2:
+	case 3:
+		render_sprite_cells(linecyc, context);
+		break;
+	//sprite attribute table scan starts
+	case 4:
+		render_sprite_cells(linecyc, context);
+		context->sprite_index = 0x80;
+		context->slot_counter = MAX_SPRITES_LINE;
+		scan_sprite_table(line, context);
+		break;
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+	case 9:
+	case 10:
+	case 11:
+	case 12:
+	case 13:
+	case 14:
+	case 15:
+	case 16:
+	case 17:
+	case 18:
+	case 19:
+	case 20:
+	//!HSYNC asserted
+	case 21:
+	case 22:
+		render_sprite_cells(linecyc, context);
+		scan_sprite_table(line, context);
+		break;
+	case 23:
+		external_slot(context);
+		break;
+	case 24:
+	case 25:
+	case 26:
+	case 27:
+	case 28:
+	case 29:
+	case 30:
+	case 31:
+	case 32:
+	case 33:
+	case 34:
+		render_sprite_cells(linecyc, context);
+		scan_sprite_table(line, context);
+		break;
+	case 35:
+		address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
+		mask = 0;
+		if (context->regs[REG_MODE_3] & 0x2) {
+			mask |= 0xF8;
+		}
+		if (context->regs[REG_MODE_3] & 0x1) {
+			mask |= 0x7;
+		}
+		line &= mask;
+		address += line * 4;
+		context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
+		context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
+		printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
+		break;
+	case 36:
+	//!HSYNC high
+	case 37:
+	case 38:
+	case 39:
+		render_sprite_cells(linecyc, context);
+		scan_sprite_table(line, context);
+		break;
+	case 40:
+		read_map_scroll_a(0, line, context);
+		break;
+	case 41:
+		render_sprite_cells(linecyc, context);
+		scan_sprite_table(line, context);
+		break;
+	case 42:
+		render_map_1(context);
+		scan_sprite_table(line, context);//Just a guess
+		break;
+	case 43:
+		render_map_2(context);
+		scan_sprite_table(line, context);//Just a guess
+		break;
+	case 44:
+		read_map_scroll_b(0, line, context);
+		break;
+	case 45:
+		render_sprite_cells(linecyc, context);
+		scan_sprite_table(line, context);
+		break;
+	case 46:
+		render_map_3(context);
+		scan_sprite_table(line, context);//Just a guess
+		break;
+	case 47:
+		render_map_output(line, 0, context);
+		scan_sprite_table(line, context);//Just a guess
+		//reverse context slot counter so it counts the number of sprite slots
+		//filled rather than the number of available slots
+		context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
+		context->sprite_draws = MAX_DRAWS;
+		break;
+	COLUMN_RENDER_BLOCK(2, 48)
+	COLUMN_RENDER_BLOCK(4, 56)
+	COLUMN_RENDER_BLOCK(6, 64)
+	COLUMN_RENDER_BLOCK_REFRESH(8, 72)
+	COLUMN_RENDER_BLOCK(10, 80)
+	COLUMN_RENDER_BLOCK(12, 88)
+	COLUMN_RENDER_BLOCK(14, 96)
+	COLUMN_RENDER_BLOCK_REFRESH(16, 104)
+	COLUMN_RENDER_BLOCK(18, 112)
+	COLUMN_RENDER_BLOCK(20, 120)
+	COLUMN_RENDER_BLOCK(22, 128)
+	COLUMN_RENDER_BLOCK_REFRESH(24, 136)
+	COLUMN_RENDER_BLOCK(26, 144)
+	COLUMN_RENDER_BLOCK(28, 152)
+	COLUMN_RENDER_BLOCK(30, 160)
+	COLUMN_RENDER_BLOCK_REFRESH(32, 168)
+	COLUMN_RENDER_BLOCK(34, 176)
+	COLUMN_RENDER_BLOCK(36, 184)
+	COLUMN_RENDER_BLOCK(38, 192)
+	COLUMN_RENDER_BLOCK_REFRESH(40, 200)
+	case 208:
+		context->sprite_draws = MAX_DRAWS - context->sprite_draws;
+	case 209:
+		external_slot(context);
+		break;
+	default:
+		//leftovers from HSYNC clock change nonsense
+		break;
+	}
+}
+
+void vdp_h32(uint32_t line, uint32_t linecyc, vdp_context * context)
+{
+	switch(linecyc)
+	{
+	//sprite render to line buffer starts
+	case 0:
+		break;
+	case 1:
+		break;
+	case 2:
+		break;
+	case 3:
+		break;
+	case 4:
+		break;
+	case 5:
+		break;
+	case 6:
+		break;
+	case 7:
+		break;
+	case 8:
+		break;
+	case 9:
+		break;
+	case 10:
+		break;
+	case 11:
+		break;
+	case 12:
+		break;
+	case 13:
+		break;
+	case 14:
+		break;
+	case 15:
+		break;
+	case 16:
+		break;
+	case 17:
+		break;
+	case 18:
+		break;
+	case 19:
+		break;
+	case 20:
+		break;
+	case 21:
+		break;
+	case 22:
+		break;
+	case 23:
+		break;
+	case 24:
+		break;
+	case 25:
+		break;
+	case 26:
+		break;
+	case 27:
+		break;
+	case 28:
+		break;
+	case 29:
+		break;
+	case 30:
+		break;
+	case 31:
+		break;
+	case 32:
+		break;
+	case 33:
+		break;
+	case 34:
+		break;
+	case 35:
+		break;
+	case 36:
+		break;
+	case 37:
+		break;
+	case 38:
+		break;
+	case 39:
+		break;
+	case 40:
+		break;
+	case 41:
+		break;
+	case 42:
+		break;
+	case 43:
+		break;
+	case 44:
+		break;
+	case 45:
+		break;
+	case 46:
+		break;
+	case 47:
+		break;
+	case 48:
+		break;
+	case 49:
+		break;
+	case 50:
+		break;
+	case 51:
+		break;
+	case 52:
+		break;
+	case 53:
+		break;
+	case 54:
+		break;
+	case 55:
+		break;
+	case 56:
+		break;
+	case 57:
+		break;
+	case 58:
+		break;
+	case 59:
+		break;
+	case 60:
+		break;
+	case 61:
+		break;
+	case 62:
+		break;
+	case 63:
+		break;
+	case 64:
+		break;
+	case 65:
+		break;
+	case 66:
+		break;
+	case 67:
+		break;
+	case 68:
+		break;
+	case 69:
+		break;
+	case 70:
+		break;
+	case 71:
+		break;
+	case 72:
+		break;
+	case 73:
+		break;
+	case 74:
+		break;
+	case 75:
+		break;
+	case 76:
+		break;
+	case 77:
+		break;
+	case 78:
+		break;
+	case 79:
+		break;
+	case 80:
+		break;
+	case 81:
+		break;
+	case 82:
+		break;
+	case 83:
+		break;
+	case 84:
+		break;
+	case 85:
+		break;
+	case 86:
+		break;
+	case 87:
+		break;
+	case 88:
+		break;
+	case 89:
+		break;
+	case 90:
+		break;
+	case 91:
+		break;
+	case 92:
+		break;
+	case 93:
+		break;
+	case 94:
+		break;
+	case 95:
+		break;
+	case 96:
+		break;
+	case 97:
+		break;
+	case 98:
+		break;
+	case 99:
+		break;
+	case 100:
+		break;
+	case 101:
+		break;
+	case 102:
+		break;
+	case 103:
+		break;
+	case 104:
+		break;
+	case 105:
+		break;
+	case 106:
+		break;
+	case 107:
+		break;
+	case 108:
+		break;
+	case 109:
+		break;
+	case 110:
+		break;
+	case 111:
+		break;
+	case 112:
+		break;
+	case 113:
+		break;
+	case 114:
+		break;
+	case 115:
+		break;
+	case 116:
+		break;
+	case 117:
+		break;
+	case 118:
+		break;
+	case 119:
+		break;
+	case 120:
+		break;
+	case 121:
+		break;
+	case 122:
+		break;
+	case 123:
+		break;
+	case 124:
+		break;
+	case 125:
+		break;
+	case 126:
+		break;
+	case 127:
+		break;
+	case 128:
+		break;
+	case 129:
+		break;
+	case 130:
+		break;
+	case 131:
+		break;
+	case 132:
+		break;
+	case 133:
+		break;
+	case 134:
+		break;
+	case 135:
+		break;
+	case 136:
+		break;
+	case 137:
+		break;
+	case 138:
+		break;
+	case 139:
+		break;
+	case 140:
+		break;
+	case 141:
+		break;
+	case 142:
+		break;
+	case 143:
+		break;
+	case 144:
+		break;
+	case 145:
+		break;
+	case 146:
+		break;
+	case 147:
+		break;
+	case 148:
+		break;
+	case 149:
+		break;
+	case 150:
+		break;
+	case 151:
+		break;
+	case 152:
+		break;
+	case 153:
+		break;
+	case 154:
+		break;
+	case 155:
+		break;
+	case 156:
+		break;
+	case 157:
+		break;
+	case 158:
+		break;
+	case 159:
+		break;
+	case 160:
+		break;
+	case 161:
+		break;
+	case 162:
+		break;
+	case 163:
+		break;
+	case 164:
+		break;
+	case 165:
+		break;
+	case 166:
+		break;
+	case 167:
+		break;
+	case 168:
+		break;
+	case 169:
+		break;
+	case 170:
+		break;
+	case 171:
+		break;
+	}
+}
+void latch_mode(vdp_context * context)
+{
+	context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL);
+}
+
+void vdp_run_context(vdp_context * context, uint32_t target_cycles)
+{
+	while(context->cycles < target_cycles)
+	{
+		uint32_t line = context->cycles / MCLKS_LINE;
+		uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
+		if (line < active_lines) {
+			if (!line) {
+				latch_mode(context);
+			}
+			//first sort-of active line is treated as 255 internally
+			//it's used for gathering sprite info for line 
+			line = (line - 1) & 0xFF;
+			uint32_t linecyc = context->cycles % MCLKS_LINE;
+			
+			//Convert to slot number
+			if (context->latched_mode & BIT_H40){
+				//TODO: Deal with nasty clock switching during HBLANK
+				linecyc = linecyc/16;
+				context->cycles += 16;
+				vdp_h40(line, linecyc, context);
+			} else {
+				linecyc = linecyc/20;
+				context->cycles += 20;
+				vdp_h32(line, linecyc, context);
+			}
+		} else {
+			//TODO: Empty FIFO
+		}
+	}
+}
+
+uint32_t vdp_run_to_vblank(vdp_context * context)
+{
+	uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_ACTIVE : NTSC_ACTIVE) * MCLKS_LINE;
+	vdp_run_context(context, target_cycles);
+	return context->cycles;
+}
+
+#define GST_VDP_REGS 0xFA
+#define GST_VDP_MEM 0x12478
+
+void vdp_load_savestate(vdp_context * context, FILE * state_file)
+{
+	uint8_t tmp_buf[CRAM_SIZE*2];
+	fseek(state_file, GST_VDP_REGS, SEEK_SET);
+	fread(context->regs, 1, VDP_REGS, state_file);
+	latch_mode(context);
+	fread(tmp_buf, 1, sizeof(tmp_buf), state_file);
+	for (int i = 0; i < CRAM_SIZE; i++) {
+		context->cram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
+	}
+	fread(tmp_buf, 2, VSRAM_SIZE, state_file);
+	for (int i = 0; i < VSRAM_SIZE; i++) {
+		context->vsram[i] = (tmp_buf[i*2] << 8) | tmp_buf[i*2+1];
+	}
+	fseek(state_file, GST_VDP_MEM, SEEK_SET);
+	fread(context->vdpmem, 1, VRAM_SIZE, state_file);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vdp.h	Sat Dec 08 02:00:54 2012 -0800
@@ -0,0 +1,80 @@
+#ifndef VDP_H_
+#define VDP_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#define VDP_REGS 24
+#define CRAM_SIZE 64
+#define VSRAM_SIZE 40
+#define VRAM_SIZE (64*1024)
+#define LINEBUF_SIZE 320
+#define FRAMEBUF_ENTRIES 320*224
+#define FRAMEBUF_SIZE (FRAMEBUF_ENTRIES*sizeof(uint16_t))
+#define MAX_DRAWS 40
+#define MAX_SPRITES_LINE 20
+
+enum {
+	REG_MODE_1=0,
+	REG_MODE_2,
+	REG_SCROLL_A,
+	REG_WINDOW,
+	REG_SCROLL_B,
+	REG_SAT,
+	REG_BG_COLOR,
+	REG_HINT=0xA,
+	REG_MODE_3,
+	REG_MODE_4,
+	REG_HSCROLL,
+	REG_AUTOINC=0xF,
+	REG_SCROLL,
+	REG_WINDOW_H,
+	REG_WINDOW_V
+} vdp_regs;
+
+typedef struct {
+	uint16_t address;
+	int16_t x_pos;
+	uint8_t pal_priority;
+	uint8_t h_flip;
+} sprite_draw;
+
+typedef struct {
+	uint8_t size;
+	uint8_t index;
+	int16_t y;
+} sprite_info;
+
+typedef struct {
+	//cycle count in MCLKs
+	uint32_t    cycles;
+	uint8_t     *vdpmem;
+	//stores 2-bit palette + 4-bit palette index + priority for current sprite line
+	uint8_t     *linebuf;
+	//stores 12-bit color + shadow/highlight bits
+	uint16_t    *framebuf;
+	uint16_t    cram[CRAM_SIZE];
+	uint16_t    vsram[VSRAM_SIZE];
+	uint8_t     latched_mode;
+	uint16_t    hscroll_a;
+	uint16_t    hscroll_b;
+	uint8_t	    sprite_index;
+	uint8_t     sprite_draws;
+	uint8_t     slot_counter;
+	uint8_t     regs[VDP_REGS];
+	sprite_draw sprite_draw_list[MAX_DRAWS];
+	sprite_info sprite_info_list[MAX_SPRITES_LINE];
+	uint16_t    col_1;
+	uint16_t    col_2;
+	uint8_t     v_offset;
+	uint8_t     *tmp_buf_a;
+	uint8_t     *tmp_buf_b;
+} vdp_context;
+
+void init_vdp_context(vdp_context * context);
+void vdp_run_context(vdp_context * context, uint32_t target_cycles);
+//runs from current cycle count to VBLANK for the current mode, returns ending cycle count
+uint32_t vdp_run_to_vblank(vdp_context * context);
+void vdp_load_savestate(vdp_context * context, FILE * state_file);
+
+#endif //VDP_H_