changeset 505:b7b7a1cab44a

The local clone on my laptop got messed up and some changes had not been pushed. This commit represents the status of the working copy from that clone. It unfortunately contains some changes that I did not intend to commit yet, but this seems like the best option at the moment.
author Michael Pavone <pavone@retrodev.com>
date Mon, 06 Jan 2014 22:54:05 -0800
parents 7b0df1aaf384
children a3b48a57e847
files Makefile blastem.c blastem.h gdb_remote.c psg.c render_sdl.c shaders/default.v.glsl transz80.c vdp.c vgmplay.c ym2612.c z80_to_x86.c ztestgen.c
diffstat 13 files changed, 285 insertions(+), 160 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Oct 31 01:00:32 2013 -0700
+++ b/Makefile	Mon Jan 06 22:54:05 2014 -0800
@@ -42,13 +42,13 @@
 	$(CC) -o trans trans.o $(M68KOBJS) $(TRANSOBJS)
 
 transz80 : transz80.o $(Z80OBJS) $(TRANSOBJS)
-	$(CC) -o transz80 $(Z80OBJS) $(TRANSOBJS)
+	$(CC) -o transz80 transz80.o $(Z80OBJS) $(TRANSOBJS)
 
 ztestrun : ztestrun.o $(Z80OBJS) $(TRANSOBJS)
-	$(CC) -o ztestrun $(Z80OBJS) $(TRANSOBJS)
+	$(CC) -o ztestrun ztestrun.o $(Z80OBJS) $(TRANSOBJS)
 
 ztestgen : ztestgen.o z80inst.o
-	$(CC) -o ztestgen ztestgen.o z80inst.o
+	$(CC) -ggdb -o ztestgen ztestgen.o z80inst.o
 
 stateview : stateview.o vdp.o render_sdl.o $(CONFIGOBJS) gst.o
 	$(CC) -o stateview stateview.o vdp.o render_sdl.o $(CONFIGOBJS) gst.o $(LDFLAGS)
--- a/blastem.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/blastem.c	Mon Jan 06 22:54:05 2014 -0800
@@ -43,6 +43,7 @@
 uint8_t z80_ram[Z80_RAM_BYTES];
 
 int headless = 0;
+int exit_after = 0;
 int z80_enabled = 1;
 int frame_limit = 0;
 
@@ -186,20 +187,22 @@
 {
 	if (z80_enabled && !reset && !busreq) {
 		genesis_context * gen = z_context->system;
-		if (need_reset) {
-			z80_reset(z_context);
-			need_reset = 0;
-		}
 		z_context->sync_cycle = mclks / MCLKS_PER_Z80;
-		uint32_t vint_cycle = vdp_next_vint_z80(gen->vdp) / MCLKS_PER_Z80;
-		while (z_context->current_cycle < z_context->sync_cycle) {
-			if (z_context->iff1 && z_context->current_cycle < (vint_cycle + Z80_VINT_DURATION)) {
-				z_context->int_cycle = vint_cycle < z_context->int_enable_cycle ? z_context->int_enable_cycle : vint_cycle;
+		if (z_context->current_cycle < z_context->sync_cycle) {
+			if (need_reset) {
+				z80_reset(z_context);
+				need_reset = 0;
 			}
-			z_context->target_cycle = z_context->sync_cycle < z_context->int_cycle ? z_context->sync_cycle : z_context->int_cycle;
-			dprintf("Running Z80 from cycle %d to cycle %d. Native PC: %p\n", z_context->current_cycle, z_context->sync_cycle, z_context->native_pc);
-			z80_run(z_context);
-			dprintf("Z80 ran to cycle %d\n", z_context->current_cycle);
+			uint32_t vint_cycle = vdp_next_vint_z80(gen->vdp) / MCLKS_PER_Z80;
+			while (z_context->current_cycle < z_context->sync_cycle) {
+				if (z_context->iff1 && z_context->current_cycle < (vint_cycle + Z80_VINT_DURATION)) {
+					z_context->int_cycle = vint_cycle < z_context->int_enable_cycle ? z_context->int_enable_cycle : vint_cycle;
+				}
+				z_context->target_cycle = z_context->sync_cycle < z_context->int_cycle ? z_context->sync_cycle : z_context->int_cycle;
+				dprintf("Running Z80 from cycle %d to cycle %d. Native PC: %p\n", z_context->current_cycle, z_context->sync_cycle, z_context->native_pc);
+				z80_run(z_context);
+				dprintf("Z80 ran to cycle %d\n", z_context->current_cycle);
+			}
 		}
 	} else {
 		z_context->current_cycle = mclks / MCLKS_PER_Z80;
@@ -243,6 +246,11 @@
 
 		if (!headless) {
 			break_on_sync |= wait_render_frame(v_context, frame_limit);
+		} else if(exit_after){
+			--exit_after;
+			if (!exit_after) {
+				exit(0);
+			}
 		}
 		frame++;
 		mclks -= mclks_per_frame;
@@ -315,6 +323,11 @@
 						if (!headless) {
 							//printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, v_context->cycles);
 							wait_render_frame(v_context, frame_limit);
+						} else if(exit_after){
+							--exit_after;
+							if (!exit_after) {
+								exit(0);
+							}
 						}
 						vdp_adjust_cycles(v_context, mclks_per_frame);
 						genesis_context * gen = context->system;
@@ -342,6 +355,11 @@
 						if (v_context->cycles >= mclks_per_frame) {
 							if (!headless) {
 								wait_render_frame(v_context, frame_limit);
+							} else if(exit_after){
+								--exit_after;
+								if (!exit_after) {
+									exit(0);
+								}
 							}
 							vdp_adjust_cycles(v_context, mclks_per_frame);
 							genesis_context * gen = context->system;
@@ -570,7 +588,7 @@
 					if (reset) {
 						need_reset = 1;
 						//TODO: Add necessary delay between release of reset and start of execution
-						gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+						gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80 + 16;
 					}
 					reset = 0;
 				} else {
@@ -1779,6 +1797,15 @@
 	for (int i = 1; i < argc; i++) {
 		if (argv[i][0] == '-') {
 			switch(argv[i][1]) {
+			case 'b':
+				i++;
+				if (i >= argc) {
+					fputs("-b must be followed by a frame count\n", stderr);
+					return 1;
+				}
+				headless = 1;
+				exit_after = atoi(argv[i]);
+				break;
 			case 'd':
 				debug = 1;
 				break;
--- a/blastem.h	Thu Oct 31 01:00:32 2013 -0700
+++ b/blastem.h	Mon Jan 06 22:54:05 2014 -0800
@@ -37,6 +37,7 @@
 } genesis_context;
 
 extern genesis_context * genesis;
+extern int headless;
 extern int break_on_sync;
 extern int save_state;
 extern tern_node * config;
--- a/gdb_remote.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/gdb_remote.c	Mon Jan 06 22:54:05 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #include "blastem.h"
@@ -14,6 +14,7 @@
 
 char * buf = NULL;
 char * curbuf = NULL;
+char * end = NULL;
 size_t bufsize;
 int cont = 0;
 int expect_break_response=0;
@@ -21,11 +22,54 @@
 
 void gdb_debug_enter(genesis_context * gen, uint32_t pc)
 {
+	fcntl(STDIN_FILENO, FD_SETFL, 0);
 	resume_pc = pc;
+	cont = 0;
+	uint8_t partial = 0;
 	while(!cont)
 	{
+		if (!curbuf) {
+			int numread = read(STDIN_FILENO, buf, bufsize);
+			curbuf = buf;
+			end = buf + numread;
+		} else if (partial) {
+			if (curbuf != buf) {
+				memmove(curbuf, buf, end-curbuf);
+				end -= cufbuf - buf;
+			}
+			int numread = read(STDIN_FILENO, end, bufsize - (end-buf));
+			end += numread;
+			curbuf = buf;
+		}
+		for (; curbuf < end; curbuf++)
+		{
+			if (*curbuf == '$')
+			{
+				curbuf++;
+				char * start = curbuf;
+				while (curbuf < end && *curbuf != '#') {
+					curbuf++;
+				}
+				if (*curbuf == '#') {
+					//check to make sure we've received the checksum bytes
+					if (end-curbuf >= 2) {
+						//TODO: verify checksum
+						//Null terminate payload
+						*curbuf = 0
+						//send acknowledgement
+						write(FILENO_STDOUT, "+", 1);
+						gdb_run_command(genesis_context * gen, start);
+						curbuf += 2;
+					}
+				} else {
+					curbuf--;
+					partial = 1;
+					break;
+				}
+			}
+		}
 	}
-	cont = 0;
+	fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK);
 }
 
 void gdb_run_command(genesis_context * gen, char * command)
@@ -90,7 +134,7 @@
 	}
 }
 
-void gdb_command_poll(genesis_context * gen)
+int gdb_command_poll(genesis_context * gen)
 {
 	for(;;)
 	{
@@ -104,10 +148,10 @@
 			}
 			curbuf = buf + bufsize/2;
 		}
-		int numread = read(STDIN_FILENO, buf, bufsize - (curbuf-buf));
+		int numread = read(STDIN_FILENO, buf, bufsize);
 		if (numread < 0) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
-				return;
+				return 0;
 			} else {
 				fprintf(stderr, "Error %d while reading GDB commands from stdin", errno);
 				exit(1);
@@ -115,8 +159,16 @@
 		} else if (numread == 0) {
 			exit(0);
 		}
-		gdb_run_commands(genesis_context * gen);
+		for (curbuf = buf, end = buf+numread; curbuf < end; curbuf++)
+		{
+			if (*curbuf = 0x03)
+			{
+				curbuf++;
+				return 1;
+			}
+		}
 	}
+	return 0;
 }
 
 void gdb_remote_init()
--- a/psg.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/psg.c	Mon Jan 06 22:54:05 2014 -0800
@@ -5,6 +5,7 @@
 */
 #include "psg.h"
 #include "render.h"
+#include "blastem.h"
 #include <string.h>
 #include <stdlib.h>
 
@@ -118,7 +119,9 @@
 			}
 			context->audio_buffer[context->buffer_pos++] = acc;
 			if (context->buffer_pos == context->samples_frame) {
-				render_wait_psg(context);
+				if (!headless) {
+					render_wait_psg(context);
+				}
 			}
 		}
 		context->cycles += context->clock_inc;
--- a/render_sdl.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/render_sdl.c	Mon Jan 06 22:54:05 2014 -0800
@@ -18,7 +18,7 @@
 SDL_Surface *screen;
 uint8_t render_dbg = 0;
 uint8_t debug_pal = 0;
-uint8_t render_gl;
+uint8_t render_gl = 1;
 
 uint32_t last_frame = 0;
 
@@ -100,7 +100,7 @@
 }
 
 #ifndef DISABLE_OPENGL
-GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], at_pos;
+GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, at_pos;
 
 GLfloat vertex_data[] = {
 	-1.0f, -1.0f,
@@ -196,6 +196,7 @@
 		}
 		un_textures[0] = glGetUniformLocation(program, "textures[0]");
 		un_textures[1] = glGetUniformLocation(program, "textures[1]");
+		un_width = glGetUniformLocation(program, "width");
 		at_pos = glGetAttribLocation(program, "pos");
 	} else {
 #endif
@@ -363,6 +364,8 @@
 	glBindTexture(GL_TEXTURE_2D, (context->regs[REG_MODE_4] & BIT_INTERLACE) ? textures[1] : textures[2]);
 	glUniform1i(un_textures[1], 1);
 
+	glUniform1f(un_width, context->latched_mode & BIT_H40 ? 320.0f : 256.0f);
+
 	glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
 	glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
 	glEnableVertexAttribArray(at_pos);
--- a/shaders/default.v.glsl	Thu Oct 31 01:00:32 2013 -0700
+++ b/shaders/default.v.glsl	Mon Jan 06 22:54:05 2014 -0800
@@ -2,9 +2,10 @@
 
 attribute vec2 pos;
 varying vec2 texcoord;
+uniform float width;
 
 void main()
 {
 	gl_Position = vec4(pos, 0.0, 1.0);
-	texcoord = sign(pos) * vec2(320.0/1024.0, 240.0/-512.0) + vec2(320.0/1024.0, 240.0/512.0);
+	texcoord = sign(pos) * vec2(width/1024.0, 240.0/-512.0) + vec2(width/1024.0, 240.0/512.0);
 }
--- a/transz80.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/transz80.c	Mon Jan 06 22:54:05 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #include "z80inst.h"
@@ -29,6 +29,11 @@
 	return context;
 }
 
+z80_context * z80_vdp_port_write(uint16_t location, z80_context * context, uint8_t value)
+{
+	return context;
+}
+
 int main(int argc, char ** argv)
 {
 	long filesize;
--- a/vdp.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/vdp.c	Mon Jan 06 22:54:05 2014 -0800
@@ -50,12 +50,18 @@
 	memset(context, 0, sizeof(*context));
 	context->vdpmem = malloc(VRAM_SIZE);
 	memset(context->vdpmem, 0, VRAM_SIZE);
-	/*context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (render_depth() / 8));
-	memset(context->framebuf, 0, FRAMEBUF_ENTRIES * (render_depth() / 8));
-	context->evenbuf = malloc(FRAMEBUF_ENTRIES * (render_depth() / 8));
-	memset(context->evenbuf, 0, FRAMEBUF_ENTRIES * (render_depth() / 8));
+	/*
 	*/
-	render_alloc_surfaces(context);
+	if (headless) {
+		context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (32 / 8));
+		memset(context->framebuf, 0, FRAMEBUF_ENTRIES * (32 / 8));
+		context->evenbuf = malloc(FRAMEBUF_ENTRIES * (32 / 8));
+		memset(context->evenbuf, 0, FRAMEBUF_ENTRIES * (32 / 8));
+		context->b32 = 1;
+	} else {
+		render_alloc_surfaces(context);
+		context->b32 = render_depth() == 32;
+	}
 	context->framebuf = context->oddbuf;
 	context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
 	memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
@@ -64,7 +70,7 @@
 	context->sprite_draws = MAX_DRAWS;
 	context->fifo_write = 0;
 	context->fifo_read = -1;
-	context->b32 = render_depth() == 32;
+
 	if (!color_map_init_done) {
 		uint8_t b,g,r;
 		for (uint16_t color = 0; color < (1 << 12); color++) {
@@ -229,7 +235,7 @@
 	       "0A: %.2X | H-Int Counter: %u\n"
 	       "0F: %.2X | Auto-increment: $%X\n"
 	       "10: %.2X | Scroll A/B Size: %sx%s\n",
-	       context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F,
+	       context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR],
 	       context->regs[REG_HINT], context->regs[REG_HINT],
 	       context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
 	       context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
@@ -749,65 +755,36 @@
 				plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
 				uint32_t * colors = context->colors;
 				src = 0;
-				uint8_t sprite_color = *sprite_buf & 0x3F;
-				if (sprite_color == 0x3E || sprite_color == 0x3F) {
-					if (sprite_color == 0x3F) {
-						colors += CRAM_SIZE;
-						src = DBG_SHADOW;
-					} else {
+				pixel = context->regs[REG_BG_COLOR];
+				src = DBG_SRC_BG;
+				if (*plane_b & 0xF) {
+					pixel = *plane_b;
+					src = DBG_SRC_B;
+				}
+				if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
+					pixel = *plane_a;
+					src = DBG_SRC_A;
+				}
+				if (*sprite_buf & 0xF) {
+					uint8_t sprite_color = *sprite_buf & 0x3F;
+					if (sprite_color == 0x3E) {
 						colors += CRAM_SIZE*2;
-						src = DBG_HILIGHT;
-					}
-					if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) {
-						pixel = *plane_a;
-						src |= a_src;
-					} else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) {
-						pixel = *plane_b;
-						src |= DBG_SRC_B;
-					} else if (*plane_a & 0xF) {
-						pixel = *plane_a;
-						src |= a_src;
-					} else if (*plane_b & 0xF){
-						pixel = *plane_b;
-						src |= DBG_SRC_B;
-					} else {
-						pixel = context->regs[REG_BG_COLOR] & 0x3F;
-						src |= DBG_SRC_BG;
-					}
-				} else {
-					if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) {
+						src |= DBG_HILIGHT;
+					} else if (sprite_color == 0x3F) {
+						colors += CRAM_SIZE;
+						src |= DBG_SHADOW;
+					} else if ((*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
 						pixel = *sprite_buf;
 						src = DBG_SRC_S;
-					} else if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) {
-						pixel = *plane_a;
-						src = a_src;
-					} else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) {
-						pixel = *plane_b;
-						src = DBG_SRC_B;
-					} else {
-						if (!(*plane_a & BUF_BIT_PRIORITY || *plane_a & BUF_BIT_PRIORITY)) {
+						if ((pixel & 0xF) == 0xE) {
+							src |= DBG_SHADOW;
 							colors += CRAM_SIZE;
-							src = DBG_SHADOW;
 						}
-						if (*sprite_buf & 0xF) {
-							pixel = *sprite_buf;
-							if (*sprite_buf & 0xF == 0xE) {
-								colors = context->colors;
-								src = DBG_SRC_S;
-							} else {
-								src |= DBG_SRC_S;
-							}
-						} else if (*plane_a & 0xF) {
-							pixel = *plane_a;
-							src |= a_src;
-						} else if (*plane_b & 0xF){
-							pixel = *plane_b;
-							src |= DBG_SRC_B;
-						} else {
-							pixel = context->regs[REG_BG_COLOR] & 0x3F;
-							src |= DBG_SRC_BG;
-						}
+
 					}
+				} else if (!((*plane_a | *plane_b) & BUF_BIT_PRIORITY)) {
+					colors += CRAM_SIZE;
+					src |= DBG_SHADOW;
 				}
 				pixel &= 0x3F;
 				uint32_t outpixel;
@@ -825,31 +802,21 @@
 			}
 		} else {
 			for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
-				uint8_t pixel;
-				src = 0;
 				plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
 				plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
-				if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) {
-					pixel = *sprite_buf;
-					src = DBG_SRC_S;
-				} else if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) {
-					pixel = *plane_a;
-					src = a_src;
-				} else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) {
+				uint8_t pixel = context->regs[REG_BG_COLOR];
+				src = DBG_SRC_BG;
+				if (*plane_b & 0xF) {
 					pixel = *plane_b;
 					src = DBG_SRC_B;
-				} else if (*sprite_buf & 0xF) {
+				}
+				if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
+					pixel = *plane_a;
+					src = DBG_SRC_A;
+				}
+				if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
 					pixel = *sprite_buf;
 					src = DBG_SRC_S;
-				} else if (*plane_a & 0xF) {
-					pixel = *plane_a;
-					src = a_src;
-				} else if (*plane_b & 0xF){
-					pixel = *plane_b;
-					src = DBG_SRC_B;
-				} else {
-					pixel = context->regs[REG_BG_COLOR] & 0x3F;
-					src = DBG_SRC_BG;
 				}
 				uint32_t outpixel;
 				if (context->debug) {
@@ -862,15 +829,8 @@
 				} else {
 					*(dst++) = outpixel;
 				}
-				//*dst = (context->cram[pixel & 0x3F] & 0xEEE) | ((pixel & BUF_BIT_PRIORITY) ? FBUF_BIT_PRIORITY : 0) | src;
 			}
 		}
-	} else {
-		//dst = context->framebuf + line * 320;
-		//sprite_buf = context->linebuf + col * 8;
-		//plane_a = context->tmp_buf_a + 16 - (context->hscroll_a & 0x7);
-		//plane_b = context->tmp_buf_b + 16 - (context->hscroll_b & 0x7);
-		//end = dst + 8;
 	}
 	context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
 	context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
@@ -1243,12 +1203,55 @@
 		if (context->flags & FLAG_DMA_RUN) {
 			run_dma_src(context, 0);
 		}
-	} else {
-		render_sprite_cells(context);
-		render_sprite_cells(context);
-		render_sprite_cells(context);
-		render_sprite_cells(context);
+		for (int i = 0; i < 19; i++)
+		{
+			scan_sprite_table(line, context);
+		}
+		external_slot(context);
+		for (int i = 0; i < 21; i++)
+		{
+			scan_sprite_table(line, context);
+		}
+		//reverse context slot counter so it counts the number of sprite slots
+		//filled rather than the number of available slots
+		//context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
+		context->cur_slot = MAX_SPRITES_LINE-1;
+		context->sprite_draws = MAX_DRAWS;
+		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
+		for (int column = 2; column < 42; column += 8)
+		{
+			external_slot(context);
+			if (context->flags & FLAG_DMA_RUN) {
+				run_dma_src(context, 0);
+			}
+			read_sprite_x(line, context);
+
+			external_slot(context);
+			if (context->flags & FLAG_DMA_RUN) {
+				run_dma_src(context, 0);
+			}
+			read_sprite_x(line, context);
+
+			external_slot(context);
+			if (context->flags & FLAG_DMA_RUN) {
+				run_dma_src(context, 0);
+			}
+			read_sprite_x(line, context);
+
+			read_sprite_x(line, context);
+		}
+		external_slot(context);
+		if (context->flags & FLAG_DMA_RUN) {
+			run_dma_src(context, 0);
+		}
+		external_slot(context);
+		return;
 	}
+
+	render_sprite_cells(context);
+	render_sprite_cells(context);
+	render_sprite_cells(context);
+	render_sprite_cells(context);
 	context->sprite_index = 0x80;
 	context->slot_counter = MAX_SPRITES_LINE;
 	for (int i = 0; i < 19; i++)
@@ -1284,8 +1287,6 @@
 	render_sprite_cells(context);
 	scan_sprite_table(line, context);
 
-	render_sprite_cells(context);
-	scan_sprite_table(line, context);
 	read_map_scroll_a(0, line, context);
 	render_sprite_cells(context);
 	scan_sprite_table(line, context);
@@ -1391,14 +1392,14 @@
 		}
 		if (starti >= 0) {
 			if (context->b32) {
-				uint32_t color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
+				uint32_t color = context->colors[context->regs[REG_BG_COLOR]];
 				uint32_t * start = context->framebuf;
 				start += starti;
 				for (int i = 0; i < 2; i++) {
 					*(start++) = color;
 				}
 			} else {
-				uint16_t color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
+				uint16_t color = context->colors[context->regs[REG_BG_COLOR]];
 				uint16_t * start = context->framebuf;
 				start += starti;
 				for (int i = 0; i < 2; i++) {
@@ -1625,6 +1626,9 @@
 				if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
 					context->hv_latch = vdp_hv_counter_read(context);
 				}
+				if (reg == REG_BG_COLOR) {
+					value &= 0x3F;
+				}
 				context->regs[reg] = value;
 				if (reg == REG_MODE_4) {
 					context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
--- a/vgmplay.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/vgmplay.c	Mon Jan 06 22:54:05 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #include "render.h"
@@ -90,6 +90,8 @@
 {
 }
 
+uint8_t headless = 0;
+
 #define CYCLE_LIMIT MCLKS_NTSC/60
 tern_node * config;
 
@@ -111,7 +113,7 @@
 {
 	uint32_t fps = 60;
 	config = load_config(argv[0]);
-	render_init(320, 240, "vgm play", 60, 0);
+	render_init(320, 240, "vgm play", 60, 0, 0);
 
 
 	ym2612_context y_context;
--- a/ym2612.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/ym2612.c	Mon Jan 06 22:54:05 2014 -0800
@@ -10,6 +10,7 @@
 #include "ym2612.h"
 #include "render.h"
 #include "wave.h"
+#include "blastem.h"
 
 //#define DO_DEBUG_PRINT
 #ifdef DO_DEBUG_PRINT
@@ -482,7 +483,9 @@
 			}
 			context->buffer_pos += 2;
 			if (context->buffer_pos == context->sample_limit) {
-				render_wait_ym(context);
+				if (!headless) {
+					render_wait_ym(context);
+				}
 			}
 		}
 	}
--- a/z80_to_x86.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/z80_to_x86.c	Mon Jan 06 22:54:05 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #include "z80inst.h"
@@ -309,19 +309,19 @@
 
 void z80_print_regs_exit(z80_context * context)
 {
-	printf("A: %X\nB: %X\nC: %X\nD: %X\nE: %X\nHL: %X\nIX: %X\nIY: %X\nSP: %X\n\nIM: %d, IFF1: %d, IFF2: %d\n", 
+	printf("A: %X\nB: %X\nC: %X\nD: %X\nE: %X\nHL: %X\nIX: %X\nIY: %X\nSP: %X\n\nIM: %d, IFF1: %d, IFF2: %d\n",
 		context->regs[Z80_A], context->regs[Z80_B], context->regs[Z80_C],
-		context->regs[Z80_D], context->regs[Z80_E], 
-		(context->regs[Z80_H] << 8) | context->regs[Z80_L], 
-		(context->regs[Z80_IXH] << 8) | context->regs[Z80_IXL], 
-		(context->regs[Z80_IYH] << 8) | context->regs[Z80_IYL], 
+		context->regs[Z80_D], context->regs[Z80_E],
+		(context->regs[Z80_H] << 8) | context->regs[Z80_L],
+		(context->regs[Z80_IXH] << 8) | context->regs[Z80_IXL],
+		(context->regs[Z80_IYH] << 8) | context->regs[Z80_IYL],
 		context->sp, context->im, context->iff1, context->iff2);
 	puts("--Alternate Regs--");
-	printf("A: %X\nB: %X\nC: %X\nD: %X\nE: %X\nHL: %X\nIX: %X\nIY: %X\n", 
+	printf("A: %X\nB: %X\nC: %X\nD: %X\nE: %X\nHL: %X\nIX: %X\nIY: %X\n",
 		context->alt_regs[Z80_A], context->alt_regs[Z80_B], context->alt_regs[Z80_C],
-		context->alt_regs[Z80_D], context->alt_regs[Z80_E], 
-		(context->alt_regs[Z80_H] << 8) | context->alt_regs[Z80_L], 
-		(context->alt_regs[Z80_IXH] << 8) | context->alt_regs[Z80_IXL], 
+		context->alt_regs[Z80_D], context->alt_regs[Z80_E],
+		(context->alt_regs[Z80_H] << 8) | context->alt_regs[Z80_L],
+		(context->alt_regs[Z80_IXH] << 8) | context->alt_regs[Z80_IXL],
 		(context->alt_regs[Z80_IYH] << 8) | context->alt_regs[Z80_IYL]);
 	exit(0);
 }
@@ -363,8 +363,8 @@
 		}
 		dst = zcycles(dst, cycles);
 		if (inst->addr_mode & Z80_DIR) {
+			dst = translate_z80_ea(inst, &dst_op, dst, opts, DONT_READ, MODIFY);
 			dst = translate_z80_reg(inst, &src_op, dst, opts);
-			dst = translate_z80_ea(inst, &dst_op, dst, opts, DONT_READ, MODIFY);
 		} else {
 			dst = translate_z80_ea(inst, &src_op, dst, opts, READ, DONT_MODIFY);
 			dst = translate_z80_reg(inst, &dst_op, dst, opts);
@@ -418,7 +418,7 @@
 		dst = call(dst, (uint8_t *)z80_read_word);
 		dst = add_ir(dst, 2, opts->regs[Z80_SP], SZ_W);
 		if (inst->reg == Z80_AF) {
-			
+
 			dst = bt_ir(dst, 0, SCRATCH1, SZ_W);
 			dst = setcc_rdisp8(dst, CC_C, CONTEXT, zf_off(ZF_C));
 			dst = bt_ir(dst, 1, SCRATCH1, SZ_W);
@@ -452,7 +452,7 @@
 				dst = mov_rr(dst, opts->regs[Z80_A], SCRATCH1, SZ_B);
 				dst = mov_rdisp8r(dst, CONTEXT, zar_off(Z80_A), opts->regs[Z80_A], SZ_B);
 				dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, zar_off(Z80_A), SZ_B);
-		
+
 				//Flags are currently word aligned, so we can move
 				//them efficiently a word at a time
 				for (int f = ZF_C; f < ZF_NUM; f+=2) {
@@ -525,7 +525,7 @@
 		dst = call(dst, (uint8_t *)z80_write_byte);
 		dst = add_ir(dst, 1, opts->regs[Z80_DE], SZ_W);
 		dst = add_ir(dst, 1, opts->regs[Z80_HL], SZ_W);
-		
+
 		dst = sub_ir(dst, 1, opts->regs[Z80_BC], SZ_W);
 		uint8_t * cont = dst+1;
 		dst = jcc(dst, CC_Z, dst+2);
@@ -563,7 +563,7 @@
 		dst = call(dst, (uint8_t *)z80_write_byte);
 		dst = sub_ir(dst, 1, opts->regs[Z80_DE], SZ_W);
 		dst = sub_ir(dst, 1, opts->regs[Z80_HL], SZ_W);
-		
+
 		dst = sub_ir(dst, 1, opts->regs[Z80_BC], SZ_W);
 		uint8_t * cont = dst+1;
 		dst = jcc(dst, CC_Z, dst+2);
@@ -1162,7 +1162,7 @@
 		dst = setcc_rdisp8(dst, CC_P, CONTEXT, zf_off(ZF_PV));
 		dst = setcc_rdisp8(dst, CC_Z, CONTEXT, zf_off(ZF_Z));
 		dst = setcc_rdisp8(dst, CC_S, CONTEXT, zf_off(ZF_S));
-		
+
 		dst = mov_rr(dst, opts->regs[Z80_HL], SCRATCH2, SZ_W);
 		dst = ror_ir(dst, 8, SCRATCH1, SZ_W);
 		dst = call(dst, (uint8_t *)z80_write_byte);
@@ -1192,7 +1192,7 @@
 		dst = setcc_rdisp8(dst, CC_P, CONTEXT, zf_off(ZF_PV));
 		dst = setcc_rdisp8(dst, CC_Z, CONTEXT, zf_off(ZF_Z));
 		dst = setcc_rdisp8(dst, CC_S, CONTEXT, zf_off(ZF_S));
-		
+
 		dst = mov_rr(dst, opts->regs[Z80_HL], SCRATCH2, SZ_W);
 		dst = ror_ir(dst, 8, SCRATCH1, SZ_W);
 		dst = call(dst, (uint8_t *)z80_write_byte);
@@ -1255,7 +1255,7 @@
 					dst = ror_ir(dst, 8, src_op.base, SZ_W);
 				} else {
 					dst = mov_rr(dst, opts->regs[inst->ea_reg], dst_op.base, SZ_B);
-				}	
+				}
 			} else {
 				dst = mov_rr(dst, src_op.base, dst_op.base, SZ_B);
 			}
@@ -1297,7 +1297,7 @@
 					dst = ror_ir(dst, 8, src_op.base, SZ_W);
 				} else {
 					dst = mov_rr(dst, opts->regs[inst->ea_reg], dst_op.base, SZ_B);
-				}	
+				}
 			} else {
 				dst = mov_rr(dst, src_op.base, dst_op.base, SZ_B);
 			}
@@ -1848,6 +1848,7 @@
 		return;
 	}
 	x86_z80_options * opts = context->options;
+	uint32_t start_address = address;
 	uint8_t * encoded = NULL, *next;
 	if (address < 0x4000) {
 		encoded = context->mem_pointers[0] + (address & 0x1FFF);
@@ -1896,7 +1897,7 @@
 			address += next-encoded;
 			if (address > 0xFFFF) {
 				address &= 0xFFFF;
-				
+
 			} else {
 				encoded = next;
 			}
@@ -1987,12 +1988,12 @@
 		}
 		bp_stub = dst;
 		native = call(native, bp_stub);
-		
+
 		//Calculate length of prologue
 		dst = z80_check_cycles_int(dst, address);
 		int check_int_size = dst-bp_stub;
 		dst = bp_stub;
-		
+
 		//Save context and call breakpoint handler
 		dst = call(dst, (uint8_t *)z80_save_context);
 		dst = push_r(dst, SCRATCH1);
--- a/ztestgen.c	Thu Oct 31 01:00:32 2013 -0700
+++ b/ztestgen.c	Mon Jan 06 22:54:05 2014 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #include "z80inst.h"
@@ -158,7 +158,7 @@
 	{
 		memcpy(&copy, inst, sizeof(copy));
 		inst = &copy;
-		if ((inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET) 
+		if ((inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET)
 			|| (addr_mode == Z80_IMMED && inst->op != Z80_IM))
 		{
 			copy.immed = rand() % (word_sized ? 65536 : 256);
@@ -236,7 +236,7 @@
 		break;
 	}
 	if (inst->reg != Z80_UNUSED && inst->reg != Z80_USE_IMMED) {
-		reg_usage[inst->reg] = 1;
+
 		if (word_sized) {
 			reg_values[inst->reg] = rand() % 65536;
 			reg_usage[z80_high_reg(inst->reg)] = 1;
@@ -244,13 +244,16 @@
 			reg_usage[z80_low_reg(inst->reg)] = 1;
 			reg_values[z80_low_reg(inst->reg)] = reg_values[inst->reg] & 0xFF;
 		} else {
-			reg_values[inst->reg] = rand() % 255;
-			uint8_t word_reg = z80_word_reg(inst->reg);
-			if (word_reg != Z80_UNUSED) {
-				reg_usage[word_reg] = 1;
-				reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF);
+			if (!reg_usage[inst->reg]) {
+				reg_values[inst->reg] = rand() % 255;
+				uint8_t word_reg = z80_word_reg(inst->reg);
+				if (word_reg != Z80_UNUSED) {
+					reg_usage[word_reg] = 1;
+					reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF);
+				}
 			}
 		}
+		reg_usage[inst->reg] = 1;
 	}
 	puts("--------------");
 	for (uint8_t reg = 0; reg < Z80_UNUSED; reg++) {
@@ -287,14 +290,14 @@
 		cur = ld_ir16(cur, Z80_BC, reg_values[Z80_A] << 8 | (i ? 0xFF : 0));
 		cur = push(cur, Z80_BC);
 		cur = pop(cur, Z80_AF);
-	
+
 		//setup other regs
 		for (uint8_t reg = Z80_BC; reg <= Z80_IY; reg++) {
 			if (reg != Z80_AF && reg != Z80_SP) {
 				cur = ld_ir16(cur, reg, reg_values[reg]);
 			}
 		}
-	
+
 		//copy instruction
 		if (instlen == 3) {
 			memcpy(cur, instbuf, 2);
@@ -303,7 +306,7 @@
 			memcpy(cur, instbuf, instlen);
 			cur += instlen;
 		}
-	
+
 		//immed/displacement byte(s)
 		if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) {
 			*(cur++) = inst->ea_reg;
@@ -325,6 +328,11 @@
 		if (!i) {
 			//Save AF from first run
 			cur = push(cur, Z80_AF);
+			if (is_mem) {
+				//Save memory location from frist run
+				cur = ld_mema(cur, address);
+				cur = push(cur, Z80_AF);
+			}
 		} else {
 			//Pop AF from first run for final result
 			for (int reg = Z80_BC; reg <= Z80_IY; reg++) {
@@ -340,6 +348,21 @@
 					break;
 				}
 			}
+			if (is_mem) {
+				//Pop memory location from frist run
+				for (int reg = Z80_BC; reg <= Z80_IY; reg++) {
+					if (reg != Z80_AF && !reg_usage[reg]) {
+						cur = pop(cur, reg);
+						cur = push(cur, Z80_AF);
+						cur = ld_mema(cur, address);
+						cur = ld_rr8(cur, Z80_A, z80_low_reg(reg));
+						cur = pop(cur, Z80_AF);
+						reg_usage[reg] = 1;
+						reg_usage[z80_low_reg(reg)] = 1;
+						break;
+					}
+				}
+			}
 		}
 	}
 
@@ -364,7 +387,7 @@
 			cur = pop(cur, Z80_AF);
 		}
 	}
-	
+
 	//halt
 	*(cur++) = 0x76;
 	sprintf(pathbuf + strlen(pathbuf), "/%s.bin", disbuf);
@@ -376,7 +399,7 @@
 
 uint8_t should_skip(z80inst * inst)
 {
-	return inst->op >= Z80_JP || (inst->op >= Z80_LDI && inst->op <= Z80_CPDR) || inst->op == Z80_HALT 
+	return inst->op >= Z80_JP || (inst->op >= Z80_LDI && inst->op <= Z80_CPDR) || inst->op == Z80_HALT
 		|| inst->op == Z80_DAA || inst->op == Z80_RLD || inst->op == Z80_RRD || inst->op == Z80_NOP
 		|| inst->op == Z80_DI || inst->op == Z80_EI;
 }