main vdp.c
   1/*
   2 Copyright 2013 Michael Pavone
   3 This file is part of BlastEm.
   4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
   5*/
   6#include "vdp.h"
   7#include "blastem.h"
   8#include <stdlib.h>
   9#include <string.h>
  10#include "render.h"
  11#include "util.h"
  12
  13#define NTSC_INACTIVE_START 224
  14#define PAL_INACTIVE_START 240
  15#define MODE4_INACTIVE_START 192
  16#define BUF_BIT_PRIORITY 0x40
  17#define MAP_BIT_PRIORITY 0x8000
  18#define MAP_BIT_H_FLIP 0x800
  19#define MAP_BIT_V_FLIP 0x1000
  20
  21#define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1)
  22#define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2)
  23
  24#define MCLKS_SLOT_H40  16
  25#define MCLKS_SLOT_H32  20
  26#define VINT_SLOT_H40  0 //21 slots before HSYNC, 16 during, 10 after
  27#define VINT_SLOT_H32  0  //old value was 23, but recent tests suggest the actual value is close to the H40 one
  28#define VINT_SLOT_MODE4 4
  29#define HSYNC_SLOT_H40  230
  30#define HSYNC_END_H40  (HSYNC_SLOT_H40+17)
  31#define HBLANK_START_H40 178 //should be 179 according to Nemesis, but 178 seems to fit slightly better with my test ROM results
  32#define HBLANK_END_H40  0 //should be 5.5 according to Nemesis, but 0 seems to fit better with my test ROM results
  33#define HBLANK_START_H32 233 //should be 147 according to Nemesis which is very different from my test ROM result
  34#define HBLANK_END_H32 0 //should be 5 according to Nemesis, but 0 seems to fit better with my test ROM results
  35#define LINE_CHANGE_H40 165
  36#define LINE_CHANGE_H32 133
  37#define LINE_CHANGE_MODE4 249
  38#define VBLANK_START_H40 (LINE_CHANGE_H40+2)
  39#define VBLANK_START_H32 (LINE_CHANGE_H32+2)
  40#define FIFO_LATENCY    3
  41
  42#define BORDER_TOP_V24     27
  43#define BORDER_TOP_V28     11
  44#define BORDER_TOP_V24_PAL 54
  45#define BORDER_TOP_V28_PAL 38
  46#define BORDER_TOP_V30_PAL 30
  47
  48#define BORDER_BOT_V24     24
  49#define BORDER_BOT_V28     8
  50#define BORDER_BOT_V24_PAL 48
  51#define BORDER_BOT_V28_PAL 32
  52#define BORDER_BOT_V30_PAL 24
  53
  54#define INVALID_LINE (PAL_INACTIVE_START+BORDER_TOP_V30_PAL+BORDER_BOT_V30_PAL)
  55
  56enum {
  57	INACTIVE = 0,
  58	PREPARING, //used for line 0x1FF
  59	ACTIVE
  60};
  61
  62static int32_t color_map[1 << 12];
  63static uint16_t mode4_address_map[0x4000];
  64static uint32_t planar_to_chunky[256];
  65static uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255};
  66
  67static uint8_t debug_base[][3] = {
  68	{127, 127, 127}, //BG
  69	{0, 0, 127},     //A
  70	{127, 0, 0},     //Window
  71	{0, 127, 0},     //B
  72	{127, 0, 127}    //Sprites
  73};
  74
  75static void update_video_params(vdp_context *context)
  76{
  77	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
  78		if (context->regs[REG_MODE_2] & BIT_PAL) {
  79			if (context->flags2 & FLAG2_REGION_PAL) {
  80				context->inactive_start = PAL_INACTIVE_START;
  81				context->border_top = BORDER_TOP_V30_PAL;
  82				context->border_bot = BORDER_BOT_V30_PAL;
  83			} else {
  84				//the behavior here is rather weird and needs more investigation
  85				context->inactive_start = 0xF0;
  86				context->border_top = 1;
  87				context->border_bot = 3;
  88			}
  89		} else {
  90			context->inactive_start = NTSC_INACTIVE_START;
  91			if (context->flags2 & FLAG2_REGION_PAL) {
  92				context->border_top = BORDER_TOP_V28_PAL;
  93				context->border_bot = BORDER_BOT_V28_PAL;
  94			} else {
  95				context->border_top = BORDER_TOP_V28;
  96				context->border_bot = BORDER_TOP_V28;
  97			}
  98		}
  99		if (context->regs[REG_MODE_4] & BIT_H40) {
 100			context->max_sprites_frame = MAX_SPRITES_FRAME;
 101			context->max_sprites_line = MAX_SPRITES_LINE;
 102		} else {
 103			context->max_sprites_frame = MAX_SPRITES_FRAME_H32;
 104			context->max_sprites_line = MAX_SPRITES_LINE_H32;
 105		}
 106		if (context->state == INACTIVE) {
 107			//Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
 108			if (context->vcounter < context->inactive_start) {
 109				context->state = ACTIVE;
 110			} else if (context->vcounter == 0x1FF) {
 111				context->state = PREPARING;
 112			}
 113		}
 114	} else {
 115		context->inactive_start = MODE4_INACTIVE_START;
 116		if (context->flags2 & FLAG2_REGION_PAL) {
 117			context->border_top = BORDER_TOP_V24_PAL;
 118			context->border_bot = BORDER_BOT_V24_PAL;
 119		} else {
 120			context->border_top = BORDER_TOP_V24;
 121			context->border_bot = BORDER_BOT_V24;
 122		}
 123		if (!(context->regs[REG_MODE_1] & BIT_MODE_4)){
 124			context->state = INACTIVE;
 125		} else if (context->state == INACTIVE) {
 126			//Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
 127			if (context->vcounter < context->inactive_start) {
 128				context->state = ACTIVE;
 129			}
 130			else if (context->vcounter == 0x1FF) {
 131				context->state = PREPARING;
 132			}
 133		}
 134	}
 135}
 136
 137static uint8_t color_map_init_done;
 138
 139vdp_context *init_vdp_context(uint8_t region_pal)
 140{
 141	vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE);
 142	if (headless) {
 143		context->output = malloc(LINEBUF_SIZE * sizeof(uint32_t));
 144		context->output_pitch = 0;
 145	} else {
 146		context->cur_buffer = VIDEO_BUFFER_ODD;
 147		context->fb = render_get_video_buffer(VIDEO_BUFFER_ODD, &context->output_pitch);
 148	}
 149	context->sprite_draws = MAX_DRAWS;
 150	context->fifo_write = 0;
 151	context->fifo_read = -1;
 152	context->regs[REG_HINT] = context->hint_counter = 0xFF;
 153
 154	if (!color_map_init_done) {
 155		uint8_t b,g,r;
 156		for (uint16_t color = 0; color < (1 << 12); color++) {
 157			if (color & FBUF_SHADOW) {
 158				b = levels[(color >> 9) & 0x7];
 159				g = levels[(color >> 5) & 0x7];
 160				r = levels[(color >> 1) & 0x7];
 161			} else if(color & FBUF_HILIGHT) {
 162				b = levels[((color >> 9) & 0x7) + 7];
 163				g = levels[((color >> 5) & 0x7) + 7];
 164				r = levels[((color >> 1) & 0x7) + 7];
 165			} else if(color & FBUF_MODE4) {
 166				b = levels[(color >> 4 & 0xC) | (color >> 6 & 0x2)];
 167				g = levels[(color >> 2 & 0x8) | (color >> 1 & 0x4) | (color >> 4 & 0x2)];
 168				r = levels[(color << 1 & 0xC) | (color >> 1 & 0x2)];
 169			} else {
 170				b = levels[(color >> 8) & 0xE];
 171				g = levels[(color >> 4) & 0xE];
 172				r = levels[color & 0xE];
 173			}
 174			color_map[color] = render_map_color(r, g, b);
 175		}
 176		for (uint16_t mode4_addr = 0; mode4_addr < 0x4000; mode4_addr++)
 177		{
 178			uint16_t mode5_addr = mode4_addr & 0x3DFD;
 179			mode5_addr |= mode4_addr << 8 & 0x200;
 180			mode5_addr |= mode4_addr >> 8 & 2;
 181			mode4_address_map[mode4_addr] = mode5_addr;
 182		}
 183		for (uint32_t planar = 0; planar < 256; planar++)
 184		{
 185			uint32_t chunky = 0;
 186			for (int bit = 7; bit >= 0; bit--)
 187			{
 188				chunky = chunky << 4;
 189				chunky |= planar >> bit & 1;
 190			}
 191			planar_to_chunky[planar] = chunky;
 192		}
 193		color_map_init_done = 1;
 194	}
 195	for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++)
 196	{
 197		uint8_t src = color & DBG_SRC_MASK;
 198		if (src > DBG_SRC_S) {
 199			context->debugcolors[color] = 0;
 200		} else {
 201			uint8_t r,g,b;
 202			b = debug_base[src][0];
 203			g = debug_base[src][1];
 204			r = debug_base[src][2];
 205			if (color & DBG_PRIORITY)
 206			{
 207				if (b) {
 208					b += 48;
 209				}
 210				if (g) {
 211					g += 48;
 212				}
 213				if (r) {
 214					r += 48;
 215				}
 216			}
 217			if (color & DBG_SHADOW) {
 218				b /= 2;
 219				g /= 2;
 220				r /=2 ;
 221			}
 222			if (color & DBG_HILIGHT) {
 223				if (b) {
 224					b += 72;
 225				}
 226				if (g) {
 227					g += 72;
 228				}
 229				if (r) {
 230					r += 72;
 231				}
 232			}
 233			context->debugcolors[color] = render_map_color(r, g, b);
 234		}
 235	}
 236	if (region_pal) {
 237		context->flags2 |= FLAG2_REGION_PAL;
 238	}
 239	update_video_params(context);
 240	if (!headless) {
 241		context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
 242	}
 243	return context;
 244}
 245
 246void vdp_free(vdp_context *context)
 247{
 248	free(context);
 249}
 250
 251static int is_refresh(vdp_context * context, uint32_t slot)
 252{
 253	if (context->regs[REG_MODE_4] & BIT_H40) {
 254		return slot == 250 || slot == 26 || slot == 59 || slot == 90 || slot == 122 || slot == 154;
 255	} else {
 256		//TODO: Figure out which slots are refresh when display is off in 32-cell mode
 257		//These numbers are guesses based on H40 numbers
 258		return slot == 243 || slot == 19 || slot == 51 || slot == 83 || slot == 115;
 259		//The numbers below are the refresh slots during active display
 260		//return (slot == 29 || slot == 61 || slot == 93 || slot == 125);
 261	}
 262}
 263
 264static void increment_address(vdp_context *context)
 265{
 266	context->address += context->regs[REG_AUTOINC];
 267	if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
 268		context->address++;
 269	}
 270}
 271
 272static void render_sprite_cells(vdp_context * context)
 273{
 274	sprite_draw * d = context->sprite_draw_list + context->cur_slot;
 275	context->serial_address = d->address;
 276	if (context->cur_slot >= context->sprite_draws) {
 277
 278		uint16_t dir;
 279		int16_t x;
 280		if (d->h_flip) {
 281			x = d->x_pos + 7;
 282			dir = -1;
 283		} else {
 284			x = d->x_pos;
 285			dir = 1;
 286		}
 287		//printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x);
 288		context->cur_slot--;
 289		for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) {
 290			if (x >= 0 && x < 320) {
 291				if (!(context->linebuf[x] & 0xF)) {
 292					context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority;
 293				} else if (context->vdpmem[address] >> 4) {
 294					context->flags2 |= FLAG2_SPRITE_COLLIDE;
 295				}
 296			}
 297			x += dir;
 298			if (x >= 0 && x < 320) {
 299				if (!(context->linebuf[x] & 0xF)) {
 300					context->linebuf[x] = (context->vdpmem[address] & 0xF)  | d->pal_priority;
 301				} else if (context->vdpmem[address] & 0xF) {
 302					context->flags2 |= FLAG2_SPRITE_COLLIDE;
 303				}
 304			}
 305			x += dir;
 306		}
 307	} else {
 308		context->cur_slot--;
 309	}
 310}
 311
 312static void fetch_sprite_cells_mode4(vdp_context * context)
 313{
 314	if (context->sprite_index >= context->sprite_draws) {
 315		sprite_draw * d = context->sprite_draw_list + context->sprite_index;
 316		uint32_t address = mode4_address_map[d->address & 0x3FFF];
 317		context->fetch_tmp[0] = context->vdpmem[address];
 318		context->fetch_tmp[1] = context->vdpmem[address + 1];
 319	}
 320}
 321
 322static void render_sprite_cells_mode4(vdp_context * context)
 323{
 324	if (context->sprite_index >= context->sprite_draws) {
 325		sprite_draw * d = context->sprite_draw_list + context->sprite_index;
 326		uint32_t pixels = planar_to_chunky[context->fetch_tmp[0]] << 1;
 327		pixels |= planar_to_chunky[context->fetch_tmp[1]];
 328		uint32_t address = mode4_address_map[(d->address + 2) & 0x3FFF];
 329		pixels |= planar_to_chunky[context->vdpmem[address]] << 3;
 330		pixels |= planar_to_chunky[context->vdpmem[address + 1]] << 2;
 331		int x = d->x_pos & 0xFF;
 332		for (int i = 28; i >= 0; i -= 4, x++)
 333		{
 334			if (context->linebuf[x] && (pixels >> i & 0xF)) {
 335				if (
 336					((context->regs[REG_MODE_1] & BIT_SPRITE_8PX) && x > 8)
 337					|| ((!(context->regs[REG_MODE_1] & BIT_SPRITE_8PX)) && x < 256)
 338				) {
 339					context->flags2 |= FLAG2_SPRITE_COLLIDE;
 340				}
 341			} else {
 342				context->linebuf[x] = pixels >> i & 0xF;
 343			}
 344		}
 345		context->sprite_index--;
 346	}
 347}
 348
 349static uint32_t mode5_sat_address(vdp_context *context)
 350{
 351	uint32_t addr = context->regs[REG_SAT] << 9;
 352	if (!(context->regs[REG_MODE_2] & BIT_128K_VRAM)) {
 353		addr &= 0xFFFF;
 354	}
 355	if (context->regs[REG_MODE_4] & BIT_H40) {
 356		addr &= 0x1FC00;
 357	}
 358	return addr;
 359}
 360
 361void vdp_print_sprite_table(vdp_context * context)
 362{
 363	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 364		uint16_t sat_address = mode5_sat_address(context);
 365		uint16_t current_index = 0;
 366		uint8_t count = 0;
 367		do {
 368			uint16_t address = current_index * 8 + sat_address;
 369			uint16_t cache_address = current_index * 4;
 370			uint8_t height = ((context->sat_cache[cache_address+2] & 0x3) + 1) * 8;
 371			uint8_t width = (((context->sat_cache[cache_address+2]  >> 2) & 0x3) + 1) * 8;
 372			int16_t y = ((context->sat_cache[cache_address] & 0x3) << 8 | context->sat_cache[cache_address+1]) & 0x1FF;
 373			int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF;
 374			uint16_t link = context->sat_cache[cache_address+3] & 0x7F;
 375			uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3;
 376			uint8_t pri = context->vdpmem[address + 4] >> 7;
 377			uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;
 378			printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
 379			current_index = link;
 380			count++;
 381		} while (current_index != 0 && count < 80);
 382	} else {
 383		uint16_t sat_address = (context->regs[REG_SAT] & 0x7E) << 7;
 384		for (int i = 0; i < 64; i++)
 385		{
 386			uint8_t y = context->vdpmem[mode4_address_map[sat_address + (i ^ 1)]];
 387			if (y == 0xD0) {
 388				break;
 389			}
 390			uint8_t x = context->vdpmem[mode4_address_map[sat_address + 0x80 + i*2 + 1]];
 391			uint16_t tile_address = context->vdpmem[mode4_address_map[sat_address + 0x80 + i*2]] * 32
 392				+ (context->regs[REG_STILE_BASE] << 11 & 0x2000);
 393			if (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) {
 394				tile_address &= ~32;
 395			}
 396			printf("Sprite %d: X=%d, Y=%d, Pat=%X\n", i, x, y, tile_address);
 397		}
 398	}
 399}
 400
 401#define VRAM_READ 0 //0000
 402#define VRAM_WRITE 1 //0001
 403//2 would trigger register write 0010
 404#define CRAM_WRITE 3 //0011
 405#define VSRAM_READ 4 //0100
 406#define VSRAM_WRITE 5//0101
 407//6 would trigger regsiter write 0110
 408//7 is a mystery //0111
 409#define CRAM_READ 8  //1000
 410//9 is also a mystery //1001
 411//A would trigger register write 1010
 412//B is a mystery 1011
 413#define VRAM_READ8 0xC //1100
 414//D is a mystery 1101
 415//E would trigger register write 1110
 416//F is a mystery 1111
 417
 418//Possible theory on how bits work
 419//CD0 = Read/Write flag
 420//CD2,(CD1|CD3) = RAM type
 421//  00 = VRAM
 422//  01 = CRAM
 423//  10 = VSRAM
 424//  11 = VRAM8
 425//Would result in
 426//  7 = VRAM8 write
 427//  9 = CRAM write alias
 428//  B = CRAM write alias
 429//  D = VRAM8 write alias
 430//  F = VRAM8 write alais
 431
 432#define DMA_START 0x20
 433
 434static const char * cd_name(uint8_t cd)
 435{
 436	switch (cd & 0xF)
 437	{
 438	case VRAM_READ:
 439		return "VRAM read";
 440	case VRAM_WRITE:
 441		return "VRAM write";
 442	case CRAM_WRITE:
 443		return "CRAM write";
 444	case VSRAM_READ:
 445		return "VSRAM read";
 446	case VSRAM_WRITE:
 447		return "VSRAM write";
 448	case VRAM_READ8:
 449		return "VRAM read (undocumented 8-bit mode)";
 450	default:
 451		return "invalid";
 452	}
 453}
 454
 455void vdp_print_reg_explain(vdp_context * context)
 456{
 457	char * hscroll[] = {"full", "7-line", "cell", "line"};
 458	printf("**Mode Group**\n"
 459	       "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n"
 460	       "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d, %dK VRAM\n"
 461	       "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n"
 462	       "0C: %.2X | Width: %d, Shadow/Highlight: %s\n",
 463	       context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", (context->regs[REG_MODE_1] & BIT_PAL_SEL) != 0,
 464	           context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled",
 465	       context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled",
 466	           context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, context->regs[REG_MODE_1] & BIT_128K_VRAM ? 128 : 64, 
 467	       context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full",
 468	           hscroll[context->regs[REG_MODE_3] & 0x3],
 469	       context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled");
 470	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 471		printf("\n**Table Group**\n"
 472			   "02: %.2X | Scroll A Name Table:    $%.4X\n"
 473			   "03: %.2X | Window Name Table:      $%.4X\n"
 474			   "04: %.2X | Scroll B Name Table:    $%.4X\n"
 475			   "05: %.2X | Sprite Attribute Table: $%.4X\n"
 476			   "0D: %.2X | HScroll Data Table:     $%.4X\n",
 477			   context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0x38) << 10,
 478			   context->regs[REG_WINDOW], (context->regs[REG_WINDOW] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3C : 0x3E)) << 10,
 479			   context->regs[REG_SCROLL_B], (context->regs[REG_SCROLL_B] & 0x7) << 13,
 480			   context->regs[REG_SAT], mode5_sat_address(context),
 481			   context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x3F) << 10);
 482	} else {
 483		printf("\n**Table Group**\n"
 484			   "02: %.2X | Background Name Table:  $%.4X\n"
 485			   "05: %.2X | Sprite Attribute Table: $%.4X\n"
 486			   "06: %.2X | Sprite Tile Base:       $%.4X\n"
 487			   "08: %.2X | Background X Scroll:    %d\n"
 488			   "09: %.2X | Background Y Scroll:    %d\n",
 489			   context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0xE) << 10,
 490			   context->regs[REG_SAT], (context->regs[REG_SAT] & 0x7E) << 7,
 491			   context->regs[REG_STILE_BASE], (context->regs[REG_STILE_BASE] & 2) << 11,
 492			   context->regs[REG_X_SCROLL], context->regs[REG_X_SCROLL],
 493			   context->regs[REG_Y_SCROLL], context->regs[REG_Y_SCROLL]);
 494			   
 495	}
 496	char * sizes[] = {"32", "64", "invalid", "128"};
 497	printf("\n**Misc Group**\n"
 498	       "07: %.2X | Backdrop Color: $%X\n"
 499	       "0A: %.2X | H-Int Counter: %u\n"
 500	       "0F: %.2X | Auto-increment: $%X\n"
 501	       "10: %.2X | Scroll A/B Size: %sx%s\n",
 502	       context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR],
 503	       context->regs[REG_HINT], context->regs[REG_HINT],
 504	       context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
 505	       context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
 506	char * src_types[] = {"68K", "68K", "Copy", "Fill"};
 507	printf("\n**DMA Group**\n"
 508	       "13: %.2X |\n"
 509		   "14: %.2X | DMA Length: $%.4X words\n"
 510		   "15: %.2X |\n"
 511		   "16: %.2X |\n"
 512		   "17: %.2X | DMA Source Address: $%.6X, Type: %s\n",
 513		   context->regs[REG_DMALEN_L],
 514		   context->regs[REG_DMALEN_H], context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L],
 515		   context->regs[REG_DMASRC_L],
 516		   context->regs[REG_DMASRC_M],
 517		   context->regs[REG_DMASRC_H],
 518		       context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1,
 519			   src_types[context->regs[REG_DMASRC_H] >> 6 & 3]);
 520	uint8_t old_flags = context->flags;
 521	uint8_t old_flags2 = context->flags2;
 522	printf("\n**Internal Group**\n"
 523	       "Address: %X\n"
 524	       "CD:      %X - %s\n"
 525	       "Pending: %s\n"
 526		   "VCounter: %d\n"
 527		   "HCounter: %d\n"
 528		   "VINT Pending: %s\n"
 529		   "HINT Pending: %s\n"
 530		   "Status: %X\n",
 531	       context->address, context->cd, cd_name(context->cd), 
 532		   (context->flags & FLAG_PENDING) ? "word" : (context->flags2 & FLAG2_BYTE_PENDING) ? "byte" : "none",
 533		   context->vcounter, context->hslot*2, (context->flags2 & FLAG2_VINT_PENDING) ? "true" : "false",
 534		   (context->flags2 & FLAG2_HINT_PENDING) ? "true" : "false", vdp_control_port_read(context));
 535	//restore flags as calling vdp_control_port_read can change them
 536	context->flags = old_flags;
 537	context->flags2 = old_flags2;
 538}
 539
 540static uint8_t is_active(vdp_context *context)
 541{
 542	return context->state != INACTIVE && (context->regs[REG_MODE_2] & BIT_DISP_EN) != 0;
 543}
 544
 545static void scan_sprite_table(uint32_t line, vdp_context * context)
 546{
 547	if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line) {
 548		line += 1;
 549		uint16_t ymask, ymin;
 550		uint8_t height_mult;
 551		if (context->double_res) {
 552			line *= 2;
 553			if (context->flags2 & FLAG2_EVEN_FIELD) {
 554				line++;
 555			}
 556			ymask = 0x3FF;
 557			ymin = 256;
 558			height_mult = 16;
 559		} else {
 560			ymask = 0x1FF;
 561			ymin = 128;
 562			height_mult = 8;
 563		}
 564		context->sprite_index &= 0x7F;
 565		//TODO: Implement squirelly behavior documented by Kabuto
 566		if (context->sprite_index >= context->max_sprites_frame) {
 567			context->sprite_index = 0;
 568			return;
 569		}
 570		uint16_t address = context->sprite_index * 4;
 571		line += ymin;
 572		line &= ymask;
 573		uint16_t y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask;
 574		uint8_t height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult;
 575		//printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
 576		if (y <= line && line < (y + height)) {
 577			//printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
 578			context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
 579			context->sprite_info_list[context->slot_counter++].index = context->sprite_index;
 580		}
 581		context->sprite_index = context->sat_cache[address+3] & 0x7F;
 582		if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line)
 583		{
 584			//TODO: Implement squirelly behavior documented by Kabuto
 585			if (context->sprite_index >= context->max_sprites_frame) {
 586				context->sprite_index = 0;
 587				return;
 588			}
 589			address = context->sprite_index * 4;
 590			y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask;
 591			height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult;
 592			//printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
 593			if (y <= line && line < (y + height)) {
 594				//printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
 595				context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
 596				context->sprite_info_list[context->slot_counter++].index = context->sprite_index;
 597			}
 598			context->sprite_index = context->sat_cache[address+3] & 0x7F;
 599		}
 600	}
 601	//TODO: Seems like the overflow flag should be set here if we run out of sprite info slots without hitting the end of the list
 602}
 603
 604static void scan_sprite_table_mode4(vdp_context * context)
 605{
 606	if (context->sprite_index < MAX_SPRITES_FRAME_H32) {
 607		uint32_t line = context->vcounter;
 608		line &= 0xFF;
 609		
 610		uint32_t sat_address = mode4_address_map[(context->regs[REG_SAT] << 7 & 0x3F00) + context->sprite_index];
 611		uint32_t y = context->vdpmem[sat_address+1];
 612		uint32_t size = (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) ? 16 : 8;
 613		
 614		if (y == 0xd0) {
 615			context->sprite_index = MAX_SPRITES_FRAME_H32;
 616			return;
 617		} else {
 618			if (y <= line && line < (y + size)) {
 619				if (!context->slot_counter) {
 620					context->sprite_index = MAX_SPRITES_FRAME_H32;
 621					context->flags |= FLAG_DOT_OFLOW;
 622					return;
 623				}
 624				context->sprite_info_list[--(context->slot_counter)].size = size;
 625				context->sprite_info_list[context->slot_counter].index = context->sprite_index;
 626				context->sprite_info_list[context->slot_counter].y = y;
 627			}
 628			context->sprite_index++;
 629		}
 630		
 631		if (context->sprite_index < MAX_SPRITES_FRAME_H32) {
 632			y = context->vdpmem[sat_address];
 633			if (y == 0xd0) {
 634				context->sprite_index = MAX_SPRITES_FRAME_H32;
 635				return;
 636			} else {
 637				if (y <= line && line < (y + size)) {
 638					if (!context->slot_counter) {
 639						context->sprite_index = MAX_SPRITES_FRAME_H32;
 640						context->flags |= FLAG_DOT_OFLOW;
 641						return;
 642					}
 643					context->sprite_info_list[--(context->slot_counter)].size = size;
 644					context->sprite_info_list[context->slot_counter].index = context->sprite_index;
 645					context->sprite_info_list[context->slot_counter].y = y;
 646				}
 647				context->sprite_index++;
 648			}
 649		}
 650		
 651	}
 652}
 653
 654static void read_sprite_x(uint32_t line, vdp_context * context)
 655{
 656	if (context->cur_slot == context->max_sprites_line) {
 657		context->cur_slot = 0;
 658	}
 659	if (context->cur_slot < context->slot_counter) {
 660		if (context->sprite_draws) {
 661			line += 1;
 662			//in tiles
 663			uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1;
 664			//in pixels
 665			uint8_t height = ((context->sprite_info_list[context->cur_slot].size & 0x3) + 1) * 8;
 666			if (context->double_res) {
 667				line *= 2;
 668				if (context->flags2 & FLAG2_EVEN_FIELD) {
 669					line++;
 670				}
 671				height *= 2;
 672			}
 673			uint16_t ymask, ymin;
 674			if (context->double_res) {
 675				ymask = 0x3FF;
 676				ymin = 256;
 677			} else {
 678				ymask = 0x1FF;
 679				ymin = 128;
 680			}
 681			uint16_t att_addr = mode5_sat_address(context) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
 682			uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
 683			uint8_t pal_priority = (tileinfo >> 9) & 0x70;
 684			uint8_t row;
 685			uint16_t cache_addr = context->sprite_info_list[context->cur_slot].index * 4;
 686			line = (line + ymin) & ymask;
 687			int16_t y = ((context->sat_cache[cache_addr] << 8 | context->sat_cache[cache_addr+1]) & ymask)/* - ymin*/;
 688			if (tileinfo & MAP_BIT_V_FLIP) {
 689				row = (y + height - 1) - line;
 690			} else {
 691				row = line-y;
 692			}
 693			row &= ymask >> 4;
 694			uint16_t address;
 695			if (context->double_res) {
 696				address = ((tileinfo & 0x3FF) << 6) + row * 4;
 697			} else {
 698				address = ((tileinfo & 0x7FF) << 5) + row * 4;
 699			}
 700			int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF;
 701			if (x) {
 702				context->flags |= FLAG_CAN_MASK;
 703			} else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) {
 704				context->flags |= FLAG_MASKED;
 705			}
 706
 707			context->flags &= ~FLAG_DOT_OFLOW;
 708			int16_t i;
 709			if (context->flags & FLAG_MASKED) {
 710				for (i=0; i < width && context->sprite_draws; i++) {
 711					--context->sprite_draws;
 712					context->sprite_draw_list[context->sprite_draws].x_pos = -128;
 713					context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4;
 714				}
 715			} else {
 716				x -= 128;
 717				int16_t base_x = x;
 718				int16_t dir;
 719				if (tileinfo & MAP_BIT_H_FLIP) {
 720					x += (width-1) * 8;
 721					dir = -8;
 722				} else {
 723					dir = 8;
 724				}
 725				//printf("Sprite %d | x: %d, y: %d, width: %d, height: %d, pal_priority: %X, row: %d, tile addr: %X\n", context->sprite_info_list[context->cur_slot].index, x, context->sprite_info_list[context->cur_slot].y, width, height, pal_priority, row, address);
 726				for (i=0; i < width && context->sprite_draws; i++, x += dir) {
 727					--context->sprite_draws;
 728					context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4;
 729					context->sprite_draw_list[context->sprite_draws].x_pos = x;
 730					context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority;
 731					context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0;
 732				}
 733			}
 734			//Used to be i < width
 735			//TODO: Confirm this is the right condition on hardware
 736			if (!context->sprite_draws) {
 737				context->flags |= FLAG_DOT_OFLOW;
 738			}
 739		} else {
 740			context->flags |= FLAG_DOT_OFLOW;
 741		}
 742	}
 743	context->cur_slot++;
 744}
 745
 746static void read_sprite_x_mode4(vdp_context * context)
 747{
 748	if (context->cur_slot >= context->slot_counter) {
 749		uint32_t address = (context->regs[REG_SAT] << 7 & 0x3F00) + 0x80 + context->sprite_info_list[context->cur_slot].index * 2;
 750		address = mode4_address_map[address];
 751		--context->sprite_draws;
 752		uint32_t tile_address = context->vdpmem[address] * 32 + (context->regs[REG_STILE_BASE] << 11 & 0x2000);
 753		if (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) {
 754			tile_address &= ~32;
 755		}
 756		tile_address += (context->vcounter - context->sprite_info_list[context->cur_slot].y)* 4;
 757		context->sprite_draw_list[context->sprite_draws].x_pos = context->vdpmem[address + 1];
 758		context->sprite_draw_list[context->sprite_draws].address = tile_address;
 759		context->cur_slot--;
 760	}
 761}
 762
 763#define CRAM_BITS 0xEEE
 764#define VSRAM_BITS 0x7FF
 765#define VSRAM_DIRTY_BITS 0xF800
 766
 767//rough estimate of slot number at which border display starts
 768#define BG_START_SLOT 6
 769
 770static void update_color_map(vdp_context *context, uint16_t index, uint16_t value)
 771{
 772	context->colors[index] = color_map[value & CRAM_BITS];
 773	context->colors[index + SHADOW_OFFSET] = color_map[(value & CRAM_BITS) | FBUF_SHADOW];
 774	context->colors[index + HIGHLIGHT_OFFSET] = color_map[(value & CRAM_BITS) | FBUF_HILIGHT];
 775	context->colors[index + MODE4_OFFSET] = color_map[(value & CRAM_BITS) | FBUF_MODE4];
 776}
 777
 778void write_cram_internal(vdp_context * context, uint16_t addr, uint16_t value)
 779{
 780	context->cram[addr] = value;
 781	update_color_map(context, addr, value);
 782}
 783
 784static void write_cram(vdp_context * context, uint16_t address, uint16_t value)
 785{
 786	uint16_t addr;
 787	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 788		addr = (address/2) & (CRAM_SIZE-1);
 789	} else {
 790		addr = address & 0x1F;
 791		value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00);
 792	}
 793	write_cram_internal(context, addr, value);
 794	
 795	if (context->hslot >= BG_START_SLOT && (
 796		context->vcounter < context->inactive_start + context->border_bot 
 797		|| context->vcounter > 0x200 - context->border_top
 798	)) {
 799		uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2;
 800		if (context->hslot < bg_end_slot) {
 801			uint32_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET];
 802			context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color;
 803		}
 804	}
 805}
 806
 807static void vdp_advance_dma(vdp_context * context)
 808{
 809	context->regs[REG_DMASRC_L] += 1;
 810	if (!context->regs[REG_DMASRC_L]) {
 811		context->regs[REG_DMASRC_M] += 1;
 812	}
 813	context->address += context->regs[REG_AUTOINC];
 814	uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1;
 815	context->regs[REG_DMALEN_H] = dma_len >> 8;
 816	context->regs[REG_DMALEN_L] = dma_len;
 817	if (!dma_len) {
 818		context->flags &= ~FLAG_DMA_RUN;
 819		context->cd &= 0xF;
 820	}
 821}
 822
 823static void vdp_check_update_sat(vdp_context *context, uint32_t address, uint16_t value)
 824{
 825	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 826		if (!(address & 4)) {
 827			uint32_t sat_address = mode5_sat_address(context);
 828			if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
 829				uint16_t cache_address = address - sat_address;
 830				cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
 831				context->sat_cache[cache_address] = value >> 8;
 832				context->sat_cache[cache_address^1] = value;
 833			}
 834		}
 835	}
 836}
 837
 838void vdp_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t value)
 839{
 840	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 841		if (!(address & 4)) {
 842			uint32_t sat_address = mode5_sat_address(context);
 843			if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
 844				uint16_t cache_address = address - sat_address;
 845				cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
 846				context->sat_cache[cache_address] = value;
 847			}
 848		}
 849	}
 850}
 851
 852static void write_vram_word(vdp_context *context, uint32_t address, uint16_t value)
 853{
 854	address = (address & 0x3FC) | (address >> 1 & 0xFC01) | (address >> 9 & 0x2);
 855	address ^= 1;
 856	//TODO: Support an option to actually have 128KB of VRAM
 857	context->vdpmem[address] = value;
 858}
 859
 860static void write_vram_byte(vdp_context *context, uint32_t address, uint8_t value)
 861{
 862	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 863		address &= 0xFFFF;
 864	} else {
 865		address = mode4_address_map[address & 0x3FFF];
 866	}
 867	context->vdpmem[address] = value;
 868}
 869
 870#define DMA_FILL 0x80
 871#define DMA_COPY 0xC0
 872#define DMA_TYPE_MASK 0xC0
 873static void external_slot(vdp_context * context)
 874{
 875	if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL && context->fifo_read < 0) {
 876		context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1);
 877		fifo_entry * cur = context->fifo + context->fifo_read;
 878		cur->cycle = context->cycles;
 879		cur->address = context->address;
 880		cur->partial = 1;
 881		vdp_advance_dma(context);
 882	}
 883	fifo_entry * start = context->fifo + context->fifo_read;
 884	if (context->fifo_read >= 0 && start->cycle <= context->cycles) {
 885		switch (start->cd & 0xF)
 886		{
 887		case VRAM_WRITE:
 888			if ((context->regs[REG_MODE_2] & (BIT_128K_VRAM|BIT_MODE_5)) == (BIT_128K_VRAM|BIT_MODE_5)) {
 889				vdp_check_update_sat(context, start->address, start->value);
 890				write_vram_word(context, start->address, start->value);
 891			} else {
 892				uint8_t byte = start->partial == 1 ? start->value >> 8 : start->value;
 893				vdp_check_update_sat_byte(context, start->address ^ 1, byte);
 894				write_vram_byte(context, start->address ^ 1, byte);
 895				if (!start->partial) {
 896					start->address = start->address ^ 1;
 897					start->partial = 1;
 898					//skip auto-increment and removal of entry from fifo
 899					return;
 900				}
 901			}
 902			break;
 903		case CRAM_WRITE: {
 904			//printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1));
 905			if (start->partial == 3) {
 906				uint16_t val;
 907				if ((start->address & 1) && (context->regs[REG_MODE_2] & BIT_MODE_5)) {
 908					val = (context->cram[start->address >> 1 & (CRAM_SIZE-1)] & 0xFF) | start->value << 8;
 909				} else {
 910					uint16_t address = (context->regs[REG_MODE_2] & BIT_MODE_5) ? start->address >> 1 & (CRAM_SIZE-1) : start->address & 0x1F;
 911					val = (context->cram[address] & 0xFF00) | start->value;
 912				}
 913				write_cram(context, start->address, val);
 914			} else {
 915				write_cram(context, start->address, start->partial ? context->fifo[context->fifo_write].value : start->value);
 916			}
 917			break;
 918		}
 919		case VSRAM_WRITE:
 920			if (((start->address/2) & 63) < VSRAM_SIZE) {
 921				//printf("VSRAM Write: %X to %X @ frame: %d, vcounter: %d, hslot: %d, cycle: %d\n", start->value, start->address, context->frame, context->vcounter, context->hslot, context->cycles);
 922				if (start->partial == 3) {
 923					if (start->address & 1) {
 924						context->vsram[(start->address/2) & 63] &= 0xFF;
 925						context->vsram[(start->address/2) & 63] |= start->value << 8;
 926					} else {
 927						context->vsram[(start->address/2) & 63] &= 0xFF00;
 928						context->vsram[(start->address/2) & 63] |= start->value;
 929					}
 930				} else {
 931					context->vsram[(start->address/2) & 63] = start->partial ? context->fifo[context->fifo_write].value : start->value;
 932				}
 933			}
 934
 935			break;
 936		}
 937		context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1);
 938		if (context->fifo_read == context->fifo_write) {
 939			if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
 940				context->flags |= FLAG_DMA_RUN;
 941			}
 942			context->fifo_read = -1;
 943		}
 944	} else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_COPY) {
 945		if (context->flags & FLAG_READ_FETCHED) {
 946			write_vram_byte(context, context->address ^ 1, context->prefetch);
 947			
 948			//Update DMA state
 949			vdp_advance_dma(context);
 950			
 951			context->flags &= ~FLAG_READ_FETCHED;
 952		} else {
 953			context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1];
 954			
 955			context->flags |= FLAG_READ_FETCHED;
 956		}
 957	} else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING))) {
 958		switch(context->cd & 0xF)
 959		{
 960		case VRAM_READ:
 961			if (context->flags2 & FLAG2_READ_PENDING) {
 962				context->prefetch |= context->vdpmem[context->address | 1];
 963				context->flags |= FLAG_READ_FETCHED;
 964				context->flags2 &= ~FLAG2_READ_PENDING;
 965				//Should this happen after the prefetch or after the read?
 966				increment_address(context);
 967			} else {
 968				//TODO: 128K VRAM Mode
 969				context->prefetch = context->vdpmem[context->address & 0xFFFE] << 8;
 970				context->flags2 |= FLAG2_READ_PENDING;
 971			}
 972			break;
 973		case VRAM_READ8: {
 974			uint32_t address = context->address ^ 1;
 975			if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
 976				address = mode4_address_map[address & 0x3FFF];
 977			}
 978			context->prefetch = context->vdpmem[address];
 979			context->prefetch |= context->fifo[context->fifo_write].value & 0xFF00;
 980			context->flags |= FLAG_READ_FETCHED;
 981			//Should this happen after the prefetch or after the read?
 982			increment_address(context);
 983			break;
 984		}
 985		case CRAM_READ:
 986			context->prefetch = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS;
 987			context->prefetch |= context->fifo[context->fifo_write].value & ~CRAM_BITS;
 988			context->flags |= FLAG_READ_FETCHED;
 989			//Should this happen after the prefetch or after the read?
 990			increment_address(context);
 991			break;
 992		case VSRAM_READ: {
 993			uint16_t address = (context->address /2) & 63;
 994			if (address >= VSRAM_SIZE) {
 995				address = 0;
 996			}
 997			context->prefetch = context->vsram[address] & VSRAM_BITS;
 998			context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS;
 999			context->flags |= FLAG_READ_FETCHED;
1000			//Should this happen after the prefetch or after the read?
1001			increment_address(context);
1002			break;
1003		}
1004		}
1005	}
1006}
1007
1008static void run_dma_src(vdp_context * context, int32_t slot)
1009{
1010	//TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode
1011	//TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations
1012	if (context->fifo_write == context->fifo_read) {
1013		return;
1014	}
1015	fifo_entry * cur = NULL;
1016	if (!(context->regs[REG_DMASRC_H] & 0x80))
1017	{
1018		//68K -> VDP
1019		if (slot == -1 || !is_refresh(context, slot-1)) {
1020			cur = context->fifo + context->fifo_write;
1021			cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
1022			cur->address = context->address;
1023			cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
1024			cur->cd = context->cd;
1025			cur->partial = 0;
1026			if (context->fifo_read < 0) {
1027				context->fifo_read = context->fifo_write;
1028			}
1029			context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
1030			vdp_advance_dma(context);
1031		}
1032	}
1033}
1034
1035#define WINDOW_RIGHT 0x80
1036#define WINDOW_DOWN  0x80
1037
1038static void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_t address, uint16_t hscroll_val, vdp_context * context)
1039{
1040	uint16_t window_line_shift, v_offset_mask, vscroll_shift;
1041	if (context->double_res) {
1042		line *= 2;
1043		if (context->flags2 & FLAG2_EVEN_FIELD) {
1044			line++;
1045		}
1046		window_line_shift = 4;
1047		v_offset_mask = 0xF;
1048		vscroll_shift = 4;
1049	} else {
1050		window_line_shift = 3;
1051		v_offset_mask = 0x7;
1052		vscroll_shift = 3;
1053	}
1054	//TODO: Further research on vscroll latch behavior and the "first column bug"
1055	if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
1056		if (!column) {
1057			if (context->regs[REG_MODE_4] & BIT_H40) {
1058				//Based on observed behavior documented by Eke-Eke, I'm guessing the VDP
1059				//ends up fetching the last value on the VSRAM bus in the H40 case
1060				//getting the last latched value should be close enough for now
1061				if (!vsram_off) {
1062					context->vscroll_latch[0] = context->vscroll_latch[1];
1063				}
1064			} else {
1065				//supposedly it's always forced to 0 in the H32 case
1066				context->vscroll_latch[0] = context->vscroll_latch[1] = 0;
1067			}
1068		} else if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
1069			context->vscroll_latch[vsram_off] = context->vsram[column - 2 + vsram_off];
1070		}
1071	}
1072	if (!vsram_off) {
1073		uint16_t left_col, right_col;
1074		if (context->regs[REG_WINDOW_H] & WINDOW_RIGHT) {
1075			left_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2 + 2;
1076			right_col = 42;
1077		} else {
1078			left_col = 0;
1079			right_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2;
1080			if (right_col) {
1081				right_col += 2;
1082			}
1083		}
1084		uint16_t top_line, bottom_line;
1085		if (context->regs[REG_WINDOW_V] & WINDOW_DOWN) {
1086			top_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
1087			bottom_line = context->double_res ? 481 : 241;
1088		} else {
1089			top_line = 0;
1090			bottom_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
1091		}
1092		if ((column >= left_col && column < right_col) || (line >= top_line && line < bottom_line)) {
1093			uint16_t address = context->regs[REG_WINDOW] << 10;
1094			uint16_t line_offset, offset, mask;
1095			if (context->regs[REG_MODE_4] & BIT_H40) {
1096				address &= 0xF000;
1097				line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF;
1098				mask = 0x7F;
1099
1100			} else {
1101				address &= 0xF800;
1102				line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF;
1103				mask = 0x3F;
1104			}
1105			if (context->double_res) {
1106				mask <<= 1;
1107				mask |= 1;
1108			}
1109			offset = address + line_offset + (((column - 2) * 2) & mask);
1110			context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
1111			//printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
1112			offset = address + line_offset + (((column - 1) * 2) & mask);
1113			context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
1114			context->v_offset = (line) & v_offset_mask;
1115			context->flags |= FLAG_WINDOW;
1116			return;
1117		}
1118		context->flags &= ~FLAG_WINDOW;
1119	}
1120	//TODO: Verify behavior for 0x20 case
1121	uint16_t vscroll = 0xFF | (context->regs[REG_SCROLL] & 0x30) << 4;
1122	if (context->double_res) {
1123		vscroll <<= 1;
1124		vscroll |= 1;
1125	}
1126	vscroll &= context->vscroll_latch[vsram_off] + line;
1127	context->v_offset = vscroll & v_offset_mask;
1128	//printf("%s | line %d, vsram: %d, vscroll: %d, v_offset: %d\n",(vsram_off ? "B" : "A"), line, context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0], vscroll, context->v_offset);
1129	vscroll >>= vscroll_shift;
1130	uint16_t hscroll_mask;
1131	uint16_t v_mul;
1132	switch(context->regs[REG_SCROLL] & 0x3)
1133	{
1134	case 0:
1135		hscroll_mask = 0x1F;
1136		v_mul = 64;
1137		break;
1138	case 0x1:
1139		hscroll_mask = 0x3F;
1140		v_mul = 128;
1141		break;
1142	case 0x2:
1143		//TODO: Verify this behavior
1144		hscroll_mask = 0x1F;
1145		v_mul = 0;
1146		break;
1147	case 0x3:
1148		hscroll_mask = 0x7F;
1149		v_mul = 256;
1150		break;
1151	}
1152	uint16_t hscroll, offset;
1153	for (int i = 0; i < 2; i++) {
1154		hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask;
1155		offset = address + ((vscroll * v_mul + hscroll*2) & 0x1FFF);
1156		//printf("%s | line: %d, col: %d, x: %d, hs_mask %X, scr reg: %X, tbl addr: %X\n", (vsram_off ? "B" : "A"), line, (column-2+i), hscroll, hscroll_mask, context->regs[REG_SCROLL], offset);
1157		uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
1158		if (i) {
1159			context->col_2 = col_val;
1160		} else {
1161			context->col_1 = col_val;
1162		}
1163	}
1164}
1165
1166static void read_map_scroll_a(uint16_t column, uint32_t line, vdp_context * context)
1167{
1168	read_map_scroll(column, 0, line, (context->regs[REG_SCROLL_A] & 0x38) << 10, context->hscroll_a, context);
1169}
1170
1171static void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context)
1172{
1173	read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context);
1174}
1175
1176static void read_map_mode4(uint16_t column, uint32_t line, vdp_context * context)
1177{
1178	uint32_t address = (context->regs[REG_SCROLL_A] & 0xE) << 10;
1179	//add row
1180	uint32_t vscroll = line;
1181	if (column < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) {
1182		vscroll += context->regs[REG_Y_SCROLL];
1183	}
1184	if (vscroll > 223) {
1185		vscroll -= 224;
1186	}
1187	address += (vscroll >> 3) * 2 * 32;
1188	//add column
1189	address += ((column - (context->hscroll_a >> 3)) & 31) * 2;
1190	//adjust for weird VRAM mapping in Mode 4
1191	address = mode4_address_map[address];
1192	context->col_1 = (context->vdpmem[address] << 8) | context->vdpmem[address+1];
1193}
1194
1195static void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context)
1196{
1197	uint16_t address;
1198	uint16_t vflip_base;
1199	if (context->double_res) {
1200		address = ((col & 0x3FF) << 6);
1201		vflip_base = 60;
1202	} else {
1203		address = ((col & 0x7FF) << 5);
1204		vflip_base = 28;
1205	}
1206	if (col & MAP_BIT_V_FLIP) {
1207		address +=  vflip_base - 4 * context->v_offset;
1208	} else {
1209		address += 4 * context->v_offset;
1210	}
1211	uint8_t pal_priority = (col >> 9) & 0x70;
1212	uint32_t bits = *((uint32_t *)(&context->vdpmem[address]));
1213	if (col & MAP_BIT_H_FLIP) {
1214		uint32_t shift = 28;
1215		for (int i = 0; i < 4; i++)
1216		{
1217			uint8_t right = pal_priority | ((bits >> shift) & 0xF);
1218			shift -= 4;
1219			tmp_buf[offset++] = pal_priority | ((bits >> shift) & 0xF);
1220			shift -= 4;
1221			offset &= SCROLL_BUFFER_MASK;
1222			tmp_buf[offset++] = right;
1223			offset &= SCROLL_BUFFER_MASK;
1224		}
1225	} else {
1226		for (int i = 0; i < 4; i++)
1227		{
1228			uint8_t right = pal_priority | (bits & 0xF);
1229			bits >>= 4;
1230			tmp_buf[offset++] = pal_priority | (bits & 0xF);
1231			offset &= SCROLL_BUFFER_MASK;
1232			bits >>= 4;
1233			tmp_buf[offset++] = right;
1234			offset &= SCROLL_BUFFER_MASK;
1235		}
1236	}
1237}
1238
1239static void render_map_1(vdp_context * context)
1240{
1241	render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context);
1242}
1243
1244static void render_map_2(vdp_context * context)
1245{
1246	render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context);
1247}
1248
1249static void render_map_3(vdp_context * context)
1250{
1251	render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context);
1252}
1253
1254static void fetch_map_mode4(uint16_t col, uint32_t line, vdp_context *context)
1255{
1256	//calculate pixel row to fetch
1257	uint32_t vscroll = line;
1258	if (col < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) {
1259		vscroll += context->regs[REG_Y_SCROLL];
1260	}
1261	if (vscroll > 223) {
1262		vscroll -= 224;
1263	}
1264	vscroll &= 7;
1265	if (context->col_1 & 0x400) {
1266		vscroll = 7 - vscroll;
1267	}
1268	
1269	uint32_t address = mode4_address_map[((context->col_1 & 0x1FF) * 32) + vscroll * 4];
1270	context->fetch_tmp[0] = context->vdpmem[address];
1271	context->fetch_tmp[1] = context->vdpmem[address+1];
1272}
1273
1274static uint8_t composite_normal(vdp_context *context, uint8_t *debug_dst, uint8_t sprite, uint8_t plane_a, uint8_t plane_b, uint8_t bg_index)
1275{
1276	uint8_t pixel = bg_index;
1277	uint8_t src = DBG_SRC_BG;
1278	if (plane_b & 0xF) {
1279		pixel = plane_b;
1280		src = DBG_SRC_B;
1281	}
1282	if (plane_a & 0xF && (plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1283		pixel = plane_a;
1284		src = DBG_SRC_A;
1285	}
1286	if (sprite & 0xF && (sprite & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1287		pixel = sprite;
1288		src = DBG_SRC_S;
1289	}
1290	*debug_dst = src;
1291	return pixel;
1292}
1293typedef struct {
1294	uint8_t index, intensity;
1295} sh_pixel;
1296
1297static sh_pixel composite_highlight(vdp_context *context, uint8_t *debug_dst, uint8_t sprite, uint8_t plane_a, uint8_t plane_b, uint8_t bg_index)
1298{
1299	uint8_t pixel = bg_index;
1300	uint8_t src = DBG_SRC_BG;
1301	uint8_t intensity = 0;
1302	if (plane_b & 0xF) {
1303		pixel = plane_b;
1304		src = DBG_SRC_B;
1305	}
1306	intensity = plane_b & BUF_BIT_PRIORITY;
1307	if (plane_a & 0xF && (plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1308		pixel = plane_a;
1309		src = DBG_SRC_A;
1310	}
1311	intensity |= plane_a & BUF_BIT_PRIORITY;
1312	if (sprite & 0xF && (sprite & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1313		if ((sprite & 0x3F) == 0x3E) {
1314			intensity += BUF_BIT_PRIORITY;
1315		} else if ((sprite & 0x3F) == 0x3F) {
1316			intensity = 0;
1317		} else {
1318			pixel = sprite;
1319			src = DBG_SRC_S;
1320			if ((pixel & 0xF) == 0xE) {
1321				intensity = BUF_BIT_PRIORITY;
1322			} else {
1323				intensity |= pixel & BUF_BIT_PRIORITY;
1324			}
1325		}
1326	}
1327	*debug_dst = src;
1328	return (sh_pixel){.index = pixel, .intensity = intensity};
1329}
1330
1331static void render_normal(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off)
1332{
1333	int start = 0;
1334	if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1335		uint32_t bgcolor = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
1336		for (int i = 0; i < 8; ++i)
1337		{
1338			*(dst++) = bgcolor;
1339			*(debug_dst++) = DBG_SRC_BG;
1340		}
1341		start = 8;
1342	}
1343	uint8_t *sprite_buf = context->linebuf + col * 8 + start;
1344	for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1345	{
1346		uint8_t sprite, plane_a, plane_b;
1347		plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1348		plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1349		sprite = *sprite_buf;
1350		uint8_t pixel = composite_normal(context, debug_dst, sprite, plane_a, plane_b, context->regs[REG_BG_COLOR]);
1351		debug_dst++;
1352		*(dst++) = context->colors[pixel & 0x3F];
1353	}
1354}
1355
1356static void render_highlight(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off)
1357{
1358	int start = 0;
1359	if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1360		uint32_t bgcolor = context->colors[SHADOW_OFFSET + (context->regs[REG_BG_COLOR] & 0x3F)];
1361		for (int i = 0; i < 8; ++i)
1362		{
1363			*(dst++) = bgcolor;
1364			*(debug_dst++) = DBG_SRC_BG | DBG_SHADOW;
1365		}
1366		start = 8;
1367	}
1368	uint8_t *sprite_buf = context->linebuf + col * 8 + start;
1369	for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1370	{
1371		uint8_t sprite, plane_a, plane_b;
1372		plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1373		plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1374		sprite = *sprite_buf;
1375		sh_pixel pixel = composite_highlight(context, debug_dst, sprite, plane_a, plane_b, context->regs[REG_BG_COLOR]);
1376		uint32_t *colors;
1377		if (pixel.intensity == BUF_BIT_PRIORITY << 1) {
1378			colors = context->colors + HIGHLIGHT_OFFSET;
1379		} else if (pixel.intensity) {
1380			colors = context->colors;
1381		} else {
1382			colors = context->colors + SHADOW_OFFSET;
1383		}
1384		debug_dst++;
1385		*(dst++) = colors[pixel.index & 0x3F];
1386	}
1387}
1388
1389static void render_testreg(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off, uint8_t output_disabled, uint8_t test_layer)
1390{
1391	if (output_disabled) {
1392		switch (test_layer)
1393		{
1394		case 0:
1395			for (int i = 0; i < 16; i++)
1396			{
1397				*(dst++) = 0x3F; //TODO: confirm this on hardware
1398				*(debug_dst++) = DBG_SRC_BG;
1399			}
1400			break;
1401		case 1: {
1402			uint8_t *sprite_buf = context->linebuf + col * 8;
1403			for (int i = 0; i < 16; i++)
1404			{
1405				*(dst++) = context->colors[*(sprite_buf++) & 0x3F];
1406				*(debug_dst++) = DBG_SRC_S;
1407			}
1408			break;
1409		}
1410		case 2:
1411			for (int i = 0; i < 16; i++)
1412			{
1413				*(dst++) = context->colors[context->tmp_buf_a[(plane_a_off++) & SCROLL_BUFFER_MASK] & 0x3F];
1414				*(debug_dst++) = DBG_SRC_A;
1415			}
1416			break;
1417		case 3:
1418			for (int i = 0; i < 16; i++)
1419			{
1420				*(dst++) = context->colors[context->tmp_buf_b[(plane_b_off++) & SCROLL_BUFFER_MASK] & 0x3F];
1421				*(debug_dst++) = DBG_SRC_B;
1422			}
1423			break;
1424		}
1425	} else {
1426		int start = 0;
1427		uint8_t *sprite_buf = context->linebuf + col * 8;
1428		if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1429			//TODO: Confirm how test register interacts with column 0 blanking
1430			uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
1431			uint8_t src = DBG_SRC_BG;
1432			for (int i = 0; i < 8; ++i)
1433			{
1434				switch (test_layer)
1435				{
1436				case 1:
1437					pixel &= sprite_buf[i];
1438					if (pixel) {
1439						src = DBG_SRC_S;
1440					}
1441					break;
1442				case 2:
1443					pixel &= context->tmp_buf_a[(plane_a_off + i) & SCROLL_BUFFER_MASK];
1444					if (pixel) {
1445						src = DBG_SRC_A;
1446					}
1447					break;
1448				case 3:
1449					pixel &= context->tmp_buf_b[(plane_b_off + i) & SCROLL_BUFFER_MASK];
1450					if (pixel) {
1451						src = DBG_SRC_B;
1452					}
1453					break;
1454				}
1455				
1456				*(dst++) = context->colors[pixel & 0x3F];
1457				*(debug_dst++) = src;
1458			}
1459			plane_a_off += 8;
1460			plane_b_off += 8;
1461			sprite_buf += 8;
1462			start = 8;
1463		}
1464		for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1465		{
1466			uint8_t sprite, plane_a, plane_b;
1467			plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1468			plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1469			sprite = *sprite_buf;
1470			uint8_t pixel = composite_normal(context, debug_dst, sprite, plane_a, plane_b, 0x3F);
1471			switch (test_layer)
1472			{
1473			case 1:
1474				pixel &= sprite;
1475				if (pixel) {
1476					*debug_dst = DBG_SRC_S;
1477				}
1478				break;
1479			case 2:
1480				pixel &= plane_a;
1481				if (pixel) {
1482					*debug_dst = DBG_SRC_A;
1483				}
1484				break;
1485			case 3:
1486				pixel &= plane_b;
1487				if (pixel) {
1488					*debug_dst = DBG_SRC_B;
1489				}
1490				break;
1491			}
1492			debug_dst++;
1493			*(dst++) = context->colors[pixel & 0x3F];
1494		}
1495	}
1496}
1497
1498static void render_testreg_highlight(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off, uint8_t output_disabled, uint8_t test_layer)
1499{
1500	int start = 0;
1501	uint8_t *sprite_buf = context->linebuf + col * 8;
1502	if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1503		//TODO: Confirm how test register interacts with column 0 blanking
1504		uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
1505		uint8_t src = DBG_SRC_BG | DBG_SHADOW;
1506		for (int i = 0; i < 8; ++i)
1507		{
1508			switch (test_layer)
1509			{
1510			case 1:
1511				pixel &= sprite_buf[i];
1512				if (pixel) {
1513					src = DBG_SRC_S | DBG_SHADOW;
1514				}
1515				break;
1516			case 2:
1517				pixel &= context->tmp_buf_a[(plane_a_off + i) & SCROLL_BUFFER_MASK];
1518				if (pixel) {
1519					src = DBG_SRC_A | DBG_SHADOW;
1520				}
1521				break;
1522			case 3:
1523				pixel &= context->tmp_buf_b[(plane_b_off + i) & SCROLL_BUFFER_MASK];
1524				if (pixel) {
1525					src = DBG_SRC_B | DBG_SHADOW;
1526				}
1527				break;
1528			}
1529			
1530			*(dst++) = context->colors[SHADOW_OFFSET + (pixel & 0x3F)];
1531			*(debug_dst++) = src;
1532		}
1533		plane_a_off += 8;
1534		plane_b_off += 8;
1535		sprite_buf += 8;
1536		start = 8;
1537	}
1538	for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1539	{
1540		uint8_t sprite, plane_a, plane_b;
1541		plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1542		plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1543		sprite = *sprite_buf;
1544		sh_pixel pixel = composite_highlight(context, debug_dst, sprite, plane_a, plane_b, 0x3F);
1545		uint32_t *colors;
1546		if (pixel.intensity == BUF_BIT_PRIORITY << 1) {
1547			colors = context->colors + HIGHLIGHT_OFFSET;
1548		} else if (pixel.intensity) {
1549			colors = context->colors;
1550		} else {
1551			colors = context->colors + SHADOW_OFFSET;
1552		}
1553		if (output_disabled) {
1554			pixel.index = 0x3F;
1555		}
1556		switch (test_layer)
1557		{
1558		case 1:
1559			pixel.index &= sprite;
1560			if (pixel.index) {
1561				*debug_dst = DBG_SRC_S;
1562			}
1563			break;
1564		case 2:
1565			pixel.index &= plane_a;
1566			if (pixel.index) {
1567				*debug_dst = DBG_SRC_A;
1568			}
1569			break;
1570		case 3:
1571			pixel.index &= plane_b;
1572			if (pixel.index) {
1573				*debug_dst = DBG_SRC_B;
1574			}
1575			break;
1576		}
1577		debug_dst++;
1578		*(dst++) = colors[pixel.index & 0x3F];
1579	}
1580}
1581
1582static void render_map_output(uint32_t line, int32_t col, vdp_context * context)
1583{
1584	uint32_t *dst;
1585	uint8_t *debug_dst;
1586	uint8_t output_disabled = (context->test_port & TEST_BIT_DISABLE) != 0;
1587	uint8_t test_layer = context->test_port >> 7 & 3;
1588	if (context->state == PREPARING && !test_layer) {
1589		if (col) {
1590			col -= 2;
1591			dst = context->output + BORDER_LEFT + col * 8;
1592		} else {
1593			dst = context->output;
1594			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
1595			for (int i = 0; i < BORDER_LEFT; i++, dst++)
1596			{
1597				*dst = bg_color;
1598			}
1599			context->done_output = dst;
1600			return;
1601		}
1602		uint32_t color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
1603		for (int i = 0; i < 16; i++)
1604		{
1605			*(dst++) = color;
1606		}
1607		context->done_output = dst;
1608		return;
1609	}
1610	line &= 0xFF;
1611	render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context);
1612	uint8_t *sprite_buf;
1613	uint8_t sprite, plane_a, plane_b;
1614	int plane_a_off, plane_b_off;
1615	if (col)
1616	{
1617		col-=2;
1618		dst = context->output + BORDER_LEFT + col * 8;
1619		debug_dst = context->layer_debug_buf + BORDER_LEFT + col * 8;
1620		
1621		
1622		uint8_t a_src, src;
1623		if (context->flags & FLAG_WINDOW) {
1624			plane_a_off = context->buf_a_off;
1625			a_src = DBG_SRC_W;
1626		} else {
1627			plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF);
1628			a_src = DBG_SRC_A;
1629		}
1630		plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
1631		//printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
1632
1633		if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
1634			if (output_disabled || test_layer) {
1635				render_testreg_highlight(context, col, dst, debug_dst, plane_a_off, plane_b_off, output_disabled, test_layer);
1636			} else {
1637				render_highlight(context, col, dst, debug_dst, plane_a_off, plane_b_off);
1638			}
1639		} else {
1640			if (output_disabled || test_layer) {
1641				render_testreg(context, col, dst, debug_dst, plane_a_off, plane_b_off, output_disabled, test_layer);
1642			} else {
1643				render_normal(context, col, dst, debug_dst, plane_a_off, plane_b_off);
1644			}
1645		}
1646		dst += 16;
1647	} else {
1648		dst = context->output;
1649		debug_dst = context->layer_debug_buf;
1650		uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
1651		if (output_disabled) {
1652			pixel = 0x3F;
1653		}
1654		uint32_t bg_color = context->colors[pixel];
1655		if (test_layer) {
1656			switch(test_layer)
1657			{
1658			case 1:
1659				bg_color = context->colors[0];
1660				for (int i = 0; i < BORDER_LEFT; i++, dst++, debug_dst++)
1661				{
1662					*dst = bg_color;
1663					*debug_dst = DBG_SRC_BG;
1664					
1665				}
1666				break;
1667			case 2: {
1668				//plane A
1669				//TODO: Deal with Window layer
1670				int i;
1671				i = 0;
1672				uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF) + (16 - BORDER_LEFT);
1673				//uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); 
1674				for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++)
1675				{
1676					*dst = context->colors[context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK]];
1677					*debug_dst = DBG_SRC_A;
1678				}
1679				break;
1680			}
1681			case 3: {
1682				//plane B
1683				int i;
1684				i = 0;
1685				uint8_t buf_off = context->buf_b_off - (context->hscroll_b & 0xF) + (16 - BORDER_LEFT);
1686				//uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK); 
1687				for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++)
1688				{
1689					*dst = context->colors[context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK]];
1690					*debug_dst = DBG_SRC_B;
1691				}
1692				break;
1693			}
1694			}
1695		} else {
1696			for (int i = 0; i < BORDER_LEFT; i++, dst++, debug_dst++)
1697			{
1698				*dst = bg_color;
1699				*debug_dst = DBG_SRC_BG;
1700			}
1701		}
1702	}
1703	context->done_output = dst;
1704	context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
1705	context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
1706}
1707
1708static void render_map_mode4(uint32_t line, int32_t col, vdp_context * context)
1709{
1710	uint32_t vscroll = line;
1711	if (col < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) {
1712		vscroll += context->regs[REG_Y_SCROLL];
1713	}
1714	if (vscroll > 223) {
1715		vscroll -= 224;
1716	}
1717	vscroll &= 7;
1718	if (context->col_1 & 0x400) {
1719		//vflip
1720		vscroll = 7 - vscroll;
1721	}
1722	
1723	uint32_t pixels = planar_to_chunky[context->fetch_tmp[0]] << 1;
1724	pixels |=  planar_to_chunky[context->fetch_tmp[1]];
1725	
1726	uint32_t address = mode4_address_map[((context->col_1 & 0x1FF) * 32) + vscroll * 4 + 2];
1727	pixels |= planar_to_chunky[context->vdpmem[address]] << 3;
1728	pixels |= planar_to_chunky[context->vdpmem[address+1]] << 2;
1729	
1730	int i, i_inc, i_limit;
1731	if (context->col_1 & 0x200) {
1732		//hflip
1733		i = 0;
1734		i_inc = 4;
1735		i_limit = 32;
1736	} else {
1737		i = 28;
1738		i_inc = -4;
1739		i_limit = -4;
1740	}
1741	uint8_t pal_priority = (context->col_1 >> 7 & 0x10) | (context->col_1 >> 6 & 0x40);
1742	for (uint8_t *dst = context->tmp_buf_a + context->buf_a_off; i != i_limit; i += i_inc, dst++)
1743	{
1744		*dst = (pixels >> i & 0xF) | pal_priority;
1745	}
1746	context->buf_a_off = (context->buf_a_off + 8) & 15;
1747	
1748	uint8_t bgcolor = 0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET;
1749	uint32_t *dst = context->output + col * 8 + BORDER_LEFT;
1750	uint8_t *debug_dst = context->layer_debug_buf + col * 8 + BORDER_LEFT;
1751	if (context->state == PREPARING) {
1752		for (int i = 0; i < 16; i++)
1753		{
1754			*(dst++) = context->colors[bgcolor];
1755		}
1756		context->done_output = dst;
1757		return;
1758	}
1759	
1760	if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1761		uint8_t *sprite_src = context->linebuf + col * 8;
1762		if (context->regs[REG_MODE_1] & BIT_SPRITE_8PX) {
1763			sprite_src += 8;
1764		}
1765		for (int i = 0; i < 8; i++, sprite_src++)
1766		{
1767			uint8_t *bg_src = context->tmp_buf_a + ((8 + i + col * 8 - (context->hscroll_a & 0x7)) & 15);
1768			if ((*bg_src & 0x4F) > 0x40 || !*sprite_src) {
1769				//background plane has priority and is opaque or sprite layer is transparent
1770				*(dst++) = context->colors[(*bg_src & 0x1F) + MODE4_OFFSET];
1771				*(debug_dst++) = DBG_SRC_A;
1772			} else {
1773				//sprite layer is opaque and not covered by high priority BG pixels
1774				*(dst++) = context->colors[*sprite_src | 0x10 + MODE4_OFFSET];
1775				*(debug_dst++) = DBG_SRC_S;
1776			}
1777		}
1778	} else {
1779		for (int i = 0; i < 8; i++)
1780		{
1781			*(dst++) = context->colors[bgcolor];
1782			*(debug_dst++) = DBG_SRC_BG;
1783		}
1784	}
1785	context->done_output = dst;
1786}
1787
1788static uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19};
1789
1790static void vdp_advance_line(vdp_context *context)
1791{
1792#ifdef TIMING_DEBUG
1793	static uint32_t last_line = 0xFFFFFFFF;
1794	if (last_line != 0xFFFFFFFF) {
1795		uint32_t diff = context->cycles - last_line;
1796		if (diff != MCLKS_LINE) {
1797			printf("Line %d took %d cycles\n", context->vcounter, diff);
1798		}
1799	}
1800	last_line = context->cycles;
1801#endif
1802	uint16_t jump_start, jump_end;
1803	uint8_t is_mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
1804	if (is_mode_5) {
1805		if (context->flags2 & FLAG2_REGION_PAL) {
1806			if (context->regs[REG_MODE_2] & BIT_PAL) {
1807				jump_start = 0x10B;
1808				jump_end = 0x1D2;
1809			} else {
1810				jump_start = 0x103;
1811				jump_end = 0x1CA;
1812			}
1813		} else if (context->regs[REG_MODE_2] & BIT_PAL) {
1814			jump_start = 0x100;
1815			jump_end = 0x1FA;
1816		} else {
1817			jump_start = 0xEB;
1818			jump_end = 0x1E5;
1819		}
1820	} else {
1821		jump_start = 0xDB;
1822		jump_end = 0x1D5;
1823	}
1824
1825	if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM | 1 << VDP_DEBUG_COMPOSITE)) {
1826		uint32_t line = context->vcounter;
1827		if (line >= jump_end) {
1828			line -= jump_end - jump_start;
1829		}
1830		uint32_t total_lines = (context->flags2 & FLAG2_REGION_PAL) ? 313 : 262;
1831		
1832		if (total_lines - line <= context->border_top) {
1833			line -= total_lines - context->border_top;
1834		} else {
1835			line += context->border_top;
1836		}
1837		if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) {
1838			uint32_t *fb = context->debug_fbs[VDP_DEBUG_CRAM] + context->debug_fb_pitch[VDP_DEBUG_CRAM] * line / sizeof(uint32_t);
1839			for (int i = 0; i < 64; i++)
1840			{
1841				for (int x = 0; x < 8; x++)
1842				{
1843					*(fb++) = context->colors[i];
1844				}
1845			}
1846		}
1847		if (
1848			context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)
1849			&& line < (context->inactive_start + context->border_bot + context->border_top)
1850		) {
1851			uint32_t *fb = context->debug_fbs[VDP_DEBUG_COMPOSITE] + context->debug_fb_pitch[VDP_DEBUG_COMPOSITE] * line / sizeof(uint32_t);
1852			for (int i = 0; i < LINEBUF_SIZE; i++)
1853			{
1854				*(fb++) = context->debugcolors[context->layer_debug_buf[i]];
1855			}
1856		}
1857	}
1858	
1859	context->vcounter++;
1860	if (context->vcounter == jump_start) {
1861		context->vcounter = jump_end;
1862	} else {
1863		context->vcounter &= 0x1FF;
1864	}
1865	if (context->state == PREPARING) {
1866		context->state = ACTIVE;
1867	}
1868	if (context->vcounter == 0x1FF) {
1869		context->flags2 &= ~FLAG2_PAUSE;
1870	}
1871
1872	if (context->state != ACTIVE) {
1873		context->hint_counter = context->regs[REG_HINT];
1874	} else if (context->hint_counter) {
1875		context->hint_counter--;
1876	} else {
1877		context->flags2 |= FLAG2_HINT_PENDING;
1878		context->pending_hint_start = context->cycles;
1879		context->hint_counter = context->regs[REG_HINT];
1880	}
1881}
1882
1883static void vdp_update_per_frame_debug(vdp_context *context)
1884{
1885	if (context->enabled_debuggers & (1 << VDP_DEBUG_PLANE)) {
1886		uint32_t pitch;
1887		uint32_t *fb = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_PLANE], &pitch);
1888		uint16_t hscroll_mask;
1889		uint16_t v_mul;
1890		uint16_t vscroll_mask = 0x1F | (context->regs[REG_SCROLL] & 0x30) << 1;
1891		switch(context->regs[REG_SCROLL] & 0x3)
1892		{
1893		case 0:
1894			hscroll_mask = 0x1F;
1895			v_mul = 64;
1896			break;
1897		case 0x1:
1898			hscroll_mask = 0x3F;
1899			v_mul = 128;
1900			break;
1901		case 0x2:
1902			//TODO: Verify this behavior
1903			hscroll_mask = 0x1F;
1904			v_mul = 0;
1905			break;
1906		case 0x3:
1907			hscroll_mask = 0x7F;
1908			v_mul = 256;
1909			break;
1910		}
1911		uint16_t table_address;
1912		switch(context->debug_modes[VDP_DEBUG_PLANE] % 3)
1913		{
1914		case 0:
1915			table_address = context->regs[REG_SCROLL_A] << 10 & 0xE000;
1916			break;
1917		case 1:
1918			table_address = context->regs[REG_SCROLL_B] << 13 & 0xE000;
1919			break;
1920		case 2:
1921			table_address = context->regs[REG_WINDOW] << 10;
1922			if (context->regs[REG_MODE_4] & BIT_H40) {
1923				table_address &= 0xF000;
1924				v_mul = 128;
1925				hscroll_mask = 0x3F;
1926			} else {
1927				table_address &= 0xF800;
1928				v_mul = 64;
1929				hscroll_mask = 0x1F;
1930			}
1931			vscroll_mask = 0x1F;
1932			break;
1933		}
1934		uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR & 0x3F]];
1935		for (uint16_t row = 0; row < 128; row++)
1936		{
1937			uint16_t row_address = table_address + (row & vscroll_mask) * v_mul;
1938			for (uint16_t col = 0; col < 128; col++)
1939			{
1940				uint16_t address = row_address + (col & hscroll_mask) * 2;
1941				//pccv hnnn nnnn nnnn
1942				//
1943				uint16_t entry = context->vdpmem[address] << 8 | context->vdpmem[address + 1];
1944				uint8_t pal = entry >> 9 & 0x30;
1945				
1946				uint32_t *dst = fb + (row * pitch * 8 / sizeof(uint32_t)) + col * 8;
1947				address = (entry & 0x7FF) * 32;
1948				int y_diff = 4;
1949				if (entry & 0x1000) {
1950					y_diff = -4;
1951					address += 7 * 4;
1952				}
1953				int x_diff = 1;
1954				if (entry & 0x800) {
1955					x_diff = -1;
1956					address += 3;
1957				}
1958				for (int y = 0; y < 8; y++)
1959				{
1960					uint16_t trow_address = address;
1961					uint32_t *row_dst = dst;
1962					for (int x = 0; x < 4; x++)
1963					{
1964						uint8_t byte = context->vdpmem[trow_address];
1965						trow_address += x_diff;
1966						uint8_t left, right;
1967						if (x_diff > 0) {
1968							left = byte >> 4;
1969							right = byte & 0xF;
1970						} else {
1971							left = byte & 0xF;
1972							right = byte >> 4;
1973						}
1974						*(row_dst++) = left ? context->colors[left|pal] : bg_color;
1975						*(row_dst++) = right ? context->colors[right|pal] : bg_color;
1976					}
1977					address += y_diff;
1978					dst += pitch / sizeof(uint32_t);
1979				}
1980			}
1981		}
1982		render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_PLANE], 1024);
1983	}
1984	
1985	if (context->enabled_debuggers & (1 << VDP_DEBUG_VRAM)) {
1986		uint32_t pitch;
1987		uint32_t *fb = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_VRAM], &pitch);
1988		
1989		uint8_t pal = (context->debug_modes[VDP_DEBUG_VRAM] % 4) << 4;
1990		for (int y = 0; y < 512; y++)
1991		{
1992			uint32_t *line = fb + y * pitch / sizeof(uint32_t);
1993			int row = y >> 4;
1994			int yoff = y >> 1 & 7;
1995			for (int col = 0; col < 64; col++)
1996			{
1997				uint16_t address = (row * 64 + col) * 32 + yoff * 4;
1998				for (int x = 0; x < 4; x++)
1999				{
2000					uint8_t byte = context->vdpmem[address++];
2001					uint8_t left = byte >> 4 | pal;
2002					uint8_t right = byte & 0xF | pal;
2003					*(line++) = context->colors[left];
2004					*(line++) = context->colors[left];
2005					*(line++) = context->colors[right];
2006					*(line++) = context->colors[right];
2007				}
2008			}
2009		}
2010		
2011		render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_VRAM], 1024);
2012	}
2013	
2014	if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) {
2015		uint32_t starting_line = 512 - 32*4;
2016		uint32_t *line = context->debug_fbs[VDP_DEBUG_CRAM] 
2017			+ context->debug_fb_pitch[VDP_DEBUG_CRAM]  * starting_line / sizeof(uint32_t);
2018		for (int pal = 0; pal < 4; pal ++)
2019		{
2020			uint32_t *cur;
2021			for (int y = 0; y < 31; y++)
2022			{
2023				cur = line;
2024				for (int offset = 0; offset < 16; offset++)
2025				{
2026					for (int x = 0; x < 31; x++)
2027					{
2028						*(cur++) = context->colors[pal * 16 + offset];
2029					}
2030					*(cur++) = 0xFF000000;
2031				}
2032				line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t);
2033			}
2034			cur = line;
2035			for (int x = 0; x < 512; x++)
2036			{
2037				*(cur++) = 0xFF000000;
2038			}
2039			line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t);
2040		}
2041		render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_CRAM], 512);
2042		context->debug_fbs[VDP_DEBUG_CRAM] = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_CRAM], &context->debug_fb_pitch[VDP_DEBUG_CRAM]);
2043	}
2044	if (context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)) {
2045		render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], LINEBUF_SIZE);
2046		context->debug_fbs[VDP_DEBUG_COMPOSITE] = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], &context->debug_fb_pitch[VDP_DEBUG_COMPOSITE]);
2047	}		
2048}
2049
2050void vdp_force_update_framebuffer(vdp_context *context)
2051{
2052	uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 
2053			? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL 
2054			: 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2055			
2056	uint16_t to_fill = lines_max - context->output_lines;
2057	memset(
2058		((char *)context->fb) + context->output_pitch * context->output_lines,
2059		0,
2060		to_fill * context->output_pitch
2061	);
2062	render_video_buffer_updated(context->cur_buffer, context->h40_lines > context->output_lines / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2063	context->fb = render_get_video_buffer(context->cur_buffer, &context->output_pitch);
2064	vdp_update_per_frame_debug(context);
2065}
2066
2067static void advance_output_line(vdp_context *context)
2068{
2069	if (headless) {
2070		if (context->vcounter == context->inactive_start) {
2071			context->frame++;
2072		}
2073		context->vcounter &= 0x1FF;
2074	} else {
2075		uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 
2076			? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL 
2077			: 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2078
2079		if (context->output_lines == lines_max) {
2080			render_video_buffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2081			context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? VIDEO_BUFFER_EVEN : VIDEO_BUFFER_ODD;
2082			context->fb = render_get_video_buffer(context->cur_buffer, &context->output_pitch);
2083			vdp_update_per_frame_debug(context);
2084			context->h40_lines = 0;
2085			context->frame++;
2086			context->output_lines = 0;
2087		}
2088		uint32_t output_line = context->vcounter;
2089		if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
2090			//vcounter increment occurs much later in Mode 4
2091			output_line++;
2092		} 
2093		if (output_line < context->inactive_start + context->border_bot && context->output_lines > 0) {
2094			output_line = context->output_lines++;//context->border_top + context->vcounter;
2095		} else if (output_line >= 0x200 - context->border_top) {
2096			if (output_line == 0x200 - context->border_top) {
2097				//We're at the top of the display, force context->output_lines to be zero to avoid
2098				//potential screen rolling if the mode is changed at an inopportune time
2099				context->output_lines = 0;
2100			}
2101			output_line = context->output_lines++;//context->vcounter - (0x200 - context->border_top);
2102		} else {
2103			output_line = INVALID_LINE;
2104		}
2105		context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line);
2106		context->done_output = context->output;
2107#ifdef DEBUG_FB_FILL
2108		for (int i = 0; i < LINEBUF_SIZE; i++)
2109		{
2110			context->output[i] = 0xFFFF00FF;
2111		}
2112#endif	
2113		if (output_line != INVALID_LINE && (context->regs[REG_MODE_4] & BIT_H40)) {
2114			context->h40_lines++;
2115		}
2116	}
2117}
2118
2119void vdp_release_framebuffer(vdp_context *context)
2120{
2121	render_video_buffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2122	context->output = context->fb = NULL;
2123}
2124
2125void vdp_reacquire_framebuffer(vdp_context *context)
2126{
2127	context->fb = render_get_video_buffer(context->cur_buffer, &context->output_pitch);
2128	uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 
2129			? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2130			: 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2131	if (context->output_lines <= lines_max && context->output_lines > 0) {
2132		context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1));
2133	} else {
2134		context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * INVALID_LINE);
2135	}
2136}
2137
2138static void render_border_garbage(vdp_context *context, uint32_t address, uint8_t *buf, uint8_t buf_off, uint16_t col)
2139{
2140	uint8_t base = col >> 9 & 0x30;
2141	for (int i = 0; i < 4; i++, address++)
2142	{
2143		uint8_t byte = context->vdpmem[address & 0xFFFF];
2144		buf[(buf_off++) & SCROLL_BUFFER_MASK] = base | byte >> 4;
2145		buf[(buf_off++) & SCROLL_BUFFER_MASK] = base | byte & 0xF;
2146	}
2147}
2148
2149static void draw_right_border(vdp_context *context)
2150{
2151	uint32_t *dst = context->output + BORDER_LEFT + ((context->regs[REG_MODE_4] & BIT_H40) ? 320 : 256);
2152	uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
2153	if ((context->test_port & TEST_BIT_DISABLE) != 0) {
2154		pixel = 0x3F;
2155	}
2156	uint32_t bg_color = context->colors[pixel];
2157	uint8_t test_layer = context->test_port >> 7 & 3;
2158	if (test_layer) {
2159		switch(test_layer)
2160			{
2161			case 1:
2162				bg_color = context->colors[0];
2163				for (int i = 0; i < BORDER_RIGHT; i++, dst++)
2164				{
2165					*dst = bg_color;
2166				}
2167				break;
2168			case 2: {
2169				//plane A
2170				//TODO: Deal with Window layer
2171				int i;
2172				i = 0;
2173				uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF);
2174				//uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); 
2175				for (; i < BORDER_RIGHT; buf_off++, i++, dst++)
2176				{
2177					*dst = context->colors[context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK] & 0x3F];
2178				}
2179				break;
2180			}
2181			case 3: {
2182				//plane B
2183				int i;
2184				i = 0;
2185				uint8_t buf_off = context->buf_b_off - (context->hscroll_b & 0xF);
2186				//uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK); 
2187				for (; i < BORDER_RIGHT; buf_off++, i++, dst++)
2188				{
2189					*dst = context->colors[context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK] & 0x3F];
2190				}
2191				break;
2192			}
2193			}
2194	} else {
2195		for (int i = 0; i < BORDER_RIGHT; i++, dst++)
2196		{
2197			*dst = bg_color;
2198		}
2199	}
2200	context->done_output = dst;
2201	context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
2202	context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
2203}
2204
2205#define CHECK_ONLY if (context->cycles >= target_cycles) { return; }
2206#define CHECK_LIMIT if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } context->hslot++; context->cycles += slot_cycles; CHECK_ONLY
2207
2208#define COLUMN_RENDER_BLOCK(column, startcyc) \
2209	case startcyc:\
2210		read_map_scroll_a(column, context->vcounter, context);\
2211		CHECK_LIMIT\
2212	case ((startcyc+1)&0xFF):\
2213		external_slot(context);\
2214		CHECK_LIMIT\
2215	case ((startcyc+2)&0xFF):\
2216		render_map_1(context);\
2217		CHECK_LIMIT\
2218	case ((startcyc+3)&0xFF):\
2219		render_map_2(context);\
2220		CHECK_LIMIT\
2221	case ((startcyc+4)&0xFF):\
2222		read_map_scroll_b(column, context->vcounter, context);\
2223		CHECK_LIMIT\
2224	case ((startcyc+5)&0xFF):\
2225		read_sprite_x(context->vcounter, context);\
2226		CHECK_LIMIT\
2227	case ((startcyc+6)&0xFF):\
2228		render_map_3(context);\
2229		CHECK_LIMIT\
2230	case ((startcyc+7)&0xFF):\
2231		render_map_output(context->vcounter, column, context);\
2232		CHECK_LIMIT
2233
2234#define COLUMN_RENDER_BLOCK_REFRESH(column, startcyc) \
2235	case startcyc:\
2236		read_map_scroll_a(column, context->vcounter, context);\
2237		CHECK_LIMIT\
2238	case (startcyc+1):\
2239		/* refresh, no don't run dma src */\
2240		context->hslot++;\
2241		context->cycles += slot_cycles;\
2242		CHECK_ONLY\
2243	case (startcyc+2):\
2244		render_map_1(context);\
2245		CHECK_LIMIT\
2246	case (startcyc+3):\
2247		render_map_2(context);\
2248		CHECK_LIMIT\
2249	case (startcyc+4):\
2250		read_map_scroll_b(column, context->vcounter, context);\
2251		CHECK_LIMIT\
2252	case (startcyc+5):\
2253		read_sprite_x(context->vcounter, context);\
2254		CHECK_LIMIT\
2255	case (startcyc+6):\
2256		render_map_3(context);\
2257		CHECK_LIMIT\
2258	case (startcyc+7):\
2259		render_map_output(context->vcounter, column, context);\
2260		CHECK_LIMIT
2261		
2262#define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \
2263	case startcyc:\
2264		read_map_mode4(column, context->vcounter, context);\
2265		CHECK_LIMIT\
2266	case ((startcyc+1)&0xFF):\
2267		if (column & 3) {\
2268			scan_sprite_table_mode4(context);\
2269		} else {\
2270			external_slot(context);\
2271		}\
2272		CHECK_LIMIT\
2273	case ((startcyc+2)&0xFF):\
2274		fetch_map_mode4(column, context->vcounter, context);\
2275		CHECK_LIMIT\
2276	case ((startcyc+3)&0xFF):\
2277		render_map_mode4(context->vcounter, column, context);\
2278		CHECK_LIMIT
2279		
2280#define CHECK_LIMIT_HSYNC(slot) \
2281	if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
2282	if (slot >= HSYNC_SLOT_H40 && slot < HSYNC_END_H40) {\
2283		context->cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];\
2284	} else {\
2285		context->cycles += slot_cycles;\
2286	}\
2287	if (slot == 182) {\
2288		context->hslot = 229;\
2289	} else {\
2290		context->hslot++;\
2291	}\
2292	CHECK_ONLY
2293
2294#define SPRITE_RENDER_H40(slot) \
2295	case slot:\
2296		if ((slot) == BG_START_SLOT + LINEBUF_SIZE/2) {\
2297			advance_output_line(context);\
2298		}\
2299		if (slot == 168 || slot == 247 || slot == 248) {\
2300			render_border_garbage(\
2301				context,\
2302				context->sprite_draw_list[context->cur_slot].address,\
2303				context->tmp_buf_b,\
2304				context->buf_b_off + (slot == 247 ? 0 : 8),\
2305				slot == 247 ? context->col_1 : context->col_2\
2306			);\
2307			if (slot == 248) {\
2308				context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2309				context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2310			}\
2311		} else if (slot == 243) {\
2312			render_border_garbage(\
2313				context,\
2314				context->sprite_draw_list[context->cur_slot].address,\
2315				context->tmp_buf_a,\
2316				context->buf_a_off,\
2317				context->col_1\
2318			);\
2319		} else if (slot == 169) {\
2320			draw_right_border(context);\
2321		}\
2322		render_sprite_cells( context);\
2323		scan_sprite_table(context->vcounter, context);\
2324		CHECK_LIMIT_HSYNC(slot)
2325
2326//Note that the line advancement check will fail if BG_START_SLOT is > 6
2327//as we're bumping up against the hcounter jump
2328#define SPRITE_RENDER_H32(slot) \
2329	case slot:\
2330		if ((slot) == BG_START_SLOT + (256+HORIZ_BORDER)/2) {\
2331			advance_output_line(context);\
2332		}\
2333		if (slot == 136 || slot == 247 || slot == 248) {\
2334			render_border_garbage(\
2335				context,\
2336				context->sprite_draw_list[context->cur_slot].address,\
2337				context->tmp_buf_b,\
2338				context->buf_b_off + (slot == 247 ? 0 : 8),\
2339				slot == 247 ? context->col_1 : context->col_2\
2340			);\
2341			if (slot == 248) {\
2342				context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2343				context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2344			}\
2345		} else if (slot == 137) {\
2346			draw_right_border(context);\
2347		}\
2348		render_sprite_cells( context);\
2349		scan_sprite_table(context->vcounter, context);\
2350		if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
2351		if (slot == 147) {\
2352			context->hslot = 233;\
2353		} else {\
2354			context->hslot++;\
2355		}\
2356		context->cycles += slot_cycles;\
2357		CHECK_ONLY
2358		
2359#define MODE4_CHECK_SLOT_LINE(slot) \
2360		if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
2361		if ((slot) == BG_START_SLOT + (256+HORIZ_BORDER)/2) {\
2362			advance_output_line(context);\
2363		}\
2364		if ((slot) == 147) {\
2365			context->hslot = 233;\
2366		} else {\
2367			context->hslot++;\
2368		}\
2369		context->cycles += slot_cycles;\
2370		if ((slot+1) == LINE_CHANGE_MODE4) {\
2371			vdp_advance_line(context);\
2372			if (context->vcounter == 192) {\
2373				return;\
2374			}\
2375		}\
2376		CHECK_ONLY
2377
2378#define CALC_SLOT(slot, increment) ((slot+increment) > 147 && (slot+increment) < 233 ? (slot+increment-148+233): (slot+increment))
2379		
2380#define SPRITE_RENDER_H32_MODE4(slot) \
2381	case slot:\
2382		read_sprite_x_mode4(context);\
2383		MODE4_CHECK_SLOT_LINE(slot)\
2384	case CALC_SLOT(slot, 1):\
2385		read_sprite_x_mode4(context);\
2386		MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot,1))\
2387	case CALC_SLOT(slot, 2):\
2388		fetch_sprite_cells_mode4(context);\
2389		MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 2))\
2390	case CALC_SLOT(slot, 3):\
2391		if ((slot + 3) == 140) {\
2392			uint32_t *dst = context->output + BORDER_LEFT + 256 + 8;\
2393			uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];\
2394			for (int i = 0; i < BORDER_RIGHT-8; i++, dst++)\
2395			{\
2396				*dst = bgcolor;\
2397			}\
2398			context->done_output = dst;\
2399		}\
2400		render_sprite_cells_mode4(context);\
2401		MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 3))\
2402	case CALC_SLOT(slot, 4):\
2403		fetch_sprite_cells_mode4(context);\
2404		MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 4))\
2405	case CALC_SLOT(slot, 5):\
2406		render_sprite_cells_mode4(context);\
2407		MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 5))
2408
2409static void vdp_h40(vdp_context * context, uint32_t target_cycles)
2410{
2411	uint16_t address;
2412	uint32_t mask;
2413	uint32_t const slot_cycles = MCLKS_SLOT_H40;
2414	switch(context->hslot)
2415	{
2416	for (;;)
2417	{
2418	case 165:
2419		if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) {
2420			//TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode
2421			//See note in vdp_h32 for why this was originally moved out of read_map_scroll
2422			//Skitchin' has a similar problem, but uses H40 mode. It seems to be able to hit the extern slot at 232
2423			//pretty consistently
2424			context->vscroll_latch[0] = context->vsram[0];
2425			context->vscroll_latch[1] = context->vsram[1];
2426		}
2427		if (context->state == PREPARING) {
2428			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2429			uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2430			if (dst >= context->done_output) {
2431				*dst = bg_color;
2432			}
2433			dst++;
2434			if (dst >= context->done_output) {
2435				*dst = bg_color;
2436			}
2437			external_slot(context);
2438		} else {
2439			render_sprite_cells(context);
2440		}
2441		CHECK_LIMIT
2442	case 166:
2443		if (context->state == PREPARING) {
2444			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2445			uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2446			if (dst >= context->done_output) {
2447				*dst = bg_color;
2448			}
2449			dst++;
2450			if (dst >= context->done_output) {
2451				*dst = bg_color;
2452			}
2453			external_slot(context);
2454		} else {
2455			render_sprite_cells(context);
2456		}
2457		if (context->vcounter == context->inactive_start) {
2458			context->hslot++;
2459			context->cycles += slot_cycles;
2460			return;
2461		}
2462		CHECK_LIMIT
2463	//sprite attribute table scan starts
2464	case 167:
2465		if (context->state == PREPARING) {
2466			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2467			uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2468			for (int i = 0; i < LINEBUF_SIZE - 2 * (context->hslot - BG_START_SLOT); i++, dst++)
2469			{
2470				if (dst >= context->done_output) {
2471					*dst = bg_color;
2472				}
2473			}
2474		}
2475		context->sprite_index = 0x80;
2476		context->slot_counter = 0;
2477		render_border_garbage(
2478			context,
2479			context->sprite_draw_list[context->cur_slot].address,
2480			context->tmp_buf_b, context->buf_b_off,
2481			context->col_1
2482		);
2483		render_sprite_cells(context);
2484		scan_sprite_table(context->vcounter, context);
2485		CHECK_LIMIT
2486	SPRITE_RENDER_H40(168)
2487	SPRITE_RENDER_H40(169)
2488	SPRITE_RENDER_H40(170)
2489	SPRITE_RENDER_H40(171)
2490	SPRITE_RENDER_H40(172)
2491	SPRITE_RENDER_H40(173)
2492	SPRITE_RENDER_H40(174)
2493	SPRITE_RENDER_H40(175)
2494	SPRITE_RENDER_H40(176)
2495	SPRITE_RENDER_H40(177)//End of border?
2496	SPRITE_RENDER_H40(178)
2497	SPRITE_RENDER_H40(179)
2498	SPRITE_RENDER_H40(180)
2499	SPRITE_RENDER_H40(181)
2500	SPRITE_RENDER_H40(182)
2501	SPRITE_RENDER_H40(229)
2502	//!HSYNC asserted
2503	SPRITE_RENDER_H40(230)
2504	SPRITE_RENDER_H40(231)
2505	case 232:
2506		external_slot(context);
2507		CHECK_LIMIT_HSYNC(232)
2508	SPRITE_RENDER_H40(233)
2509	SPRITE_RENDER_H40(234)
2510	SPRITE_RENDER_H40(235)
2511	SPRITE_RENDER_H40(236)
2512	SPRITE_RENDER_H40(237)
2513	SPRITE_RENDER_H40(238)
2514	SPRITE_RENDER_H40(239)
2515	SPRITE_RENDER_H40(240)
2516	SPRITE_RENDER_H40(241)
2517	SPRITE_RENDER_H40(242)
2518	SPRITE_RENDER_H40(243) //provides "garbage" for border when plane A selected
2519	case 244:
2520		address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
2521		mask = 0;
2522		if (context->regs[REG_MODE_3] & 0x2) {
2523			mask |= 0xF8;
2524		}
2525		if (context->regs[REG_MODE_3] & 0x1) {
2526			mask |= 0x7;
2527		}
2528		render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2);
2529		address += (context->vcounter & mask) * 4;
2530		context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
2531		context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
2532		//printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
2533		if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); }
2534		context->hslot++;
2535		context->cycles += h40_hsync_cycles[14];
2536		CHECK_ONLY //provides "garbage" for border when plane A selected
2537	//!HSYNC high
2538	SPRITE_RENDER_H40(245)
2539	SPRITE_RENDER_H40(246)
2540	SPRITE_RENDER_H40(247) //provides "garbage" for border when plane B selected
2541	SPRITE_RENDER_H40(248) //provides "garbage" for border when plane B selected
2542	case 249:
2543		read_map_scroll_a(0, context->vcounter, context);
2544		CHECK_LIMIT
2545	SPRITE_RENDER_H40(250)
2546	case 251:
2547		render_map_1(context);
2548		scan_sprite_table(context->vcounter, context);//Just a guess
2549		CHECK_LIMIT
2550	case 252:
2551		render_map_2(context);
2552		scan_sprite_table(context->vcounter, context);//Just a guess
2553		CHECK_LIMIT
2554	case 253:
2555		read_map_scroll_b(0, context->vcounter, context);
2556		CHECK_LIMIT
2557	SPRITE_RENDER_H40(254)
2558	case 255:
2559		render_map_3(context);
2560		scan_sprite_table(context->vcounter, context);//Just a guess
2561		CHECK_LIMIT
2562	case 0:
2563		render_map_output(context->vcounter, 0, context);
2564		scan_sprite_table(context->vcounter, context);//Just a guess
2565		//seems like the sprite table scan fills a shift register
2566		//values are FIFO, but unused slots precede used slots
2567		//so we set cur_slot to slot_counter and let it wrap around to
2568		//the beginning of the list
2569		context->cur_slot = context->slot_counter;
2570		context->sprite_draws = MAX_DRAWS;
2571		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
2572		CHECK_LIMIT
2573	COLUMN_RENDER_BLOCK(2, 1)
2574	COLUMN_RENDER_BLOCK(4, 9)
2575	COLUMN_RENDER_BLOCK(6, 17)
2576	COLUMN_RENDER_BLOCK_REFRESH(8, 25)
2577	COLUMN_RENDER_BLOCK(10, 33)
2578	COLUMN_RENDER_BLOCK(12, 41)
2579	COLUMN_RENDER_BLOCK(14, 49)
2580	COLUMN_RENDER_BLOCK_REFRESH(16, 57)
2581	COLUMN_RENDER_BLOCK(18, 65)
2582	COLUMN_RENDER_BLOCK(20, 73)
2583	COLUMN_RENDER_BLOCK(22, 81)
2584	COLUMN_RENDER_BLOCK_REFRESH(24, 89)
2585	COLUMN_RENDER_BLOCK(26, 97)
2586	COLUMN_RENDER_BLOCK(28, 105)
2587	COLUMN_RENDER_BLOCK(30, 113)
2588	COLUMN_RENDER_BLOCK_REFRESH(32, 121)
2589	COLUMN_RENDER_BLOCK(34, 129)
2590	COLUMN_RENDER_BLOCK(36, 137)
2591	COLUMN_RENDER_BLOCK(38, 145)
2592	COLUMN_RENDER_BLOCK_REFRESH(40, 153)
2593	case 161:
2594		external_slot(context);
2595		CHECK_LIMIT
2596	case 162:
2597		external_slot(context);
2598		CHECK_LIMIT
2599	//sprite render to line buffer starts
2600	case 163:
2601		context->cur_slot = MAX_DRAWS-1;
2602		memset(context->linebuf, 0, LINEBUF_SIZE);
2603		render_border_garbage(
2604			context,
2605			context->sprite_draw_list[context->cur_slot].address,
2606			context->tmp_buf_a, context->buf_a_off,
2607			context->col_1
2608		);
2609		render_sprite_cells(context);
2610		CHECK_LIMIT
2611	case 164:
2612		render_border_garbage(
2613			context,
2614			context->sprite_draw_list[context->cur_slot].address,
2615			context->tmp_buf_a, context->buf_a_off + 8,
2616			context->col_2
2617		);
2618		render_sprite_cells(context);
2619		if (context->flags & FLAG_DMA_RUN) {
2620			run_dma_src(context, -1);
2621		}
2622		context->hslot++;
2623		context->cycles += slot_cycles;
2624		vdp_advance_line(context);
2625		CHECK_ONLY
2626	}
2627	default:
2628		context->hslot++;
2629		context->cycles += slot_cycles;
2630		return;
2631	}
2632}
2633
2634static void vdp_h32(vdp_context * context, uint32_t target_cycles)
2635{
2636	uint16_t address;
2637	uint32_t mask;
2638	uint32_t const slot_cycles = MCLKS_SLOT_H32;
2639	switch(context->hslot)
2640	{
2641	for (;;)
2642	{
2643	case 133:
2644		if (context->state == PREPARING) {
2645			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2646			uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2647			if (dst >= context->done_output) {
2648				*dst = bg_color;
2649			}
2650			dst++;
2651			if (dst >= context->done_output) {
2652				*dst = bg_color;
2653			}
2654			external_slot(context);
2655		} else {
2656			render_sprite_cells(context);
2657		}
2658		CHECK_LIMIT
2659	case 134:
2660		if (context->state == PREPARING) {
2661			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2662			uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2663			if (dst >= context->done_output) {
2664				*dst = bg_color;
2665			}
2666			dst++;
2667			if (dst >= context->done_output) {
2668				*dst = bg_color;
2669			}
2670			external_slot(context);
2671		} else {
2672			render_sprite_cells(context);
2673		}
2674		if (context->vcounter == context->inactive_start) {
2675			context->hslot++;
2676			context->cycles += slot_cycles;
2677			return;
2678		}
2679		CHECK_LIMIT
2680	//sprite attribute table scan starts
2681	case 135:
2682		if (context->state == PREPARING) {
2683			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2684			uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2685			for (int i = 0; i < (256+HORIZ_BORDER) - 2 * (context->hslot - BG_START_SLOT); i++)
2686			{
2687				if (dst >= context->done_output) {
2688					*(dst++) = bg_color;
2689				}
2690			}
2691		}
2692		context->sprite_index = 0x80;
2693		context->slot_counter = 0;
2694		render_border_garbage(
2695			context,
2696			context->sprite_draw_list[context->cur_slot].address,
2697			context->tmp_buf_b, context->buf_b_off,
2698			context->col_1
2699		);
2700		render_sprite_cells(context);
2701		scan_sprite_table(context->vcounter, context);
2702		CHECK_LIMIT
2703	SPRITE_RENDER_H32(136)
2704	SPRITE_RENDER_H32(137)
2705	SPRITE_RENDER_H32(138)
2706	SPRITE_RENDER_H32(139)
2707	SPRITE_RENDER_H32(140)
2708	SPRITE_RENDER_H32(141)
2709	SPRITE_RENDER_H32(142)
2710	SPRITE_RENDER_H32(143)
2711	SPRITE_RENDER_H32(144)
2712	case 145:
2713		external_slot(context);
2714		CHECK_LIMIT
2715	SPRITE_RENDER_H32(146)
2716	SPRITE_RENDER_H32(147)
2717	SPRITE_RENDER_H32(233)
2718	SPRITE_RENDER_H32(234)
2719	SPRITE_RENDER_H32(235)
2720	//HSYNC start
2721	SPRITE_RENDER_H32(236)
2722	SPRITE_RENDER_H32(237)
2723	SPRITE_RENDER_H32(238)
2724	SPRITE_RENDER_H32(239)
2725	SPRITE_RENDER_H32(240)
2726	SPRITE_RENDER_H32(241)
2727	SPRITE_RENDER_H32(242)
2728	case 243:
2729		if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) {
2730			//TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode
2731			//Top Gear 2 has a very efficient HINT routine that can occassionally hit this slot with a VSRAM write
2732			//Since CRAM-updatnig HINT routines seem to indicate that my HINT latency is perhaps slightly too high
2733			//the most reasonable explanation is that vscroll is latched before this slot, but tests are needed
2734			//to confirm that one way or another
2735			context->vscroll_latch[0] = context->vsram[0];
2736			context->vscroll_latch[1] = context->vsram[1];
2737		}
2738		external_slot(context);
2739		//provides "garbage" for border when plane A selected
2740		render_border_garbage(
2741				context,
2742				context->sprite_draw_list[context->cur_slot].address,
2743				context->tmp_buf_a,
2744				context->buf_a_off,
2745				context->col_1
2746			);
2747		CHECK_LIMIT
2748	case 244:
2749		address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
2750		mask = 0;
2751		if (context->regs[REG_MODE_3] & 0x2) {
2752			mask |= 0xF8;
2753		}
2754		if (context->regs[REG_MODE_3] & 0x1) {
2755			mask |= 0x7;
2756		}
2757		render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2);
2758		address += (context->vcounter & mask) * 4;
2759		context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
2760		context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
2761		//printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
2762		CHECK_LIMIT //provides "garbage" for border when plane A selected
2763	SPRITE_RENDER_H32(245)
2764	SPRITE_RENDER_H32(246)
2765	SPRITE_RENDER_H32(247) //provides "garbage" for border when plane B selected
2766	SPRITE_RENDER_H32(248) //provides "garbage" for border when plane B selected
2767	//!HSYNC high
2768	case 249:
2769		read_map_scroll_a(0, context->vcounter, context);
2770		CHECK_LIMIT
2771	SPRITE_RENDER_H32(250)
2772	case 251:
2773		render_map_1(context);
2774		scan_sprite_table(context->vcounter, context);//Just a guess
2775		CHECK_LIMIT
2776	case 252:
2777		render_map_2(context);
2778		scan_sprite_table(context->vcounter, context);//Just a guess
2779		CHECK_LIMIT
2780	case 253:
2781		read_map_scroll_b(0, context->vcounter, context);
2782		CHECK_LIMIT
2783	case 254:
2784		render_sprite_cells(context);
2785		scan_sprite_table(context->vcounter, context);
2786		CHECK_LIMIT
2787	case 255:
2788		render_map_3(context);
2789		scan_sprite_table(context->vcounter, context);//Just a guess
2790		CHECK_LIMIT
2791	case 0:
2792		render_map_output(context->vcounter, 0, context);
2793		scan_sprite_table(context->vcounter, context);//Just a guess
2794		//reverse context slot counter so it counts the number of sprite slots
2795		//filled rather than the number of available slots
2796		//context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
2797		context->cur_slot = context->slot_counter;
2798		context->sprite_draws = MAX_DRAWS_H32;
2799		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
2800		CHECK_LIMIT
2801	COLUMN_RENDER_BLOCK(2, 1)
2802	COLUMN_RENDER_BLOCK(4, 9)
2803	COLUMN_RENDER_BLOCK(6, 17)
2804	COLUMN_RENDER_BLOCK_REFRESH(8, 25)
2805	COLUMN_RENDER_BLOCK(10, 33)
2806	COLUMN_RENDER_BLOCK(12, 41)
2807	COLUMN_RENDER_BLOCK(14, 49)
2808	COLUMN_RENDER_BLOCK_REFRESH(16, 57)
2809	COLUMN_RENDER_BLOCK(18, 65)
2810	COLUMN_RENDER_BLOCK(20, 73)
2811	COLUMN_RENDER_BLOCK(22, 81)
2812	COLUMN_RENDER_BLOCK_REFRESH(24, 89)
2813	COLUMN_RENDER_BLOCK(26, 97)
2814	COLUMN_RENDER_BLOCK(28, 105)
2815	COLUMN_RENDER_BLOCK(30, 113)
2816	COLUMN_RENDER_BLOCK_REFRESH(32, 121)
2817	case 129:
2818		external_slot(context);
2819		CHECK_LIMIT
2820	case 130: {
2821		external_slot(context);
2822		CHECK_LIMIT
2823	}
2824	//sprite render to line buffer starts
2825	case 131:
2826		context->cur_slot = MAX_DRAWS_H32-1;
2827		memset(context->linebuf, 0, LINEBUF_SIZE);
2828		render_border_garbage(
2829			context,
2830			context->sprite_draw_list[context->cur_slot].address,
2831			context->tmp_buf_a, context->buf_a_off,
2832			context->col_1
2833		);
2834		render_sprite_cells(context);
2835		CHECK_LIMIT
2836	case 132:
2837		render_border_garbage(
2838			context,
2839			context->sprite_draw_list[context->cur_slot].address,
2840			context->tmp_buf_a, context->buf_a_off + 8,
2841			context->col_2
2842		);
2843		render_sprite_cells(context);
2844		if (context->flags & FLAG_DMA_RUN) {
2845			run_dma_src(context, -1);
2846		}
2847		context->hslot++;
2848		context->cycles += slot_cycles;
2849		vdp_advance_line(context);
2850		CHECK_ONLY
2851	}
2852	default:
2853		context->hslot++;
2854		context->cycles += MCLKS_SLOT_H32;
2855	}
2856}
2857
2858static void vdp_h32_mode4(vdp_context * context, uint32_t target_cycles)
2859{
2860	uint16_t address;
2861	uint32_t mask;
2862	uint32_t const slot_cycles = MCLKS_SLOT_H32;
2863	switch(context->hslot)
2864	{
2865	for (;;)
2866	{
2867	//sprite rendering starts
2868	SPRITE_RENDER_H32_MODE4(137)
2869	SPRITE_RENDER_H32_MODE4(143)
2870	case 234:
2871		external_slot(context);
2872		CHECK_LIMIT
2873	case 235:
2874		external_slot(context);
2875		CHECK_LIMIT
2876	//!HSYNC low
2877	case 236:
2878		external_slot(context);
2879		CHECK_LIMIT
2880	case 237:
2881		external_slot(context);
2882		CHECK_LIMIT
2883	case 238:
2884		external_slot(context);
2885		CHECK_LIMIT
2886	SPRITE_RENDER_H32_MODE4(239)
2887	SPRITE_RENDER_H32_MODE4(245)
2888	case 251:
2889		external_slot(context);
2890		CHECK_LIMIT
2891	case 252:
2892		external_slot(context);
2893		if (context->regs[REG_MODE_1] & BIT_HSCRL_LOCK && context->vcounter < 16) {
2894			context->hscroll_a = 0;
2895		} else {
2896			context->hscroll_a = context->regs[REG_X_SCROLL];
2897		}
2898		CHECK_LIMIT
2899	case 253:
2900		context->sprite_index = 0;
2901		context->slot_counter = MAX_DRAWS_H32_MODE4;
2902		scan_sprite_table_mode4(context);
2903		CHECK_LIMIT
2904	case 254:
2905		scan_sprite_table_mode4(context);
2906		CHECK_LIMIT
2907	case 255:
2908		scan_sprite_table_mode4(context);
2909		CHECK_LIMIT
2910	case 0: {
2911		scan_sprite_table_mode4(context);
2912		uint32_t *dst = context->output;;
2913		uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
2914		for (int i = 0; i < BORDER_LEFT-8; i++, dst++)
2915		{
2916			*dst = bgcolor;
2917		}
2918		context->done_output = dst;
2919		CHECK_LIMIT
2920	}
2921	case 1:
2922		scan_sprite_table_mode4(context);
2923		CHECK_LIMIT
2924	case 2:
2925		scan_sprite_table_mode4(context);
2926		CHECK_LIMIT
2927	case 3:
2928		scan_sprite_table_mode4(context);
2929		CHECK_LIMIT
2930	case 4: {
2931		scan_sprite_table_mode4(context);
2932		context->buf_a_off = 8;
2933		memset(context->tmp_buf_a, 0, 8);
2934		uint32_t *dst = context->output + BORDER_LEFT - 8;
2935		uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
2936		for (int i = 0; i < 8; i++, dst++)
2937		{
2938			*dst = bgcolor;
2939		}
2940		context->done_output = dst;
2941		CHECK_LIMIT
2942	}
2943	COLUMN_RENDER_BLOCK_MODE4(0, 5)
2944	COLUMN_RENDER_BLOCK_MODE4(1, 9)
2945	COLUMN_RENDER_BLOCK_MODE4(2, 13)
2946	COLUMN_RENDER_BLOCK_MODE4(3, 17)
2947	COLUMN_RENDER_BLOCK_MODE4(4, 21)
2948	COLUMN_RENDER_BLOCK_MODE4(5, 25)
2949	COLUMN_RENDER_BLOCK_MODE4(6, 29)
2950	COLUMN_RENDER_BLOCK_MODE4(7, 33)
2951	COLUMN_RENDER_BLOCK_MODE4(8, 37)
2952	COLUMN_RENDER_BLOCK_MODE4(9, 41)
2953	COLUMN_RENDER_BLOCK_MODE4(10, 45)
2954	COLUMN_RENDER_BLOCK_MODE4(11, 49)
2955	COLUMN_RENDER_BLOCK_MODE4(12, 53)
2956	COLUMN_RENDER_BLOCK_MODE4(13, 57)
2957	COLUMN_RENDER_BLOCK_MODE4(14, 61)
2958	COLUMN_RENDER_BLOCK_MODE4(15, 65)
2959	COLUMN_RENDER_BLOCK_MODE4(16, 69)
2960	COLUMN_RENDER_BLOCK_MODE4(17, 73)
2961	COLUMN_RENDER_BLOCK_MODE4(18, 77)
2962	COLUMN_RENDER_BLOCK_MODE4(19, 81)
2963	COLUMN_RENDER_BLOCK_MODE4(20, 85)
2964	COLUMN_RENDER_BLOCK_MODE4(21, 89)
2965	COLUMN_RENDER_BLOCK_MODE4(22, 93)
2966	COLUMN_RENDER_BLOCK_MODE4(23, 97)
2967	COLUMN_RENDER_BLOCK_MODE4(24, 101)
2968	COLUMN_RENDER_BLOCK_MODE4(25, 105)
2969	COLUMN_RENDER_BLOCK_MODE4(26, 109)
2970	COLUMN_RENDER_BLOCK_MODE4(27, 113)
2971	COLUMN_RENDER_BLOCK_MODE4(28, 117)
2972	COLUMN_RENDER_BLOCK_MODE4(29, 121)
2973	COLUMN_RENDER_BLOCK_MODE4(30, 125)
2974	COLUMN_RENDER_BLOCK_MODE4(31, 129)
2975	case 133:
2976		external_slot(context);
2977		CHECK_LIMIT
2978	case 134:
2979		external_slot(context);
2980		CHECK_LIMIT
2981	case 135:
2982		external_slot(context);
2983		CHECK_LIMIT
2984	case 136: {
2985		external_slot(context);
2986		//set things up for sprite rendering in the next slot
2987		memset(context->linebuf, 0, LINEBUF_SIZE);
2988		context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1;
2989		context->sprite_draws = MAX_DRAWS_H32_MODE4;
2990		uint32_t *dst = context->output + BORDER_LEFT + 256;
2991		uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
2992		for (int i = 0; i < 8; i++, dst++)
2993		{
2994			*dst = bgcolor;
2995		}
2996		context->done_output = dst;
2997		CHECK_LIMIT
2998	}}
2999	default:
3000		context->hslot++;
3001		context->cycles += MCLKS_SLOT_H32;
3002	}
3003}
3004
3005static void inactive_test_output(vdp_context *context, uint8_t is_h40, uint8_t test_layer)
3006{
3007	uint8_t max_slot = is_h40 ? 169 : 136;
3008	if (context->hslot > max_slot) {
3009		return;
3010	}
3011	uint32_t *dst = context->output + (context->hslot >> 3) * SCROLL_BUFFER_DRAW;
3012	int32_t len;
3013	uint32_t src_off;
3014	if (context->hslot) {
3015		dst -= SCROLL_BUFFER_DRAW - BORDER_LEFT;
3016		src_off = 0;
3017		len = context->hslot == max_slot ? BORDER_RIGHT : SCROLL_BUFFER_DRAW;
3018	} else {
3019		src_off = SCROLL_BUFFER_DRAW - BORDER_LEFT;
3020		len = BORDER_LEFT;
3021	}
3022	uint8_t *src;
3023	if (test_layer == 2) {
3024		//plane A
3025		src_off += context->buf_a_off + context->hscroll_a;
3026		src = context->tmp_buf_a;
3027	} else if (test_layer == 3){
3028		//plane B
3029		src_off += context->buf_b_off + context->hscroll_b;
3030		src = context->tmp_buf_b;
3031	} else {
3032		//sprite layer
3033		for (; len >=0; len--, dst++, src_off++)
3034		{
3035			*dst = context->colors[0];
3036		}
3037	}
3038	for (; len >=0; len--, dst++, src_off++)
3039	{
3040		*dst = context->colors[src[src_off & SCROLL_BUFFER_MASK] & 0x3F];
3041	}
3042	context->done_output = dst;
3043	context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_DRAW;
3044	context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_DRAW;
3045}
3046
3047static void check_switch_inactive(vdp_context *context, uint8_t is_h40)
3048{
3049	//technically the second hcounter check should be different for H40, but this is probably close enough for now
3050	if (context->state == ACTIVE && context->vcounter == context->inactive_start && (context->hslot >= (is_h40 ? 167 : 135) || context->hslot < 133)) {
3051		context->state = INACTIVE;
3052	}
3053}
3054
3055static void vdp_inactive(vdp_context *context, uint32_t target_cycles, uint8_t is_h40, uint8_t mode_5)
3056{
3057	uint8_t buf_clear_slot, index_reset_slot, bg_end_slot, vint_slot, line_change, jump_start, jump_dest, latch_slot;
3058	uint8_t index_reset_value, max_draws, max_sprites;
3059	uint16_t vint_line, active_line;
3060	uint32_t bg_color;
3061	
3062	if (mode_5) {
3063		if (is_h40) {
3064			latch_slot = 165;
3065			buf_clear_slot = 163;
3066			index_reset_slot = 167;
3067			bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2;
3068			max_draws = MAX_DRAWS-1;
3069			max_sprites = MAX_SPRITES_LINE;
3070			index_reset_value = 0x80;
3071			vint_slot = VINT_SLOT_H40;
3072			line_change = LINE_CHANGE_H40;
3073			jump_start = 182;
3074			jump_dest = 229;
3075		} else {
3076			bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2;
3077			max_draws = MAX_DRAWS_H32-1;
3078			max_sprites = MAX_SPRITES_LINE_H32;
3079			buf_clear_slot = 128;
3080			index_reset_slot = 132;
3081			index_reset_value = 0x80;
3082			vint_slot = VINT_SLOT_H32;
3083			line_change = LINE_CHANGE_H32;
3084			jump_start = 147;
3085			jump_dest = 233;
3086			latch_slot = 243;
3087		}
3088		vint_line = context->inactive_start;
3089		active_line = 0x1FF;
3090		if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
3091			latch_slot = 220;
3092		}
3093	} else {
3094		latch_slot = 220;
3095		bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2;
3096		max_draws = MAX_DRAWS_H32_MODE4;
3097		max_sprites = 8;
3098		buf_clear_slot = 136;
3099		index_reset_slot = 253;
3100		index_reset_value = 0;
3101		vint_line = context->inactive_start + 1;
3102		vint_slot = VINT_SLOT_MODE4;
3103		line_change = LINE_CHANGE_MODE4;
3104		bg_color = render_map_color(0, 0, 0);
3105		jump_start = 147;
3106		jump_dest = 233;
3107		if (context->regs[REG_MODE_1] & BIT_MODE_4) {
3108			active_line = 0x1FF;
3109		} else {
3110			//never active unless either mode 4 or mode 5 is turned on
3111			active_line = 0x200;
3112		}
3113	}
3114	uint32_t *dst;
3115	uint8_t *debug_dst;
3116	if (
3117		(
3118			context->vcounter < context->inactive_start + context->border_bot 
3119			|| context->vcounter >= 0x200 - context->border_top
3120		) && context->hslot >= BG_START_SLOT && context->hslot < bg_end_slot
3121	) {
3122		dst = context->output + 2 * (context->hslot - BG_START_SLOT);
3123		debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
3124	} else {
3125		dst = NULL;
3126	}
3127		
3128	if (
3129		!dst && context->vcounter == context->inactive_start + context->border_bot
3130		&& context->hslot >= line_change  && context->hslot < bg_end_slot
3131	) {
3132		dst = context->output + 2 * (context->hslot - BG_START_SLOT);
3133		debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
3134	}
3135		
3136	uint8_t test_layer = context->test_port >> 7 & 3;
3137	if (test_layer) {
3138		dst = NULL;
3139	}
3140	
3141	while(context->cycles < target_cycles)
3142	{
3143		check_switch_inactive(context, is_h40);
3144		if (context->hslot == BG_START_SLOT && !test_layer && (
3145			context->vcounter < context->inactive_start + context->border_bot 
3146			|| context->vcounter >= 0x200 - context->border_top
3147		)) {
3148			dst = context->output + (context->hslot - BG_START_SLOT) * 2;
3149			debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
3150		} else if (context->hslot == bg_end_slot) {
3151			advance_output_line(context);
3152			dst = NULL;
3153		}
3154		//this will need some tweaking to properly interact with 128K mode, 
3155		//but this should be good enough for now
3156		context->serial_address += 1024;
3157		if (test_layer) {
3158			switch (context->hslot & 7)
3159			{
3160			case 3:
3161				render_border_garbage(context, context->serial_address, context->tmp_buf_a, context->buf_a_off, context->col_1);
3162				break;
3163			case 4:
3164				render_border_garbage(context, context->serial_address, context->tmp_buf_a, context->buf_a_off+8, context->col_2);
3165				break;
3166			case 7:
3167				render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off, context->col_1);
3168				break;
3169			case 0:
3170				render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off+8, context->col_2);
3171				inactive_test_output(context, is_h40, test_layer);
3172				break;
3173			}
3174		}
3175		
3176		if (context->hslot == buf_clear_slot) {
3177			if (mode_5) {
3178				context->cur_slot = max_draws;
3179			} else {
3180				context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1;
3181				context->sprite_draws = MAX_DRAWS_H32_MODE4;
3182			}
3183			memset(context->linebuf, 0, LINEBUF_SIZE);
3184		} else if (context->hslot == index_reset_slot) {
3185			context->sprite_index = index_reset_value;
3186			context->slot_counter = mode_5 ? 0 : max_sprites;
3187		} else if (context->hslot == latch_slot) {
3188			//it seems unlikely to me that vscroll actually gets latched when the display is off
3189			//but it's the only straightforward way to reconcile what I'm seeing between Skitchin 
3190			//(which seems to expect vscroll to be latched early) and the intro of Gunstar Heroes
3191			//(which disables the display and ends up with garbage if vscroll is latched during that period)
3192			//without it. Some more tests are definitely needed
3193			context->vscroll_latch[0] = context->vsram[0];
3194			context->vscroll_latch[1] = context->vsram[1];
3195		} else if (context->vcounter == vint_line && context->hslot == vint_slot) {
3196			context->flags2 |= FLAG2_VINT_PENDING;
3197			context->pending_vint_start = context->cycles;
3198		} else if (context->vcounter == context->inactive_start && context->hslot == 1 && (context->regs[REG_MODE_4] & BIT_INTERLACE)) {
3199			context->flags2 ^= FLAG2_EVEN_FIELD;
3200		}
3201		
3202		if (dst) {
3203			if (mode_5) {
3204				bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
3205			} else if (context->regs[REG_MODE_1] & BIT_MODE_4) {
3206				bg_color = context->colors[MODE4_OFFSET + 0x10 + (context->regs[REG_BG_COLOR] & 0xF)];
3207			}
3208			if (dst >= context->done_output) {
3209				*(dst++) = bg_color;
3210				*(debug_dst++) = DBG_SRC_BG;
3211			} else {
3212				dst++;
3213				debug_dst++;
3214			}
3215			if (dst >= context->done_output) {
3216				*(dst++) = bg_color;
3217				*(debug_dst++) = DBG_SRC_BG;
3218				context->done_output = dst;
3219			} else {
3220				dst++;
3221				debug_dst++;
3222			}
3223			if (context->hslot == (bg_end_slot-1)) {
3224				*(dst++) = bg_color;
3225				*(debug_dst++) = DBG_SRC_BG;
3226				context->done_output = dst;
3227			}
3228		}
3229		
3230		if (!is_refresh(context, context->hslot)) {
3231			external_slot(context);
3232			if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) {
3233				run_dma_src(context, context->hslot);
3234			}
3235		}
3236		
3237		if (is_h40) {
3238			if (context->hslot >= HSYNC_SLOT_H40 && context->hslot < HSYNC_END_H40) {
3239				context->cycles += h40_hsync_cycles[context->hslot - HSYNC_SLOT_H40];
3240			} else {
3241				context->cycles += MCLKS_SLOT_H40;
3242			}
3243		} else {
3244			context->cycles += MCLKS_SLOT_H32;
3245		}
3246		if (context->hslot == jump_start) {
3247			context->hslot = jump_dest;
3248		} else {
3249			context->hslot++;
3250		}
3251		if (context->hslot == line_change) {
3252			vdp_advance_line(context);
3253			if (context->vcounter == active_line) {
3254				context->state = PREPARING;
3255				return;
3256			}
3257		}
3258	}
3259}
3260
3261void vdp_run_context_full(vdp_context * context, uint32_t target_cycles)
3262{
3263	uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40;
3264	uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
3265	while(context->cycles < target_cycles)
3266	{
3267		check_switch_inactive(context, is_h40);
3268		
3269		if (is_active(context)) {
3270			if (mode_5) {
3271				if (is_h40) {
3272					vdp_h40(context, target_cycles);
3273				} else {
3274					vdp_h32(context, target_cycles);
3275				}
3276			} else {
3277				vdp_h32_mode4(context, target_cycles);
3278			}
3279		} else {
3280			vdp_inactive(context, target_cycles, is_h40, mode_5);
3281		}
3282	}
3283}
3284
3285void vdp_run_context(vdp_context *context, uint32_t target_cycles)
3286{
3287	//TODO: Deal with H40 hsync shenanigans
3288	uint32_t slot_cyc = context->regs[REG_MODE_4] & BIT_H40 ? 15 : 19;
3289	if (target_cycles < slot_cyc) {
3290		//avoid overflow
3291		return;
3292	}
3293	vdp_run_context_full(context, target_cycles - slot_cyc);
3294}
3295
3296uint32_t vdp_run_to_vblank(vdp_context * context)
3297{
3298	uint32_t old_frame = context->frame;
3299	while (context->frame == old_frame) {
3300		vdp_run_context_full(context, context->cycles + MCLKS_LINE);
3301	}
3302	return context->cycles;
3303}
3304
3305void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles)
3306{
3307	for(;;) {
3308		uint32_t dmalen = (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L];
3309		if (!dmalen) {
3310			dmalen = 0x10000;
3311		}
3312		uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20);
3313		if (
3314			(context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_COPY 
3315			|| (((context->cd & 0xF) == VRAM_WRITE) && !(context->regs[REG_MODE_2] & BIT_128K_VRAM))) {
3316			//DMA copies take twice as long to complete since they require a read and a write
3317			//DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word
3318			//unless 128KB mode is enabled
3319			min_dma_complete *= 2;
3320		}
3321		min_dma_complete += context->cycles;
3322		if (target_cycles < min_dma_complete) {
3323			vdp_run_context_full(context, target_cycles);
3324			return;
3325		} else {
3326			vdp_run_context_full(context, min_dma_complete);
3327			if (!(context->flags & FLAG_DMA_RUN)) {
3328				return;
3329			}
3330		}
3331	}
3332}
3333
3334static uint16_t get_ext_vcounter(vdp_context *context)
3335{
3336	uint16_t line= context->vcounter;
3337	if (context->regs[REG_MODE_4] & BIT_INTERLACE) {
3338		if (context->double_res) {
3339			line <<= 1;
3340		} else {
3341			line &= 0x1FE;
3342		}
3343		if (line & 0x100) {
3344			line |= 1;
3345		}
3346	}
3347	return line << 8;
3348}
3349
3350void vdp_latch_hv(vdp_context *context)
3351{
3352	context->hv_latch = context->hslot | get_ext_vcounter(context);
3353}
3354
3355uint16_t vdp_hv_counter_read(vdp_context * context)
3356{
3357	if ((context->regs[REG_MODE_2] & BIT_MODE_5) && (context->regs[REG_MODE_1] & BIT_HVC_LATCH)) {
3358		return context->hv_latch;
3359	}
3360	uint16_t hv;
3361	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3362		hv = context->hslot;
3363	} else {
3364		hv = context->hv_latch & 0xFF;
3365	}
3366	hv |= get_ext_vcounter(context);
3367	
3368	return hv;
3369}
3370
3371int vdp_control_port_write(vdp_context * context, uint16_t value)
3372{
3373	//printf("control port write: %X at %d\n", value, context->cycles);
3374	if (context->flags & FLAG_DMA_RUN) {
3375		return -1;
3376	}
3377	if (context->flags & FLAG_PENDING) {
3378		context->address = (context->address & 0x3FFF) | (value << 14 & 0x1C000);
3379		//It seems like the DMA enable bit doesn't so much enable DMA so much 
3380		//as it enables changing CD5 from control port writes
3381		uint8_t preserve = (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) ? 0x3 : 0x23;
3382		context->cd = (context->cd & preserve) | ((value >> 2) & ~preserve & 0xFF);
3383		context->flags &= ~FLAG_PENDING;
3384		//Should these be taken care of here or after the first write?
3385		context->flags &= ~FLAG_READ_FETCHED;
3386		context->flags2 &= ~FLAG2_READ_PENDING;
3387		//printf("New Address: %X, New CD: %X\n", context->address, context->cd);
3388		if (context->cd & 0x20) {
3389			//
3390			if((context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) {
3391				//DMA copy or 68K -> VDP, transfer starts immediately
3392				//printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot);
3393				if (!(context->regs[REG_DMASRC_H] & 0x80)) {
3394					//printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]);
3395					//68K -> VDP DMA takes a few slots to actually start reading even though it acquires the bus immediately
3396					//logic analyzer captures made it seem like the proper value is 4 slots, but that seems to cause trouble with the Nemesis' FIFO Wait State test
3397					//only captures are from a direct color DMA demo which will generally start DMA at a very specific point in display so other values are plausible
3398					//sticking with 3 slots for now until I can do some more captures
3399					vdp_run_context_full(context, context->cycles + 12 * ((context->regs[REG_MODE_2] & BIT_MODE_5) && (context->regs[REG_MODE_4] & BIT_H40) ? 4 : 5));
3400					context->flags |= FLAG_DMA_RUN;
3401					return 1;
3402				} else {
3403					context->flags |= FLAG_DMA_RUN;
3404					//printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
3405				}
3406			} else {
3407				//printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd);
3408			}
3409		}
3410	} else {
3411		uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
3412		context->address = (context->address &0xC000) | (value & 0x3FFF);
3413		context->cd = (context->cd & 0x3C) | (value >> 14);
3414		if ((value & 0xC000) == 0x8000) {
3415			//Register write
3416			uint8_t reg = (value >> 8) & 0x1F;
3417			if (reg < (mode_5 ? VDP_REGS : 0xB)) {
3418				//printf("register %d set to %X\n", reg, value & 0xFF);
3419				if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
3420					vdp_latch_hv(context);
3421				}
3422				if (reg == REG_BG_COLOR) {
3423					value &= 0x3F;
3424				}
3425				/*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) {
3426					printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame);
3427				}*/
3428				context->regs[reg] = value;
3429				if (reg == REG_MODE_4) {
3430					context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
3431					if (!context->double_res) {
3432						context->flags2 &= ~FLAG2_EVEN_FIELD;
3433					}
3434				}
3435				if (reg == REG_MODE_1 || reg == REG_MODE_2 || reg == REG_MODE_4) {
3436					update_video_params(context);
3437				}
3438			}
3439		} else if (mode_5) {
3440			context->flags |= FLAG_PENDING;
3441			//Should these be taken care of here or after the second write?
3442			//context->flags &= ~FLAG_READ_FETCHED;
3443			//context->flags2 &= ~FLAG2_READ_PENDING;
3444		} else {
3445			context->flags &= ~FLAG_READ_FETCHED;
3446			context->flags2 &= ~FLAG2_READ_PENDING;
3447		}
3448	}
3449	return 0;
3450}
3451
3452void vdp_control_port_write_pbc(vdp_context *context, uint8_t value)
3453{
3454	if (context->flags2 & FLAG2_BYTE_PENDING) {
3455		uint16_t full_val = value << 8 | context->pending_byte;
3456		context->flags2 &= ~FLAG2_BYTE_PENDING;
3457		//TODO: Deal with fact that Vbus->VDP DMA doesn't do anything in PBC mode
3458		vdp_control_port_write(context, full_val);
3459		if (context->cd == VRAM_READ) {
3460			context->cd = VRAM_READ8;
3461		}
3462	} else {
3463		context->pending_byte = value;
3464		context->flags2 |= FLAG2_BYTE_PENDING;
3465	}
3466}
3467
3468int vdp_data_port_write(vdp_context * context, uint16_t value)
3469{
3470	//printf("data port write: %X at %d\n", value, context->cycles);
3471	if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) {
3472		return -1;
3473	}
3474	if (context->flags & FLAG_PENDING) {
3475		context->flags &= ~FLAG_PENDING;
3476		//Should these be cleared here?
3477		context->flags &= ~FLAG_READ_FETCHED;
3478		context->flags2 &= ~FLAG2_READ_PENDING;
3479	}
3480	/*if (context->fifo_cur == context->fifo_end) {
3481		printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
3482	}*/
3483	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
3484		context->flags &= ~FLAG_DMA_RUN;
3485	}
3486	while (context->fifo_write == context->fifo_read) {
3487		vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
3488	}
3489	fifo_entry * cur = context->fifo + context->fifo_write;
3490	cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
3491	cur->address = context->address;
3492	cur->value = value;
3493	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3494		cur->cd = context->cd;
3495	} else {
3496		cur->cd = (context->cd & 2) | 1;
3497	}
3498	cur->partial = 0;
3499	if (context->fifo_read < 0) {
3500		context->fifo_read = context->fifo_write;
3501	}
3502	context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
3503	increment_address(context);
3504	return 0;
3505}
3506
3507void vdp_data_port_write_pbc(vdp_context * context, uint8_t value)
3508{
3509	if (context->flags & FLAG_PENDING) {
3510		context->flags &= ~FLAG_PENDING;
3511		//Should these be cleared here?
3512		context->flags &= ~FLAG_READ_FETCHED;
3513		context->flags2 &= ~FLAG2_READ_PENDING;
3514	}
3515	context->flags2 &= ~FLAG2_BYTE_PENDING;
3516	/*if (context->fifo_cur == context->fifo_end) {
3517		printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
3518	}*/
3519	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
3520		context->flags &= ~FLAG_DMA_RUN;
3521	}
3522	while (context->fifo_write == context->fifo_read) {
3523		vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
3524	}
3525	fifo_entry * cur = context->fifo + context->fifo_write;
3526	cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
3527	cur->address = context->address;
3528	cur->value = value;
3529	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3530		cur->cd = context->cd;
3531	} else {
3532		cur->cd = (context->cd & 2) | 1;
3533	}
3534	cur->partial = 3;
3535	if (context->fifo_read < 0) {
3536		context->fifo_read = context->fifo_write;
3537	}
3538	context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
3539	increment_address(context);
3540}
3541
3542void vdp_test_port_write(vdp_context * context, uint16_t value)
3543{
3544	context->test_port = value;
3545}
3546
3547uint16_t vdp_control_port_read(vdp_context * context)
3548{
3549	context->flags &= ~FLAG_PENDING;
3550	context->flags2 &= ~FLAG2_BYTE_PENDING;
3551	//Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch
3552	uint16_t value = context->system->get_open_bus_value(context->system) & 0xFC00;
3553	if (context->fifo_read < 0) {
3554		value |= 0x200;
3555	}
3556	if (context->fifo_read == context->fifo_write) {
3557		value |= 0x100;
3558	}
3559	if (context->flags2 & FLAG2_VINT_PENDING) {
3560		value |= 0x80;
3561	}
3562	if (context->flags & FLAG_DOT_OFLOW) {
3563		value |= 0x40;
3564		context->flags &= ~FLAG_DOT_OFLOW;
3565	}
3566	if (context->flags2 & FLAG2_SPRITE_COLLIDE) {
3567		value |= 0x20;
3568		context->flags2 &= ~FLAG2_SPRITE_COLLIDE;
3569	}
3570	if ((context->regs[REG_MODE_4] & BIT_INTERLACE) && !(context->flags2 & FLAG2_EVEN_FIELD)) {
3571		value |= 0x10;
3572	}
3573	uint32_t slot = context->hslot;
3574	if (!is_active(context)) {
3575		value |= 0x8;
3576	}
3577	if (context->regs[REG_MODE_4] & BIT_H40) {
3578		if (slot < HBLANK_END_H40 || slot > HBLANK_START_H40) {
3579			value |= 0x4;
3580		}
3581	} else {
3582		if (slot < HBLANK_END_H32 || slot > HBLANK_START_H32) {
3583			value |= 0x4;
3584		}
3585	}
3586	if (context->cd & 0x20) {
3587		value |= 0x2;
3588	}
3589	if (context->flags2 & FLAG2_REGION_PAL) {
3590		value |= 0x1;
3591	}
3592	//printf("status read at cycle %d returned %X\n", context->cycles, value);
3593	return value;
3594}
3595
3596uint16_t vdp_data_port_read(vdp_context * context)
3597{
3598	if (context->flags & FLAG_PENDING) {
3599		context->flags &= ~FLAG_PENDING;
3600		//Should these be cleared here?
3601		context->flags &= ~FLAG_READ_FETCHED;
3602		context->flags2 &= ~FLAG2_READ_PENDING;
3603	}
3604	if (context->cd & 1) {
3605		warning("Read from VDP data port while writes are configured, CPU is now frozen. VDP Address: %X, CD: %X\n", context->address, context->cd);
3606	}
3607	while (!(context->flags & FLAG_READ_FETCHED)) {
3608		vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
3609	}
3610	context->flags &= ~FLAG_READ_FETCHED;
3611	return context->prefetch;
3612}
3613
3614uint8_t vdp_data_port_read_pbc(vdp_context * context)
3615{
3616	context->flags &= ~(FLAG_PENDING | FLAG_READ_FETCHED);
3617	context->flags2 &= ~FLAG2_BYTE_PENDING;
3618		
3619	context->cd = VRAM_READ8;
3620	return context->prefetch;
3621}
3622
3623uint16_t vdp_test_port_read(vdp_context * context)
3624{
3625	//TODO: Find out what actually gets returned here
3626	return context->test_port;
3627}
3628
3629void vdp_adjust_cycles(vdp_context * context, uint32_t deduction)
3630{
3631	context->cycles -= deduction;
3632	if (context->pending_vint_start >= deduction) {
3633		context->pending_vint_start -= deduction;
3634	} else {
3635		context->pending_vint_start = 0;
3636	}
3637	if (context->pending_hint_start >= deduction) {
3638		context->pending_hint_start -= deduction;
3639	} else {
3640		context->pending_hint_start = 0;
3641	}
3642	if (context->fifo_read >= 0) {
3643		int32_t idx = context->fifo_read;
3644		do {
3645			if (context->fifo[idx].cycle >= deduction) {
3646				context->fifo[idx].cycle -= deduction;
3647			} else {
3648				context->fifo[idx].cycle = 0;
3649			}
3650			idx = (idx+1) & (FIFO_SIZE-1);
3651		} while(idx != context->fifo_write);
3652	}
3653}
3654
3655static uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context)
3656{
3657	if (context->hslot < 183) {
3658		return MCLKS_LINE - context->hslot * MCLKS_SLOT_H40;
3659	} else if (context->hslot < HSYNC_END_H40) {
3660		uint32_t before_hsync = context->hslot < HSYNC_SLOT_H40 ? (HSYNC_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40 : 0;
3661		uint32_t hsync = 0;
3662		for (int i = context->hslot <= HSYNC_SLOT_H40 ? 0 : context->hslot - HSYNC_SLOT_H40; i < sizeof(h40_hsync_cycles)/sizeof(uint32_t); i++)
3663		{
3664			hsync += h40_hsync_cycles[i];
3665		}
3666		uint32_t after_hsync = (256- HSYNC_END_H40) * MCLKS_SLOT_H40;
3667		return before_hsync + hsync + after_hsync;
3668	} else {
3669		return (256-context->hslot) * MCLKS_SLOT_H40;
3670	}
3671}
3672
3673static uint32_t vdp_cycles_next_line(vdp_context * context)
3674{
3675	if (context->regs[REG_MODE_4] & BIT_H40) {
3676		//TODO: Handle "illegal" Mode 4/H40 combo
3677		if (context->hslot < LINE_CHANGE_H40) {
3678			return (LINE_CHANGE_H40 - context->hslot) * MCLKS_SLOT_H40;
3679		} else {
3680			return vdp_cycles_hslot_wrap_h40(context) + LINE_CHANGE_H40 * MCLKS_SLOT_H40;
3681		}
3682	} else {
3683		if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3684			if (context->hslot < LINE_CHANGE_H32) {
3685				return (LINE_CHANGE_H32 - context->hslot) * MCLKS_SLOT_H32;
3686			} else if (context->hslot < 148) {
3687				return MCLKS_LINE - (context->hslot - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
3688			} else {
3689				return (256-context->hslot + LINE_CHANGE_H32) * MCLKS_SLOT_H32;
3690			}
3691		} else {
3692			if (context->hslot < 148) {
3693				return (148 - context->hslot + LINE_CHANGE_MODE4 - 233) * MCLKS_SLOT_H32;
3694			} else if (context->hslot < LINE_CHANGE_MODE4) {
3695				return (LINE_CHANGE_MODE4 - context->hslot) * MCLKS_SLOT_H32;
3696			} else {
3697				return MCLKS_LINE - (context->hslot - LINE_CHANGE_MODE4) * MCLKS_SLOT_H32;
3698			}
3699		}
3700	}
3701}
3702
3703static void get_jump_params(vdp_context *context, uint32_t *jump_start, uint32_t *jump_dst)
3704{
3705	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3706		if (context->flags2 & FLAG2_REGION_PAL) {
3707			if (context->regs[REG_MODE_2] & BIT_PAL) {
3708				*jump_start = 0x10B;
3709				*jump_dst = 0x1D2;
3710			} else {
3711				*jump_start = 0x103;
3712				*jump_dst = 0x1CA;
3713			}
3714		} else {
3715			if (context->regs[REG_MODE_2] & BIT_PAL) {
3716				*jump_start = 0x100;
3717				*jump_dst = 0x1FA;
3718			} else {
3719				*jump_start = 0xEB;
3720				*jump_dst = 0x1E5;
3721			}
3722		}
3723	} else {
3724		*jump_start = 0xDB;
3725		*jump_dst = 0x1D5;
3726	}
3727}
3728
3729static uint32_t vdp_cycles_to_line(vdp_context * context, uint32_t target)
3730{
3731	uint32_t jump_start, jump_dst;
3732	get_jump_params(context, &jump_start, &jump_dst);
3733	uint32_t lines;
3734	if (context->vcounter < target) {
3735		if (target < jump_start || context->vcounter > jump_start) {
3736			lines = target - context->vcounter;
3737		} else {
3738			lines = jump_start - context->vcounter + target - jump_dst;
3739		}
3740	} else {
3741		if (context->vcounter < jump_start) {
3742			lines = jump_start - context->vcounter + 512 - jump_dst;
3743		} else {
3744			lines = 512 - context->vcounter;
3745		}
3746		if (target < jump_start) {
3747			lines += target;
3748		} else {
3749			lines += jump_start + target - jump_dst;
3750		}
3751	}
3752	return MCLKS_LINE * (lines - 1) + vdp_cycles_next_line(context);
3753}
3754
3755uint32_t vdp_cycles_to_frame_end(vdp_context * context)
3756{
3757	return context->cycles + vdp_cycles_to_line(context, context->inactive_start);
3758}
3759
3760uint32_t vdp_next_hint(vdp_context * context)
3761{
3762	if (!(context->regs[REG_MODE_1] & BIT_HINT_EN)) {
3763		return 0xFFFFFFFF;
3764	}
3765	if (context->flags2 & FLAG2_HINT_PENDING) {
3766		return context->pending_hint_start;
3767	}
3768	uint32_t hint_line;
3769	if (context->state != ACTIVE) {
3770		hint_line = context->regs[REG_HINT];
3771		if (hint_line > context->inactive_start) {
3772			return 0xFFFFFFFF;
3773		}
3774	} else {
3775		hint_line = context->vcounter + context->hint_counter + 1;
3776		if (context->vcounter < context->inactive_start) {
3777			if (hint_line > context->inactive_start) {
3778				hint_line = context->regs[REG_HINT];
3779				if (hint_line > context->inactive_start) {
3780					return 0xFFFFFFFF;
3781				}
3782				if (hint_line >= context->vcounter) {
3783					//Next interrupt is for a line in the next frame that
3784					//is higher than the line we're on now so just passing
3785					//that line number to vdp_cycles_to_line will yield the wrong
3786					//result
3787					return context->cycles + vdp_cycles_to_line(context,  0) + hint_line * MCLKS_LINE;
3788				}
3789			}
3790		} else {
3791			uint32_t jump_start, jump_dst;
3792			get_jump_params(context, &jump_start, &jump_dst);
3793			if (hint_line >= jump_start && context->vcounter < jump_dst) {
3794				hint_line = (hint_line + jump_dst - jump_start) & 0x1FF;
3795			}
3796			if (hint_line < context->vcounter && hint_line > context->inactive_start) {
3797				return 0xFFFFFFFF;
3798			}
3799		}
3800	}
3801	return context->cycles + vdp_cycles_to_line(context, hint_line);
3802}
3803
3804static uint32_t vdp_next_vint_real(vdp_context * context)
3805{
3806	if (!(context->regs[REG_MODE_2] & BIT_VINT_EN)) {
3807		return 0xFFFFFFFF;
3808	}
3809	if (context->flags2 & FLAG2_VINT_PENDING) {
3810		return context->pending_vint_start;
3811	}
3812
3813
3814	return vdp_next_vint_z80(context);
3815}
3816
3817uint32_t vdp_next_vint(vdp_context *context)
3818{
3819	uint32_t ret = vdp_next_vint_real(context);
3820#ifdef TIMING_DEBUG
3821	static uint32_t last = 0xFFFFFFFF;
3822	if (last != ret) {
3823		printf("vdp_next_vint is %d at frame %d, line %d, hslot %d\n", ret, context->frame, context->vcounter, context->hslot);
3824	}
3825	last = ret;
3826#endif
3827	return ret;
3828}
3829
3830uint32_t vdp_next_vint_z80(vdp_context * context)
3831{
3832	uint16_t vint_line = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->inactive_start : context->inactive_start + 1;
3833	if (context->vcounter == vint_line) {
3834		if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3835			if (context->regs[REG_MODE_4] & BIT_H40) {
3836				if (context->hslot >= LINE_CHANGE_H40 || context->hslot <= VINT_SLOT_H40) {
3837					uint32_t cycles = context->cycles;
3838					if (context->hslot >= LINE_CHANGE_H40) {
3839						if (context->hslot < 183) {
3840							cycles += (183 - context->hslot) * MCLKS_SLOT_H40;
3841						}
3842						
3843						if (context->hslot < HSYNC_SLOT_H40) {
3844							cycles += (HSYNC_SLOT_H40 - (context->hslot >= 229 ? context->hslot : 229)) * MCLKS_SLOT_H40;
3845						}
3846						for (int slot = context->hslot <= HSYNC_SLOT_H40 ? HSYNC_SLOT_H40 : context->hslot; slot < HSYNC_END_H40; slot++ )
3847						{
3848							cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];
3849						}
3850						cycles += (256 - (context->hslot > HSYNC_END_H40 ? context->hslot : HSYNC_END_H40)) * MCLKS_SLOT_H40;
3851					}
3852					
3853					cycles += (VINT_SLOT_H40 - (context->hslot >= LINE_CHANGE_H40 ? 0 : context->hslot)) * MCLKS_SLOT_H40;
3854					return cycles;
3855				}
3856			} else {
3857				if (context->hslot >= LINE_CHANGE_H32 || context->hslot <= VINT_SLOT_H32) {
3858					if (context->hslot <= VINT_SLOT_H32) {
3859						return context->cycles + (VINT_SLOT_H32 - context->hslot) * MCLKS_SLOT_H32;
3860					} else if (context->hslot < 233) {
3861						return context->cycles + (VINT_SLOT_H32 + 256 - 233 + 148 - context->hslot) * MCLKS_SLOT_H32;
3862					} else {
3863						return context->cycles + (VINT_SLOT_H32 + 256 - context->hslot) * MCLKS_SLOT_H32;
3864					}
3865				}
3866			}
3867		} else {
3868			if (context->hslot >= LINE_CHANGE_MODE4) {
3869				return context->cycles + (VINT_SLOT_MODE4 + 256 - context->hslot) * MCLKS_SLOT_H32;
3870			}
3871			if (context->hslot <= VINT_SLOT_MODE4) {
3872				return context->cycles + (VINT_SLOT_MODE4 - context->hslot) * MCLKS_SLOT_H32;
3873			}
3874		}
3875	}
3876	int32_t cycles_to_vint = vdp_cycles_to_line(context, vint_line);
3877	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3878		if (context->regs[REG_MODE_4] & BIT_H40) {
3879			cycles_to_vint += MCLKS_LINE - (LINE_CHANGE_H40 - VINT_SLOT_H40) * MCLKS_SLOT_H40;
3880		} else {
3881			cycles_to_vint += (VINT_SLOT_H32 + 256 - 233 + 148 - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
3882		}
3883	} else {
3884		cycles_to_vint += (256 - LINE_CHANGE_MODE4 + VINT_SLOT_MODE4) * MCLKS_SLOT_H32;
3885	}
3886	return context->cycles + cycles_to_vint;
3887}
3888
3889uint32_t vdp_next_nmi(vdp_context *context)
3890{
3891	if (!(context->flags2 & FLAG2_PAUSE)) {
3892		return 0xFFFFFFFF;
3893	}
3894	return context->cycles + vdp_cycles_to_line(context, 0x1FF);
3895}
3896
3897void vdp_pbc_pause(vdp_context *context)
3898{
3899	context->flags2 |= FLAG2_PAUSE;
3900}
3901
3902void vdp_int_ack(vdp_context * context)
3903{
3904	//CPU interrupt acknowledge is only used in Mode 5
3905	if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3906		//Apparently the VDP interrupt controller is not very smart
3907		//Instead of paying attention to what interrupt is being acknowledged it just 
3908		//clears the pending flag for whatever interrupt it is currently asserted
3909		//which may be different from the interrupt it was asserting when the 68k
3910		//started the interrupt process. The window for this is narrow and depends
3911		//on the latency between the int enable register write and the interrupt being
3912		//asserted, but Fatal Rewind depends on this due to some buggy code
3913		if ((context->flags2 & FLAG2_VINT_PENDING) && (context->regs[REG_MODE_2] & BIT_VINT_EN)) {
3914			context->flags2 &= ~FLAG2_VINT_PENDING;
3915		} else if((context->flags2 & FLAG2_HINT_PENDING) && (context->regs[REG_MODE_1] & BIT_HINT_EN)) {
3916			context->flags2 &= ~FLAG2_HINT_PENDING;
3917		}
3918	}
3919}
3920
3921void vdp_serialize(vdp_context *context, serialize_buffer *buf)
3922{
3923	save_int8(buf, VRAM_SIZE / 1024);//VRAM size in KB, needed for future proofing
3924	save_buffer8(buf, context->vdpmem, VRAM_SIZE);
3925	save_buffer16(buf, context->cram, CRAM_SIZE);
3926	save_buffer16(buf, context->vsram, VSRAM_SIZE);
3927	save_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE);
3928	for (int i = 0; i <= REG_DMASRC_H; i++)
3929	{
3930		save_int8(buf, context->regs[i]);
3931	}
3932	save_int32(buf, context->address);
3933	save_int32(buf, context->serial_address);
3934	save_int8(buf, context->cd);
3935	uint8_t fifo_size;
3936	if (context->fifo_read < 0) {
3937		fifo_size = 0;
3938	} else if (context->fifo_write > context->fifo_read) {
3939		fifo_size = context->fifo_write - context->fifo_read;
3940	} else {
3941		fifo_size = context->fifo_write + FIFO_SIZE - context->fifo_read;
3942	}
3943	save_int8(buf, fifo_size);
3944	for (int i = 0, cur = context->fifo_read; i < fifo_size; i++)
3945	{
3946		fifo_entry *entry = context->fifo + cur;
3947		cur = (cur + 1) & (FIFO_SIZE - 1);
3948		save_int32(buf, entry->cycle);
3949		save_int32(buf, entry->address);
3950		save_int16(buf, entry->value);
3951		save_int8(buf, entry->cd);
3952		save_int8(buf, entry->partial);
3953	}
3954	//FIXME: Flag bits should be rearranged for maximum correspondence to status reg
3955	save_int16(buf, context->flags2 << 8 | context->flags);
3956	save_int32(buf, context->frame);
3957	save_int16(buf, context->vcounter);
3958	save_int8(buf, context->hslot);
3959	save_int16(buf, context->hv_latch);
3960	save_int8(buf, context->state);
3961	save_int16(buf, context->hscroll_a);
3962	save_int16(buf, context->hscroll_b);
3963	save_int16(buf, context->vscroll_latch[0]);
3964	save_int16(buf, context->vscroll_latch[1]);
3965	save_int16(buf, context->col_1);
3966	save_int16(buf, context->col_2);
3967	save_int16(buf, context->test_port);
3968	save_buffer8(buf, context->tmp_buf_a, SCROLL_BUFFER_SIZE);
3969	save_buffer8(buf, context->tmp_buf_b, SCROLL_BUFFER_SIZE);
3970	save_int8(buf, context->buf_a_off);
3971	save_int8(buf, context->buf_b_off);
3972	//FIXME: Sprite rendering state is currently a mess
3973	save_int8(buf, context->sprite_index);
3974	save_int8(buf, context->sprite_draws);
3975	save_int8(buf, context->slot_counter);
3976	save_int8(buf, context->cur_slot);
3977	for (int i = 0; i < MAX_DRAWS; i++)
3978	{
3979		sprite_draw *draw = context->sprite_draw_list + i;
3980		save_int16(buf, draw->address);
3981		save_int16(buf, draw->x_pos);
3982		save_int8(buf, draw->pal_priority);
3983		save_int8(buf, draw->h_flip);
3984	}
3985	for (int i = 0; i < MAX_SPRITES_LINE; i++)
3986	{
3987		sprite_info *info = context->sprite_info_list + i;
3988		save_int8(buf, info->size);
3989		save_int8(buf, info->index);
3990		save_int16(buf, info->y);
3991	}
3992	save_buffer8(buf, context->linebuf, LINEBUF_SIZE);
3993	
3994	save_int32(buf, context->cycles);
3995	save_int32(buf, context->pending_vint_start);
3996	save_int32(buf, context->pending_hint_start);
3997}
3998
3999void vdp_deserialize(deserialize_buffer *buf, void *vcontext)
4000{
4001	vdp_context *context = vcontext;
4002	uint8_t vramk = load_int8(buf);
4003	load_buffer8(buf, context->vdpmem, (vramk * 1024) <= VRAM_SIZE ? vramk * 1024 : VRAM_SIZE);
4004	if ((vramk * 1024) > VRAM_SIZE) {
4005		buf->cur_pos += (vramk * 1024) - VRAM_SIZE;
4006	}
4007	load_buffer16(buf, context->cram, CRAM_SIZE);
4008	for (int i = 0; i < CRAM_SIZE; i++)
4009	{
4010		update_color_map(context, i, context->cram[i]);
4011	}
4012	load_buffer16(buf, context->vsram, VSRAM_SIZE);
4013	load_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE);
4014	for (int i = 0; i <= REG_DMASRC_H; i++)
4015	{
4016		context->regs[i] = load_int8(buf);
4017	}
4018	context->address = load_int32(buf);
4019	context->serial_address = load_int32(buf);
4020	context->cd = load_int8(buf);
4021	uint8_t fifo_size = load_int8(buf);
4022	if (fifo_size > FIFO_SIZE) {
4023		fatal_error("Invalid fifo size %d", fifo_size);
4024	}
4025	if (fifo_size) {
4026		context->fifo_read = 0;
4027		context->fifo_write = fifo_size & (FIFO_SIZE - 1);
4028		for (int i = 0; i < fifo_size; i++)
4029		{
4030			fifo_entry *entry = context->fifo + i;
4031			entry->cycle = load_int32(buf);
4032			entry->address = load_int32(buf);
4033			entry->value = load_int16(buf);
4034			entry->cd = load_int8(buf);
4035			entry->partial = load_int8(buf);
4036		}
4037	} else {
4038		context->fifo_read = -1;
4039		context->fifo_write = 0;
4040	}
4041	uint16_t flags = load_int16(buf);
4042	context->flags2 = flags >> 8;
4043	context->flags = flags;
4044	context->frame = load_int32(buf);
4045	context->vcounter = load_int16(buf);
4046	context->hslot = load_int8(buf);
4047	context->hv_latch = load_int16(buf);
4048	context->state = load_int8(buf);
4049	context->hscroll_a = load_int16(buf);
4050	context->hscroll_b = load_int16(buf);
4051	context->vscroll_latch[0] = load_int16(buf);
4052	context->vscroll_latch[1] = load_int16(buf);
4053	context->col_1 = load_int16(buf);
4054	context->col_2 = load_int16(buf);
4055	context->test_port = load_int16(buf);
4056	load_buffer8(buf, context->tmp_buf_a, SCROLL_BUFFER_SIZE);
4057	load_buffer8(buf, context->tmp_buf_b, SCROLL_BUFFER_SIZE);
4058	context->buf_a_off = load_int8(buf) & SCROLL_BUFFER_MASK;
4059	context->buf_b_off = load_int8(buf) & SCROLL_BUFFER_MASK;
4060	context->sprite_index = load_int8(buf);
4061	context->sprite_draws = load_int8(buf);
4062	context->slot_counter = load_int8(buf);
4063	context->cur_slot = load_int8(buf);
4064	for (int i = 0; i < MAX_DRAWS; i++)
4065	{
4066		sprite_draw *draw = context->sprite_draw_list + i;
4067		draw->address = load_int16(buf);
4068		draw->x_pos = load_int16(buf);
4069		draw->pal_priority = load_int8(buf);
4070		draw->h_flip = load_int8(buf);
4071	}
4072	for (int i = 0; i < MAX_SPRITES_LINE; i++)
4073	{
4074		sprite_info *info = context->sprite_info_list + i;
4075		info->size = load_int8(buf);
4076		info->index = load_int8(buf);
4077		info->y = load_int16(buf);
4078	}
4079	load_buffer8(buf, context->linebuf, LINEBUF_SIZE);
4080	
4081	context->cycles = load_int32(buf);
4082	context->pending_vint_start = load_int32(buf);
4083	context->pending_hint_start = load_int32(buf);
4084	update_video_params(context);
4085}
4086
4087static vdp_context *current_vdp;
4088static void vdp_debug_window_close(uint8_t which)
4089{
4090	//TODO: remove need for current_vdp global, and find the VDP via current_system instead
4091	for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++)
4092	{
4093		if (current_vdp->enabled_debuggers & (1 << i) && which == current_vdp->debug_fb_indices[i]) {
4094			vdp_toggle_debug_view(current_vdp, i);
4095			break;
4096		}
4097	}
4098}
4099
4100void vdp_toggle_debug_view(vdp_context *context, uint8_t debug_type)
4101{
4102	if (context->enabled_debuggers & 1 << debug_type) {
4103		render_destroy_window(context->debug_fb_indices[debug_type]);
4104		context->enabled_debuggers &= ~(1 << debug_type);
4105	} else {
4106		uint32_t width,height;
4107		uint8_t fetch_immediately = 0;
4108		char *caption;
4109		switch(debug_type)
4110		{
4111		case VDP_DEBUG_PLANE:
4112			caption = "BlastEm - VDP Plane Debugger";
4113			width = height = 1024;
4114			break;
4115		case VDP_DEBUG_VRAM:
4116			caption = "BlastEm - VDP VRAM Debugger";
4117			width = 1024;
4118			height = 512;
4119			break;
4120		case VDP_DEBUG_CRAM:
4121			caption = "BlastEm - VDP CRAM Debugger";
4122			width = 512;
4123			height = 512;
4124			fetch_immediately = 1;
4125			break;
4126		case VDP_DEBUG_COMPOSITE:
4127			caption = "BlastEm - VDP Plane Composition Debugger";
4128			width = LINEBUF_SIZE;
4129			height = context->inactive_start + context->border_top + context->border_bot;
4130			fetch_immediately = 1;
4131			break;
4132		default:
4133			return;
4134		}
4135		current_vdp = context;
4136		context->debug_fb_indices[debug_type] = render_create_window(caption, width, height, vdp_debug_window_close);
4137		if (context->debug_fb_indices[debug_type]) {
4138			context->enabled_debuggers |= 1 << debug_type;
4139		}
4140		if (fetch_immediately) {
4141			context->debug_fbs[debug_type] = render_get_video_buffer(context->debug_fb_indices[debug_type], &context->debug_fb_pitch[debug_type]);
4142		}
4143	}
4144}
4145
4146void vdp_inc_debug_mode(vdp_context *context)
4147{
4148	uint8_t active = render_get_active_video_buffer();
4149	if (active < VIDEO_BUFFER_USER_START) {
4150		return;
4151	}
4152	for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++)
4153	{
4154		if (context->enabled_debuggers & (1 << i) && context->debug_fb_indices[i] == active) {
4155			context->debug_modes[i]++;
4156			return;
4157		}
4158	}
4159}