changeset 2227:eaaf28af3c94

Implement VDP read latency and invalid write delays revealed by Ti_'s instruction timing ROM
author Michael Pavone <pavone@retrodev.com>
date Mon, 05 Sep 2022 22:18:25 -0700
parents d15c68157288
children 0db9dc6a9020
files genesis.c vdp.c vdp.h
diffstat 3 files changed, 50 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/genesis.c	Mon Sep 05 12:00:02 2022 -0700
+++ b/genesis.c	Mon Sep 05 22:18:25 2022 -0700
@@ -634,7 +634,7 @@
 		} else if(vdp_port < 8) {
 			vdp_run_context_full(v_context, context->current_cycle);
 			before_cycle = v_context->cycles;
-			blocked = vdp_control_port_write(v_context, value);
+			blocked = vdp_control_port_write(v_context, value, context->current_cycle);
 			if (blocked) {
 				while (blocked) {
 					while(v_context->flags & FLAG_DMA_RUN) {
@@ -653,7 +653,7 @@
 					}
 
 					if (blocked < 0) {
-						blocked = vdp_control_port_write(v_context, value);
+						blocked = vdp_control_port_write(v_context, value, context->current_cycle);
 					} else {
 						blocked = 0;
 					}
@@ -719,7 +719,7 @@
 			vdp_data_port_write(gen->vdp, value << 8 | value);
 		} else if (vdp_port < 8) {
 			vdp_run_context_full(gen->vdp, context->Z80_CYCLE);
-			vdp_control_port_write(gen->vdp, value << 8 | value);
+			vdp_control_port_write(gen->vdp, value << 8 | value, context->Z80_CYCLE);
 		} else {
 			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
 		}
@@ -752,10 +752,10 @@
 #endif
 	sync_components(context, 0);
 	vdp_context * v_context = gen->vdp;
-	uint32_t before_cycle = v_context->cycles;
+	uint32_t before_cycle = context->current_cycle;
 	if (vdp_port < 0x10) {
 		if (vdp_port < 4) {
-			value = vdp_data_port_read(v_context);
+			value = vdp_data_port_read(v_context, &context->current_cycle, MCLKS_PER_68K);
 		} else if(vdp_port < 8) {
 			value = vdp_control_port_read(v_context);
 		} else {
@@ -767,13 +767,12 @@
 	} else {
 		value = get_open_bus_value(&gen->header);
 	}
-	if (v_context->cycles != before_cycle) {
+	if (context->current_cycle != before_cycle) {
 		//printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
-		context->current_cycle = v_context->cycles;
 		//Lock the Z80 out of the bus until the VDP access is complete
 		genesis_context *gen = context->system;
 		gen->bus_busy = 1;
-		sync_z80(gen, v_context->cycles);
+		sync_z80(gen, context->current_cycle);
 		gen->bus_busy = 0;
 	}
 #ifdef REFRESH_EMULATION
@@ -818,13 +817,17 @@
 	if (vdp_port < 0x10) {
 		//These probably won't currently interact well with the 68K accessing the VDP
 		vdp_run_context(gen->vdp, context->Z80_CYCLE);
+		uint32_t before = context->Z80_CYCLE;
 		if (vdp_port < 4) {
-			ret = vdp_data_port_read(gen->vdp);
+			ret = vdp_data_port_read(gen->vdp, &context->Z80_CYCLE, MCLKS_PER_Z80);
 		} else if (vdp_port < 8) {
 			ret = vdp_control_port_read(gen->vdp);
 		} else {
 			ret = vdp_hv_counter_read(gen->vdp);
 		}
+		if (context->Z80_CYCLE != before) {
+			gen->m68k->current_cycle += context->Z80_CYCLE - before;
+		}
 	} else {
 		//TODO: Figure out the correct value today
 		ret = 0xFFFF;
--- a/vdp.c	Mon Sep 05 12:00:02 2022 -0700
+++ b/vdp.c	Mon Sep 05 22:18:25 2022 -0700
@@ -40,6 +40,7 @@
 #define VBLANK_START_H40 (LINE_CHANGE_H40+2)
 #define VBLANK_START_H32 (LINE_CHANGE_H32+2)
 #define FIFO_LATENCY    3
+#define READ_LATENCY    3
 
 #define BORDER_TOP_V24     27
 #define BORDER_TOP_V28     11
@@ -1019,6 +1020,11 @@
 			}
 
 			break;
+		default:
+			if (!(context->cd & 4) && !start->partial && (context->regs[REG_MODE_2] & (BIT_128K_VRAM|BIT_MODE_5)) != (BIT_128K_VRAM|BIT_MODE_5)) {
+				start->partial = 1;
+				return;
+			}
 		}
 		context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1);
 		if (context->fifo_read == context->fifo_write) {
@@ -1040,7 +1046,7 @@
 
 			context->flags |= FLAG_READ_FETCHED;
 		}
-	} else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING))) {
+	} else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING)) && context->read_latency <= context->cycles) {
 		switch(context->cd & 0xF)
 		{
 		case VRAM_READ:
@@ -3785,7 +3791,7 @@
 	return hv;
 }
 
-int vdp_control_port_write(vdp_context * context, uint16_t value)
+int vdp_control_port_write(vdp_context * context, uint16_t value, uint32_t cpu_cycle)
 {
 	//printf("control port write: %X at %d\n", value, context->cycles);
 	if (context->flags & FLAG_DMA_RUN) {
@@ -3805,6 +3811,9 @@
 		//Should these be taken care of here or after the first write?
 		context->flags &= ~FLAG_READ_FETCHED;
 		context->flags2 &= ~FLAG2_READ_PENDING;
+		if (!(context->cd & 1)) {
+			context->read_latency = cpu_cycle + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*READ_LATENCY;
+		}
 		//printf("New Address: %X, New CD: %X\n", context->address, context->cd);
 		if (context->cd & 0x20) {
 			//
@@ -3918,7 +3927,7 @@
 		uint16_t full_val = value << 8 | context->pending_byte;
 		context->flags2 &= ~FLAG2_BYTE_PENDING;
 		//TODO: Deal with fact that Vbus->VDP DMA doesn't do anything in PBC mode
-		vdp_control_port_write(context, full_val);
+		vdp_control_port_write(context, full_val, context->cycles);
 		if (context->cd == VRAM_READ) {
 			context->cd = VRAM_READ8;
 		}
@@ -4056,7 +4065,7 @@
 	return value;
 }
 
-uint16_t vdp_data_port_read(vdp_context * context)
+uint16_t vdp_data_port_read(vdp_context * context, uint32_t *cpu_cycle, uint32_t cpu_divider)
 {
 	if (context->flags & FLAG_PENDING) {
 		context->flags &= ~FLAG_PENDING;
@@ -4081,10 +4090,27 @@
 		context->system->enter_debugger = 1;
 		return context->prefetch;
 	}
+	uint32_t starting_cycle = context->cycles;
 	while (!(context->flags & FLAG_READ_FETCHED)) {
 		vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
 	}
 	context->flags &= ~FLAG_READ_FETCHED;
+	//TODO: Make some logic analyzer captures to better characterize what's happening with read latency here
+	if (context->cycles != starting_cycle) {
+		uint32_t delta = context->cycles - *cpu_cycle;
+		uint32_t cpu_delta = delta / cpu_divider;
+		if (delta % cpu_divider) {
+			cpu_delta++;
+		}
+		*cpu_cycle += cpu_delta * cpu_divider;
+		if (*cpu_cycle - context->cycles < 2) {
+			context->read_latency = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*(READ_LATENCY - 1);
+		} else {
+			context->read_latency = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*READ_LATENCY;
+		}
+	} else {
+		context->read_latency = *cpu_cycle + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*(READ_LATENCY - 1);
+	}
 	return context->prefetch;
 }
 
@@ -4121,6 +4147,11 @@
 			idx = (idx+1) & (FIFO_SIZE-1);
 		} while(idx != context->fifo_write);
 	}
+	if (context->read_latency >= deduction) {
+		context->read_latency -= deduction;
+	} else {
+		context->read_latency = 0;
+	}
 }
 
 static uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context)
--- a/vdp.h	Mon Sep 05 12:00:02 2022 -0700
+++ b/vdp.h	Mon Sep 05 22:18:25 2022 -0700
@@ -205,6 +205,7 @@
 	uint32_t       pending_vint_start;
 	uint32_t       pending_hint_start;
 	uint32_t       top_offset;
+	uint32_t       read_latency;
 	uint16_t       vsram[MAX_VSRAM_SIZE];
 	uint16_t       vscroll_latch[2];
 	uint16_t       vcounter;
@@ -270,13 +271,13 @@
 void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles);
 uint8_t vdp_load_gst(vdp_context * context, FILE * state_file);
 uint8_t vdp_save_gst(vdp_context * context, FILE * outfile);
-int vdp_control_port_write(vdp_context * context, uint16_t value);
+int vdp_control_port_write(vdp_context * context, uint16_t value, uint32_t cpu_cycle);
 void vdp_control_port_write_pbc(vdp_context * context, uint8_t value);
 int vdp_data_port_write(vdp_context * context, uint16_t value);
 void vdp_data_port_write_pbc(vdp_context * context, uint8_t value);
 void vdp_test_port_write(vdp_context * context, uint16_t value);
 uint16_t vdp_control_port_read(vdp_context * context);
-uint16_t vdp_data_port_read(vdp_context * context);
+uint16_t vdp_data_port_read(vdp_context * context, uint32_t *cpu_cycle, uint32_t cpu_divider);
 uint8_t vdp_data_port_read_pbc(vdp_context * context);
 void vdp_latch_hv(vdp_context *context);
 uint16_t vdp_hv_counter_read(vdp_context * context);