changeset 1335:26e72126f9d1

Fixes to sprite phase 2 so that sprite X reads use the exact same slot as on hardware in the case that there are fewer than the max number of sprites on each line. Re-read sprite Y from SAT cache during phase 2 and properly mask the calculated row. Fixes remaining issues with spinning cube scene in Overdrive 2.
author Michael Pavone <pavone@retrodev.com>
date Thu, 27 Apr 2017 23:08:49 -0700
parents 7757d605e365
children baaf05fd64c4
files vdp.c vdp.h
diffstat 2 files changed, 55 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/vdp.c	Thu Apr 27 09:32:21 2017 -0700
+++ b/vdp.c	Thu Apr 27 23:08:49 2017 -0700
@@ -98,6 +98,13 @@
 				context->border_bot = BORDER_TOP_V28;
 			}
 		}
+		if (context->regs[REG_MODE_4] & BIT_H40) {
+			context->max_sprites_frame = MAX_SPRITES_FRAME;
+			context->max_sprites_line = MAX_SPRITES_LINE;
+		} else {
+			context->max_sprites_frame = MAX_SPRITES_FRAME_H32;
+			context->max_sprites_line = MAX_SPRITES_LINE_H32;
+		}
 		if (context->state == INACTIVE) {
 			//Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
 			if (context->vcounter < context->inactive_start) {
@@ -544,7 +551,7 @@
 
 static void scan_sprite_table(uint32_t line, vdp_context * context)
 {
-	if (context->sprite_index && context->slot_counter) {
+	if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line) {
 		line += 1;
 		line &= 0xFF;
 		uint16_t ymask, ymin;
@@ -563,12 +570,8 @@
 			height_mult = 8;
 		}
 		context->sprite_index &= 0x7F;
-		if (context->regs[REG_MODE_4] & BIT_H40) {
-			if (context->sprite_index >= MAX_SPRITES_FRAME) {
-				context->sprite_index = 0;
-				return;
-			}
-		} else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) {
+		//TODO: Implement squirelly behavior documented by Kabuto
+		if (context->sprite_index >= context->max_sprites_frame) {
 			context->sprite_index = 0;
 			return;
 		}
@@ -579,19 +582,15 @@
 		//printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
 		if (y <= line && line < (y + height)) {
 			//printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
-			context->sprite_info_list[--(context->slot_counter)].size = context->sat_cache[address+2];
+			context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
 			context->sprite_info_list[context->slot_counter].index = context->sprite_index;
-			context->sprite_info_list[context->slot_counter].y = y-ymin;
+			context->sprite_info_list[context->slot_counter++].y = y-ymin;
 		}
 		context->sprite_index = context->sat_cache[address+3] & 0x7F;
-		if (context->sprite_index && context->slot_counter)
+		if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line)
 		{
-			if (context->regs[REG_MODE_4] & BIT_H40) {
-				if (context->sprite_index >= MAX_SPRITES_FRAME) {
-					context->sprite_index = 0;
-					return;
-				}
-			} else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) {
+			//TODO: Implement squirelly behavior documented by Kabuto
+			if (context->sprite_index >= context->max_sprites_frame) {
 				context->sprite_index = 0;
 				return;
 			}
@@ -601,13 +600,14 @@
 			//printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
 			if (y <= line && line < (y + height)) {
 				//printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
-				context->sprite_info_list[--(context->slot_counter)].size = context->sat_cache[address+2];
+				context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
 				context->sprite_info_list[context->slot_counter].index = context->sprite_index;
-				context->sprite_info_list[context->slot_counter].y = y-ymin;
+				context->sprite_info_list[context->slot_counter++].y = y-ymin;
 			}
 			context->sprite_index = context->sat_cache[address+3] & 0x7F;
 		}
 	}
+	//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
 }
 
 static void scan_sprite_table_mode4(vdp_context * context)
@@ -632,7 +632,6 @@
 				}
 				context->sprite_info_list[--(context->slot_counter)].size = size;
 				context->sprite_info_list[context->slot_counter].index = context->sprite_index;
-				context->sprite_info_list[context->slot_counter].y = y;
 			}
 			context->sprite_index++;
 		}
@@ -651,7 +650,6 @@
 					}
 					context->sprite_info_list[--(context->slot_counter)].size = size;
 					context->sprite_info_list[context->slot_counter].index = context->sprite_index;
-					context->sprite_info_list[context->slot_counter].y = y;
 				}
 				context->sprite_index++;
 			}
@@ -662,7 +660,10 @@
 
 static void read_sprite_x(uint32_t line, vdp_context * context)
 {
-	if (context->cur_slot >= context->slot_counter) {
+	if (context->cur_slot == context->max_sprites_line) {
+		context->cur_slot = 0;
+	}
+	if (context->cur_slot < context->slot_counter) {
 		if (context->sprite_draws) {
 			line += 1;
 			line &= 0xFF;
@@ -677,15 +678,26 @@
 				}
 				height *= 2;
 			}
+			uint16_t ymask, ymin;
+			if (context->double_res) {
+				ymask = 0x3FF;
+				ymin = 256;
+			} else {
+				ymask = 0x1FF;
+				ymin = 128;
+			}
 			uint16_t att_addr = mode5_sat_address(context) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
 			uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
 			uint8_t pal_priority = (tileinfo >> 9) & 0x70;
 			uint8_t row;
+			uint16_t cache_addr = context->sprite_info_list[context->cur_slot].index * 4;
+			int16_t y = ((context->sat_cache[cache_addr] << 8 | context->sat_cache[cache_addr+1]) & ymask) - ymin;
 			if (tileinfo & MAP_BIT_V_FLIP) {
-				row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line;
+				row = (y + height - 1) - line;
 			} else {
-				row = line-context->sprite_info_list[context->cur_slot].y;
+				row = line-y;
 			}
+			row &= ymask >> 4;
 			uint16_t address;
 			if (context->double_res) {
 				address = ((tileinfo & 0x3FF) << 6) + row * 4;
@@ -730,11 +742,11 @@
 			if (!context->sprite_draws) {
 				context->flags |= FLAG_DOT_OFLOW;
 			}
-			context->cur_slot--;
 		} else {
 			context->flags |= FLAG_DOT_OFLOW;
 		}
 	}
+	context->cur_slot++;
 }
 
 static void read_sprite_x_mode4(vdp_context * context)
@@ -1899,7 +1911,7 @@
 			}
 		}
 		context->sprite_index = 0x80;
-		context->slot_counter = MAX_SPRITES_LINE;
+		context->slot_counter = 0;
 		render_sprite_cells( context);
 		scan_sprite_table(context->vcounter, context);
 		CHECK_LIMIT
@@ -1981,10 +1993,11 @@
 	case 0:
 		render_map_output(context->vcounter, 0, context);
 		scan_sprite_table(context->vcounter, context);//Just a guess
-		//reverse context slot counter so it counts the number of sprite slots
-		//filled rather than the number of available slots
-		//context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
-		context->cur_slot = MAX_SPRITES_LINE-1;
+		//seems like the sprite table scan fills a shift register
+		//values are FIFO, but unused slots precede used slots
+		//so we set cur_slot to slot_counter and let it wrap around to
+		//the beginning of the list
+		context->cur_slot = context->slot_counter;
 		context->sprite_draws = MAX_DRAWS;
 		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
 		CHECK_LIMIT
@@ -2106,7 +2119,7 @@
 			}
 		}
 		context->sprite_index = 0x80;
-		context->slot_counter = MAX_SPRITES_LINE_H32;
+		context->slot_counter = 0;
 		render_sprite_cells( context);
 		scan_sprite_table(context->vcounter, context);
 		CHECK_LIMIT
@@ -2186,7 +2199,7 @@
 		//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_H32-1;
+		context->cur_slot = context->slot_counter;
 		context->sprite_draws = MAX_DRAWS_H32;
 		context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
 		CHECK_LIMIT
@@ -2376,8 +2389,8 @@
 	
 	if (mode_5) {
 		if (is_h40) {
-			buf_clear_slot = 161;
-			index_reset_slot = 165;
+			buf_clear_slot = 163;
+			index_reset_slot = 167;
 			bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2;
 			max_draws = MAX_DRAWS-1;
 			max_sprites = MAX_SPRITES_LINE;
@@ -2450,7 +2463,7 @@
 			memset(context->linebuf, 0, LINEBUF_SIZE);
 		} else if (context->hslot == index_reset_slot) {
 			context->sprite_index = index_reset_value;
-			context->slot_counter = max_sprites;
+			context->slot_counter = mode_5 ? 0 : max_sprites;
 		} else if (context->vcounter == vint_line && context->hslot == vint_slot) {
 			context->flags2 |= FLAG2_VINT_PENDING;
 			context->pending_vint_start = context->cycles;
@@ -2662,7 +2675,7 @@
 						context->flags2 &= ~FLAG2_EVEN_FIELD;
 					}
 				}
-				if (reg == REG_MODE_2) {
+				if (reg == REG_MODE_1 || reg == REG_MODE_2 || reg == REG_MODE_4) {
 					update_video_params(context);
 				}
 			}
--- a/vdp.h	Thu Apr 27 09:32:21 2017 -0700
+++ b/vdp.h	Thu Apr 27 23:08:49 2017 -0700
@@ -179,11 +179,6 @@
 	uint16_t    hscroll_b;
 	uint16_t    h40_lines;
 	uint16_t    output_lines;
-	uint8_t     hslot; //hcounter/2
-	uint8_t	    sprite_index;
-	uint8_t     sprite_draws;
-	int8_t      slot_counter;
-	int8_t      cur_slot;
 	sprite_draw sprite_draw_list[MAX_DRAWS];
 	sprite_info sprite_info_list[MAX_SPRITES_LINE];
 	uint8_t     sat_cache[SAT_CACHE_SIZE];
@@ -192,6 +187,13 @@
 	uint16_t    hv_latch;
 	uint16_t    prefetch;
 	uint16_t    test_port;
+	uint8_t     hslot; //hcounter/2
+	uint8_t	    sprite_index;
+	uint8_t     sprite_draws;
+	int8_t      slot_counter;
+	int8_t      cur_slot;
+	uint8_t     max_sprites_frame;
+	uint8_t     max_sprites_line;
 	uint8_t     fetch_tmp[2];
 	uint8_t     v_offset;
 	uint8_t     dma_cd;