diff vdp.c @ 54:3b79cbcf6846

Get Flavio's color bar demo kind of sort of working
author Mike Pavone <pavone@retrodev.com>
date Tue, 18 Dec 2012 02:16:42 -0800
parents 3fc57e1a2c56
children a28b1dfe1af2
line wrap: on
line diff
--- a/vdp.c	Sun Dec 16 22:25:29 2012 -0800
+++ b/vdp.c	Tue Dec 18 02:16:42 2012 -0800
@@ -2,7 +2,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define MCLKS_LINE 3420
 #define NTSC_ACTIVE 225
 #define PAL_ACTIVE 241
 #define BUF_BIT_PRIORITY 0x40
@@ -20,16 +19,24 @@
 #define FLAG_CAN_MASK  0x2
 #define FLAG_MASKED    0x4
 #define FLAG_WINDOW    0x8
+#define FLAG_PENDING   0x10
+#define FLAG_UNUSED_SLOT 0x20
+
+#define FIFO_SIZE 4
 
 void init_vdp_context(vdp_context * context)
 {
 	memset(context, 0, sizeof(context));
 	context->vdpmem = malloc(VRAM_SIZE);
 	context->framebuf = malloc(FRAMEBUF_SIZE);
+	memset(context->framebuf, 0, FRAMEBUF_SIZE);
 	context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
+	memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
 	context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
 	context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE;
 	context->sprite_draws = MAX_DRAWS;
+	context->fifo_cur = malloc(sizeof(fifo_entry) * FIFO_SIZE);
+	context->fifo_end = context->fifo_cur + FIFO_SIZE;
 }
 
 void render_sprite_cells(vdp_context * context)
@@ -168,9 +175,52 @@
 	}
 }
 
+#define VRAM_READ 0
+#define VRAM_WRITE 1
+#define CRAM_READ 8
+#define CRAM_WRITE 3
+#define VSRAM_READ 4
+#define VSRAM_WRITE 5
+
 void external_slot(vdp_context * context)
 {
-	//TODO: Implement me
+	fifo_entry * start = (context->fifo_end - FIFO_SIZE);
+	//TODO: Implement DMA
+	if (context->fifo_cur != start && start->cycle <= context->cycles) {
+		switch (context->cd & 0x7)
+		{
+		case VRAM_WRITE:
+			if (start->partial) {
+				printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1);
+				context->vdpmem[context->address ^ 1] = start->value;
+			} else {
+				printf("VRAM Write: %X to %X\n", start->value >> 8, context->address);
+				context->vdpmem[context->address] = start->value >> 8;
+				start->partial = 1;
+				//skip auto-increment and removal of entry from fifo
+				return;
+			}
+			break;
+		case CRAM_WRITE:
+			printf("CRAM Write: %X to %X\n", start->value, context->address);
+			context->cram[context->address & (CRAM_SIZE-1)] = start->value;
+			break;
+		case VSRAM_WRITE:
+			if ((context->address & 63) < VSRAM_SIZE) {
+				printf("VSRAM Write: %X to %X\n", start->value, context->address);
+				context->vsram[context->address & 63] = start->value;
+			}
+			break;
+		}
+		context->address += context->regs[REG_AUTOINC];
+		fifo_entry * cur = start+1;
+		if (cur < context->fifo_cur) {
+			memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur));
+		}
+		context->fifo_cur -= 1;
+	} else {
+		context->flags |= FLAG_UNUSED_SLOT;
+	}
 }
 
 #define WINDOW_RIGHT 0x80
@@ -213,7 +263,7 @@
 			}
 			offset = address + line_offset + (((column - 2) * 2) & mask);
 			context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
-			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]);
+			//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]);
 			offset = address + line_offset + (((column - 1) * 2) & mask);
 			context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
 			context->v_offset = (line) & 0x7;
@@ -524,7 +574,7 @@
 		address += line * 4;
 		context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
 		context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
-		printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
+		//printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
 		break;
 	case 36:
 	//!HSYNC high
@@ -743,16 +793,58 @@
 	context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL);
 }
 
+#define DISPLAY_ENABLE 0x40
+
+int is_refresh(vdp_context * context)
+{
+	uint32_t linecyc = context->cycles % MCLKS_LINE;
+	if (context->latched_mode & BIT_H40) {
+		linecyc = linecyc/16;
+		return (linecyc == 73 || linecyc == 105 || linecyc == 137 || linecyc == 169 || linecyc == 201);
+	} else {
+		linecyc = linecyc/20;
+		return (linecyc == 66 || linecyc == 98 || linecyc == 130 || linecyc == 162);
+	}
+}
+
+void check_render_bg(vdp_context * context, int32_t line)
+{
+	if (line > 0) {
+		line -= 1;
+		uint16_t * start = NULL, *end = NULL;
+		uint32_t linecyc = (context->cycles % MCLKS_LINE);
+		if (context->latched_mode & BIT_H40) {
+			linecyc /= 16;
+			if (linecyc >= 55 && linecyc <= 207 && !((linecyc-55) % 8)) {
+				uint32_t x = ((linecyc-55)&(~0xF))*2;
+				start = context->framebuf + line * 320 + x;
+				end = start + 16;
+			}
+		} else {
+			linecyc /= 20;
+			if (linecyc >= 48 && linecyc <= 168 && !((linecyc-48) % 8)) {
+				uint32_t x = ((linecyc-48)&(~0xF))*2;
+				start = context->framebuf + line * 256 + x;
+				end = start + 16;
+			}
+		}
+		while (start != end) {
+			*start = context->regs[REG_BG_COLOR] & 0x3F;
+			++start;
+		}
+	}
+}
+
 void vdp_run_context(vdp_context * context, uint32_t target_cycles)
 {
 	while(context->cycles < target_cycles)
 	{
 		uint32_t line = context->cycles / MCLKS_LINE;
 		uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
-		if (line < active_lines) {
-			if (!line) {
-				latch_mode(context);
-			}
+		if (!line) {
+			latch_mode(context);
+		}
+		if (line < active_lines && context->regs[REG_MODE_2] & DISPLAY_ENABLE) {
 			//first sort-of active line is treated as 255 internally
 			//it's used for gathering sprite info for line 
 			line = (line - 1) & 0xFF;
@@ -762,15 +854,26 @@
 			if (context->latched_mode & BIT_H40){
 				//TODO: Deal with nasty clock switching during HBLANK
 				linecyc = linecyc/16;
+				vdp_h40(line, linecyc, context);
 				context->cycles += 16;
-				vdp_h40(line, linecyc, context);
 			} else {
 				linecyc = linecyc/20;
+				vdp_h32(line, linecyc, context);
 				context->cycles += 20;
-				vdp_h32(line, linecyc, context);
 			}
 		} else {
-			//TODO: Empty FIFO
+			if (!is_refresh(context)) {
+				external_slot(context);
+			}
+			if (line < active_lines) {
+				check_render_bg(context, line);
+			}
+			if (context->latched_mode & BIT_H40){
+				//TODO: Deal with nasty clock switching during HBLANK
+				context->cycles += 16;
+			} else {
+				context->cycles += 20;
+			}
 		}
 	}
 }
@@ -782,6 +885,94 @@
 	return context->cycles;
 }
 
+void vdp_control_port_write(vdp_context * context, uint16_t value)
+{
+	printf("control port write: %X\n", value);
+	if (context->flags & FLAG_PENDING) {
+		context->address = (context->address & 0x3FFF) | (value << 14);
+		context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C);
+		context->flags &= ~FLAG_PENDING;
+	} else {
+		if ((value & 0xC000) == 0x8000) {
+			//Register write
+			uint8_t reg = (value >> 8) & 0x1F;
+			if (reg < VDP_REGS) {
+				printf("register %d set to %X\n", reg, value);
+				context->regs[reg] = value;
+			}
+		} else {
+			context->flags |= FLAG_PENDING;
+			context->address = (context->address &0xC000) | (value & 0x3FFF);
+			context->cd = (context->cd &0x3C) | (value >> 14);
+		}
+	}
+}
+
+void vdp_data_port_write(vdp_context * context, uint16_t value)
+{
+	printf("data port write: %X\n", value);
+	context->flags &= ~FLAG_PENDING;
+	if (context->fifo_cur == context->fifo_end) {
+		printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
+	}
+	while (context->fifo_cur == context->fifo_end) {
+		vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+	}
+	context->fifo_cur->cycle = context->cycles;
+	context->fifo_cur->value = value;
+	context->fifo_cur->partial = 0;
+	context->fifo_cur++;
+}
+
+uint16_t vdp_control_port_read(vdp_context * context)
+{
+	context->flags &= ~FLAG_PENDING;
+	uint16_t value = 0x3400;
+	if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) {
+		value |= 0x200;
+	}
+	if (context->fifo_cur == context->fifo_end) {
+		value |= 0x100;
+	}
+	//TODO: Lots of other bits in status port
+	return value;
+}
+
+uint16_t vdp_data_port_read(vdp_context * context)
+{
+	context->flags &= ~FLAG_PENDING;
+	if (!(context->cd & 1)) {
+		return 0;
+	}
+	//Not sure if the FIFO should be drained before processing a read or not, but it would make sense
+	context->flags &= ~FLAG_UNUSED_SLOT;
+	while (!(context->flags & FLAG_UNUSED_SLOT)) {
+		vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+	}
+	uint16_t value = 0;
+	switch (context->cd & 0x7)
+	{
+	case VRAM_READ:
+		value = context->vdpmem[context->address] << 8;
+		context->flags &= ~FLAG_UNUSED_SLOT;
+		while (!(context->flags & FLAG_UNUSED_SLOT)) {
+			vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+		}
+		value |= context->vdpmem[context->address ^ 1];
+		break;
+	case CRAM_READ:
+		value = context->cram[(context->address/2) & (CRAM_SIZE-1)];
+		break;
+	case VSRAM_READ:
+		if (((context->address / 2) & 63) < VSRAM_SIZE) {
+			value = context->vsram[context->address & 63];
+		}
+		break;
+	}
+	context->address += context->regs[REG_AUTOINC];
+	return value;
+}
+
 #define GST_VDP_REGS 0xFA
 #define GST_VDP_MEM 0x12478
 
@@ -802,3 +993,4 @@
 	fseek(state_file, GST_VDP_MEM, SEEK_SET);
 	fread(context->vdpmem, 1, VRAM_SIZE, state_file);
 }
+