summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pavone <pavone@retrodev.com>2012-12-08 02:00:54 -0800
committerMike Pavone <pavone@retrodev.com>2012-12-08 02:00:54 -0800
commit0fe3c72239c1bc454965bfe6877307667135bbac (patch)
tree84eaf7aa383791b430730d2a2e4bed2c0d9f9dad
parent97d78d6e0c9cebc49ccb61b57d7944db7a739df6 (diff)
Mostly broken VDP core and savestate viewer
-rw-r--r--Makefile10
-rw-r--r--dis.c2
-rw-r--r--fib.s682
-rw-r--r--render.h10
-rw-r--r--render_sdl.c102
-rw-r--r--stateview.c25
-rw-r--r--vdp.c894
-rw-r--r--vdp.h80
8 files changed, 1120 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index 4275750..2b5554e 100644
--- a/Makefile
+++ b/Makefile
@@ -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 @@ dis : dis.o 68kinst.o
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 @@ gen_fib : gen_fib.o gen_x86.o mem.o
$(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
diff --git a/dis.c b/dis.c
index 4aae55c..af153d3 100644
--- a/dis.c
+++ b/dis.c
@@ -20,7 +20,7 @@ int main(int argc, char ** argv)
{
*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;
diff --git a/fib.s68 b/fib.s68
index 665a9b7..c8cf58b 100644
--- a/fib.s68
+++ b/fib.s68
@@ -1,6 +1,6 @@
dc.l $0, start
start:
- moveq #10, d0
+ moveq #36, d0
bsr fib
illegal
fib:
diff --git a/render.h b/render.h
new file mode 100644
index 0000000..7f58515
--- /dev/null
+++ b/render.h
@@ -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_
+
diff --git a/render_sdl.c b/render_sdl.c
new file mode 100644
index 0000000..f2f15c0
--- /dev/null
+++ b/render_sdl.c
@@ -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;
+ }
+ }
+}
+
diff --git a/stateview.c b/stateview.c
new file mode 100644
index 0000000..6da765e
--- /dev/null
+++ b/stateview.c
@@ -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;
+}
diff --git a/vdp.c b/vdp.c
new file mode 100644
index 0000000..e8a1e67
--- /dev/null
+++ b/vdp.c
@@ -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);
+}
diff --git a/vdp.h b/vdp.h
new file mode 100644
index 0000000..7561ba4
--- /dev/null
+++ b/vdp.h
@@ -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_