changeset 2144:10e4439d8f13

Fix speed of CDC to PCM RAM DMA
author Michael Pavone <pavone@retrodev.com>
date Sat, 26 Mar 2022 00:54:47 -0700
parents 67f20f9188b0
children 62a53c052d9b
files lc8951.c lc8951.h segacd.c
diffstat 3 files changed, 39 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/lc8951.c	Fri Mar 25 21:41:33 2022 -0700
+++ b/lc8951.c	Sat Mar 26 00:54:47 2022 -0700
@@ -85,11 +85,22 @@
 	//unclear if the difference is in the lc8951 or gate array
 	context->regs[IFSTAT] = 0xFF;
 	context->ar_mask = 0x1F;
-	context->clock_step = (2 + 2) * 6; // external divider, internal divider + DMA period
+	context->clock_step = (2 + 2); // external divider, internal divider
+	context->cycles_per_byte = context->clock_step * 6;
 	context->byte_handler = byte_handler;
 	context->handler_data = handler_data;
 	context->decode_end = CYCLE_NEVER;
 	context->transfer_end = CYCLE_NEVER;
+	context->next_byte_cycle = CYCLE_NEVER;
+}
+
+void lc8951_set_dma_multiple(lc8951 *context, uint32_t multiple)
+{
+	context->cycles_per_byte = context->clock_step * multiple;
+	if (context->transfer_end != CYCLE_NEVER) {
+		uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8);
+		context->transfer_end = context->next_byte_cycle + transfer_size * context->cycles_per_byte;
+	}
 }
 
 void lc8951_reg_write(lc8951 *context, uint8_t value)
@@ -130,7 +141,8 @@
 		if (context->ifctrl & BIT_DOUTEN) {
 			context->regs[IFSTAT] &= ~BIT_DTBSY;
 			uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8);
-			context->transfer_end = context->cycle + transfer_size * context->clock_step;
+			context->transfer_end = context->cycle + transfer_size * context->cycles_per_byte;
+			context->next_byte_cycle = context->cycle;
 			printf("DTTRG: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end);
 		}
 		break;
@@ -251,8 +263,9 @@
 			}
 			context->regs[STAT3] |= BIT_WLONG;
 		}
-		if (context->transfer_end != CYCLE_NEVER) {
+		if (context->cycle >= context->next_byte_cycle) {
 			if (context->byte_handler(context->handler_data, context->buffer[context->dac & (sizeof(context->buffer)-1)])) {
+				context->next_byte_cycle += context->cycles_per_byte;
 				context->dac++;
 				context->regs[DBCL]--;
 				if (context->regs[DBCL] == 0xFF) {
@@ -264,10 +277,12 @@
 							printf("Expected transfer end at %u but ended at %u\n", context->transfer_end, context->cycle);
 						}
 						context->transfer_end = CYCLE_NEVER;
+						context->next_byte_cycle = CYCLE_NEVER;
 					}
 				}
 			} else {
 				// pause transfer
+				context->next_byte_cycle = CYCLE_NEVER;
 				context->transfer_end = CYCLE_NEVER;
 			}
 		}
@@ -284,7 +299,8 @@
 			if (step_diff) {
 				context->cycle -= step_diff * context->clock_step;
 			}
-			context->transfer_end = context->cycle + transfer_size * context->clock_step;
+			context->transfer_end = context->cycle + transfer_size * context->cycles_per_byte;
+			context->next_byte_cycle = context->cycle;
 			if (step_diff) {
 				lc8951_run(context, cycle);
 			}
--- a/lc8951.h	Fri Mar 25 21:41:33 2022 -0700
+++ b/lc8951.h	Sat Mar 26 00:54:47 2022 -0700
@@ -10,8 +10,10 @@
 	void     *handler_data;
 	uint32_t cycle;
 	uint32_t clock_step;
+	uint32_t cycles_per_byte;
 	uint32_t decode_end;
 	uint32_t transfer_end;
+	uint32_t next_byte_cycle;
 	uint16_t sector_counter;
 
 	uint8_t  buffer[0x4000];
@@ -31,6 +33,7 @@
 } lc8951;
 
 void lc8951_init(lc8951 *context, lcd8951_byte_recv_fun byte_handler, void *handler_data);
+void lc8951_set_dma_multiple(lc8951 *context, uint32_t multiple);
 void lc8951_run(lc8951 *context, uint32_t cycle);
 void lc8951_reg_write(lc8951 *context, uint8_t value);
 uint8_t lc8951_reg_read(lc8951 *context);
--- a/segacd.c	Fri Mar 25 21:41:33 2022 -0700
+++ b/segacd.c	Sat Mar 26 00:54:47 2022 -0700
@@ -741,15 +741,29 @@
 		}
 		break;
 	}
-	case GA_CDC_CTRL:
+	case GA_CDC_CTRL: {
 		cdd_run(cd, m68k->current_cycle);
 		lc8951_ar_write(&cd->cdc, value);
 		//cd->gate_array[reg] &= 0xC000;
+		uint16_t old_dest = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7;
 		//apparently this clears EDT, should it also clear DSR?
 		cd->gate_array[reg] = value & 0x0700;
+		uint16_t dest = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7;
+		if (dest != old_dest) {
+			if (dest == DST_PCM_RAM) {
+				lc8951_set_dma_multiple(&cd->cdc, 21);
+			} else {
+				lc8951_set_dma_multiple(&cd->cdc, 6);
+			}
+			if ((old_dest < DST_MAIN_CPU || old_dest == 6) && dest >= DST_MAIN_CPU && dest != 6) {
+				lc8951_resume_transfer(&cd->cdc, m68k->current_cycle);
+			}
+			calculate_target_cycle(m68k);
+		}
 		cd->gate_array[GA_CDC_DMA_ADDR] = 0;
 		cd->cdc_dst_low = 0;
 		break;
+	}
 	case GA_CDC_REG_DATA:
 		cdd_run(cd, m68k->current_cycle);
 		printf("CDC write %X: %X @ %u\n", cd->cdc.ar, value, m68k->current_cycle);
@@ -1012,6 +1026,7 @@
 		cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3;
 		break;
 	default:
+		return 0;
 		printf("Invalid CDC transfer destination %d\n", dest);
 	}
 	return 1;