diff src/vdp.c @ 43:6e7bfe83d2b0

Changed the design to vastly simplify the video hardware and support a 23-bit address space on the CPU
author Michael Pavone <pavone@retrodev.com>
date Sat, 27 Aug 2016 22:38:31 -0700
parents 083347ccd508
children
line wrap: on
line diff
--- a/src/vdp.c	Mon Apr 11 23:35:51 2016 -0700
+++ b/src/vdp.c	Sat Aug 27 22:38:31 2016 -0700
@@ -4,225 +4,105 @@
 #include "vdp.h"
 #include "system.h"
 
+#define MAX_ACTIVE_LINES 240
+#define TOTAL_LINES 262
+#define ACTIVE_WIDTH 320
+#define TOTAL_WIDTH 416
+
+#define VDP_STATUS_FB_SELECT    1
+#define VDP_STATUS_PENDING_VINT 2
+#define VDP_STATUS_VBLANK       4
+#define VDP_STATUS_CRAM_PENDING 8
+#define VDP_STATUS_VINT_ENABLED 0x2000
+#define VDP_STATUS_DEPTH        0x4000
+#define VDP_STATUS_ENABLED      0x8000
+
 void vdp_init(vdp *context, uint32_t clock_div)
 {
 	memset(context, 0, sizeof(vdp));
 	//clock div specifies the pixel clock divider
 	//but our emulation step is half that fast
 	context->clock_inc = clock_div*2;
-	context->drawbuffer = context->linebuffers;
-	context->readbuffer = context->linebuffers+320;
 }
 
 void vdp_run(vdp *context, uint32_t target)
 {
+	uint8_t *current_fb = context->status & VDP_STATUS_FB_SELECT ? context->vram + 64*1024 : context->vram;
+	
 	while (context->cycles < target)
 	{
 		context->hcounter+=2;
-		if (context->hcounter == 416) {
+		if (context->hcounter == TOTAL_WIDTH) {
 			context->hcounter = 0;
 			context->vcounter++;
-			if (context->vcounter == 262) {
+			if (context->vcounter == TOTAL_LINES) {
 				context->vcounter = 0;
 			}
 		}
-		context->status &= ~(VDP_STATUS_VRAM|VDP_STATUS_SRAM);
-		//Render to linebuffer
-		if ((context->status & VDP_STATUS_ENABLED) && context->vcounter > 15 && context->vcounter < 240 && context->hcounter < 406) {
-			if (context->hcounter < 246) {
-				context->status |= VDP_STATUS_VRAM;
-				if (!context->hcounter) {
-					//flip linebuffers
-					if (context->drawbuffer == context->linebuffers) {
-						context->drawbuffer = context->linebuffers + 328;
-						context->readbuffer = context->linebuffers;
-					} else {
-						context->drawbuffer = context->linebuffers;
-						context->readbuffer = context->linebuffers + 328;
-					}
-					context->draw_dest = 0;
-					//enable sprite scanning
-					context->status |= VDP_STATUS_SPRITE_SCAN;
-					context->current_draw = 0;
-				}
-				if (context->draw_counter) {
-					context->draw_counter--;
-					uint16_t pixels = context->vram[context->draw_source++];
-					for (int i = context->hflip ? 0 : 12; i >= 0 && i < 16; i+= context->hflip ? 4 : -4)
-					{
-						uint8_t pixel = ((pixels >> i) & 0xF) | context->palpriority;
-						context->drawbuffer[context->draw_dest ^ (context->hflip << 2)] = pixel;
-						context->draw_dest++;
-					}
-				} else {
-					//00VV VVVV VVHH HHHH
-					uint16_t vpos = (context->vscroll & 0x7FF) + context->vcounter - 16;
-					uint16_t vmask = (context->vscroll >> 2) & 0x3E00;
-					uint16_t vcoarse = (vpos << 3) & 0x3FC0;
-					uint16_t vfine = vpos & 7;
-					uint16_t hcoarse = ((context->hscroll >> 3) + context->hcounter/6) & 0x3F;
-					uint16_t tableaddress = hcoarse | (vcoarse & ~vmask) | ((context->vscroll << 3) & vmask);
-					//printf("VCounter: %X, VScroll: %X, HCounter: %X, Table: %X\n", context->vcounter, context->vscroll, context->hcounter, tableaddress);
-					uint16_t entry = context->vram[tableaddress];
-					context->draw_source = (entry & 0x3FF) * 16;
-					if (entry & 0x1000) {
-						context->draw_source += 14 - vfine * 2;
-					} else {
-						context->draw_source += vfine * 2;
-					}
-					context->palpriority = entry >> 9 & 0x70;
-					context->draw_counter = 2;
-					context->hflip = (entry & 0x800) != 0;
-				}
-				if (context->status & VDP_STATUS_SPRITE_SCAN) {
-					context->status |= VDP_STATUS_SRAM;
-					uint16_t pos = context->sram[context->hcounter];
-					uint16_t y = pos & 0xFF;
-					uint16_t x = pos >> 8;
-					uint16_t atts = context->sram[context->hcounter+1];
-					x |= atts << 2 & 0x100;
-					if (x | y) {
-						uint16_t size = atts & 0x400 ? 16 : 8;
-						if (context->vcounter >= y && context->vcounter < y + size) {
-							uint16_t address = (atts & 0x3F) * 16;
-							if (atts & 0x1000) {
-								address += (size-1) * 2 - (context->vcounter - y) * 2;
-							} else {
-								address += (context->vcounter - y) * 2;
-							}
-							context->sprite_draws[context->current_draw].source = address;
-							context->sprite_draws[context->current_draw].x = x;
-							context->sprite_draws[context->current_draw].hflip = (atts & 0x800) != 0;
-							context->sprite_draws[context->current_draw].palpriority = 0x80 | (atts >> 9 & 0x50);
-							context->current_draw++;
-							if (size == 16) {
-								context->sprite_draws[context->current_draw].source = address + 32;
-								context->sprite_draws[context->current_draw].x = x + 8;
-								context->sprite_draws[context->current_draw].hflip = (atts & 0x800) != 0;
-								context->sprite_draws[context->current_draw].palpriority = 0x80 | (atts >> 9 & 0x50);
-								if (context->sprite_draws[context->current_draw].hflip) {
-									context->sprite_draws[context->current_draw].x -= 8;
-									context->sprite_draws[context->current_draw-1].x += 8;
-								}
-							}
-							context->current_draw++;
-							if (context->current_draw == 40) {
-								//no more rendering capacity
-								context->status &= ~VDP_STATUS_SPRITE_SCAN;
-								context->current_draw = 0;
-							}
-						}
-					} else {
-						//hit sprite list terminator
-						context->status &= ~VDP_STATUS_SPRITE_SCAN;
-						context->current_draw = 0;
-					}
-				}
-			} else {
-				sprite_draw *draw = context->sprite_draws + (context->current_draw >> 1);
-				if (draw->palpriority) {
-					context->status |= VDP_STATUS_VRAM;
-					uint16_t pixels = context->vram[draw->source + (context->current_draw & 1)];
-					uint16_t x = draw->x - 16 + (context->hscroll & 7);
-					for (int i = draw->hflip ? 0 : 12; i >= 0 && i < 16; i+= draw->hflip ? 4 : -4, x++)
-					{
-						uint8_t pixel = (pixels >> i) & 0xF;
-						if (pixel && x < 328 && ((draw->palpriority & 0x40) || !(context->drawbuffer[x] & 0x40))) {
-							context->drawbuffer[x ^ (draw->hflip << 2)] = pixel | draw->palpriority;
-						}
-					}
-					if (context->current_draw & 1) {
-						draw->palpriority = 0;
-					} else {
-						draw->x += 4;
-					}
-				}
-				context->current_draw++;
-			}
-		}
 		//Draw to framebuffer
-		if (context->vcounter > 8 && context->vcounter < 249 && context->hcounter < 320) {
-			if (!context->hcounter && context->vcounter == 9) {
+		if (context->vcounter < MAX_ACTIVE_LINES && context->hcounter < ACTIVE_WIDTH) {
+			if (!context->framebuffer) {
 				context->framebuffer = system_get_framebuffer(&context->pitch);
 				//pitch is in terms of bytes, but we want it in terms of pixels
 				context->pitch /= sizeof(uint16_t);
-				//clear pending interrupt flag since VBlank is over
-				context->status &= ~VDP_STATUS_PENDING_VINT;
 			}
-			uint16_t *dest = context->framebuffer + (context->vcounter - 9) * context->pitch + context->hcounter;
-			if (context->status & VDP_STATUS_ENABLED && context->vcounter > 16 && context->vcounter < 241) {
-				*dest = context->cram[0x3F & context->readbuffer[context->hcounter]];
-				dest++;
-				*dest = context->cram[0x3F & context->readbuffer[context->hcounter+1]];
+			uint16_t *dest = context->framebuffer + context->vcounter * context->pitch + context->hcounter;
+			if (
+				context->status & VDP_STATUS_ENABLED 
+				&& context->vcounter >= context->top_skip 
+				&& context->vcounter < MAX_ACTIVE_LINES - context->bottom_skip
+			) {
+				if (context->status & VDP_STATUS_DEPTH) {
+					uint16_t offset = context->start_offset + (context->vcounter - context->top_skip) * ACTIVE_WIDTH + context->hcounter;
+					//8bpp
+					*(dest++) = context->cram[current_fb[offset++]];
+					*dest = context->cram[current_fb[offset]];
+				} else {
+					//4bpp
+					uint8_t pixels = current_fb[context->start_offset + (context->vcounter - context->top_skip) * ACTIVE_WIDTH + context->hcounter >> 1];
+					*(dest++) = context->cram[context->pal_select | pixels >> 4];
+					*dest = context->cram[context->pal_select | (pixels & 0xF)];
+				}
 			} else {
-				//Display is disabled or we're in the border area, draw the background color
-				*dest = *context->cram;
-				dest++;
-				*dest = *context->cram;
+				*(dest++) = context->cram[0];
+				*dest = context->cram[0];
 			}
-		} else if(!context->hcounter && context->vcounter == 249) {
-			if (context->status & VDP_STATUS_ENABLED) {
-				context->status |= VDP_STATUS_PENDING_VINT;
-			}
+		} else if (context->framebuffer && context->hcounter < ACTIVE_WIDTH) {
 			system_framebuffer_updated();
 			context->framebuffer = NULL;
 		}
-		//Handle the FIFO
-		if (context->status & VDP_STATUS_FIFO) {
-			switch (context->fifo_dest)
-			{
-			case FIFO_DEST_VRAM:
-				if (!(context->status & VDP_STATUS_VRAM)) {
-					context->vram[context->dest_offset++] = context->fifo;
-					context->dest_offset &= sizeof(context->vram)/2-1;
-					context->status &= ~VDP_STATUS_FIFO;
-				}
-				break;
-			case FIFO_DEST_SRAM:
-				if (!(context->status & VDP_STATUS_SRAM)) {
-					context->sram[context->dest_offset++] = context->fifo;
-					context->dest_offset &= sizeof(context->sram)/2-1;
-					context->status &= ~VDP_STATUS_FIFO;
-				}
-				break;
-			case FIFO_DEST_CRAM:
-				context->cram[context->dest_offset++] = context->fifo;
-				context->dest_offset &= sizeof(context->cram)/2-1;
-				context->status &= ~VDP_STATUS_FIFO;
-				break;
+		if (!context->hcounter) {
+			if (context->vcounter == (context->vcounter - context->bottom_skip)) {
+				context->status |= VDP_STATUS_PENDING_VINT | VDP_STATUS_VBLANK;
+			} else if (context->vcounter == context->top_skip) {
+				//clear pending interrupt flag since VBlank is over
+				context->status &= ~(VDP_STATUS_PENDING_VINT | VDP_STATUS_VBLANK);
 			}
 		}
 		context->cycles += context->clock_inc;
 	}
 }
-void vdp_write_address(vdp *context, uint16_t value)
+void vdp_write_mode(vdp *context, uint16_t value)
 {
-	context->status &= ~VDP_STATUS_FIFO;
-	if (!(value & 0x8000)) {
-		context->fifo_dest = FIFO_DEST_VRAM;
-		context->dest_offset = (value & (sizeof(context->vram) -1))/2;
-	} else if ((value & 0xFF00) == 0xFE00) {
-		context->fifo_dest = FIFO_DEST_SRAM;
-		context->dest_offset = (value & (sizeof(context->sram) -1))/2;
-	} else if ((value & 0xFF00) == 0xFF00) {
-		context->fifo_dest = FIFO_DEST_CRAM;
-		context->dest_offset = (value & (sizeof(context->cram) -1))/2;
-	} 
+	uint16_t status_bits = VDP_STATUS_ENABLED | VDP_STATUS_DEPTH | VDP_STATUS_VINT_ENABLED | VDP_STATUS_FB_SELECT;
+	context->status &= ~status_bits;
+	context->status |= value & status_bits;
+	context->pal_select = value >> 7 & 0x30;
+	context->top_skip = value >> 6 & 0x1F;
+	context->bottom_skip = value >> 1 & 0x1F;
 }
 
-void vdp_write_data(vdp *context, uint16_t value)
+void vdp_write_cram(vdp *context, uint16_t value)
 {
-	context->fifo = value;
-	context->status |= VDP_STATUS_FIFO;
-}
-
-void vdp_write_hscroll(vdp *context, uint16_t value)
-{
-	context->hscroll = value & 0x1FF;
-	if (value & 0x8000) {
-		context->status |= VDP_STATUS_ENABLED;
+	if (context->status & VDP_STATUS_CRAM_PENDING) {
+		context->cram[context->pal_write_index++] = value;
+		if (!(--context->pal_write_count)) {
+			context->status &= ~VDP_STATUS_CRAM_PENDING;
+		}
 	} else {
-		context->status &= ~VDP_STATUS_ENABLED;
+		context->pal_write_count = value;
+		context->pal_write_index = value >> 8;
+		context->status |= VDP_STATUS_CRAM_PENDING;
 	}
 }
 
@@ -232,11 +112,12 @@
 		return 0;
 	} else if (context->status & VDP_STATUS_ENABLED) {
 		uint32_t next_line = context->vcounter + 1;
-		uint32_t next_line_cyc = context->cycles + ((416 - context->hcounter) >> 1) * context->clock_inc;
-		if (context->vcounter < 249) {
-			return next_line_cyc + (249 - next_line) * 832;
+		uint32_t next_line_cyc = context->cycles + ((TOTAL_WIDTH - context->hcounter) >> 1) * context->clock_inc;
+		uint32_t vint_line = (MAX_ACTIVE_LINES - context->bottom_skip);
+		if (context->vcounter < vint_line) {
+			return next_line_cyc + (vint_line - next_line) * (TOTAL_WIDTH >> 1) * context->clock_inc;
 		} else {
-			return next_line_cyc + (249 + 262 - next_line) * 832;
+			return next_line_cyc + (vint_line + TOTAL_LINES - next_line) * (TOTAL_WIDTH >> 1) * context->clock_inc;
 		}
 	} else {
 		return 0xFFFFFFFF;
@@ -252,3 +133,8 @@
 {
 	return (context->status & VDP_STATUS_PENDING_VINT) != 0;
 }
+
+uint8_t *vdp_get_back_buffer(vdp *context)
+{
+	return context->status & VDP_STATUS_FB_SELECT ? context->vram : context->vram + 64*1024;
+}