diff lc8951.c @ 2062:07ed42bd7b4c segacd

Some progress on CDC and CDD emulation. Now passes first 3 "CDC INIT" tests in mcd-verificator
author Michael Pavone <pavone@retrodev.com>
date Fri, 28 Jan 2022 00:50:17 -0800
parents 70260f6051dd
children 02a9846668d1
line wrap: on
line diff
--- a/lc8951.c	Thu Jan 27 00:33:41 2022 -0800
+++ b/lc8951.c	Fri Jan 28 00:50:17 2022 -0800
@@ -1,4 +1,5 @@
 #include "lc8951.h"
+#include "backend.h"
 
 enum {
 	COMIN,
@@ -36,7 +37,7 @@
 //IFCTRL
 #define BIT_CMDIEN 0x80
 #define BIT_DTEIEN 0x40
-#define BIT_CECIEN 0x20
+#define BIT_DECIEN 0x20
 #define BIT_CMDBK  0x10
 #define BIT_DTWAI  0x08
 #define BIT_STWAI  0x04
@@ -52,6 +53,13 @@
 #define BIT_DTEN   0x02
 #define BIT_STEN   0x01
 
+//CTRL0
+#define BIT_DECEN 0x80
+#define BIT_WRRQ  0x04
+
+//STAT3
+#define BIT_VALST 0x80
+
 //datasheet timing info
 //3 cycles for memory operation
 //6 cycles min for DMA-mode host transfer
@@ -62,10 +70,12 @@
 	//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
 }
 
 void lc8951_reg_write(lc8951 *context, uint8_t value)
 {
+	printf("CDC write %X: %X\n", context->ar, value);
 	switch (context->ar)
 	{
 	case SBOUT:
@@ -100,6 +110,8 @@
 	case DTTRG:
 		if (value & 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;
 		}
 		break;
 	case DTACK:
@@ -111,11 +123,20 @@
 	case WAH_WRITE:
 		context->regs[WAH] = value;
 		break;
+	case CTRL0:
+		context->ctrl0 = value;
+		break;
+	case CTRL1:
+		context->ctrl1 = value;
+		break;
 	case PTL_WRITE:
 		context->regs[PTL] = value;
 		break;
 	case PTH_WRITE:
 		context->regs[PTH] = value;
+		context->ptl_internal = (context->regs[PTL] | (context->regs[PTH] << 8)) & (sizeof(context->buffer) - 1);
+		context->decoding = 1;
+		context->decode_end = context->cycle + 2352 * context->clock_step * 4;
 		break;
 	case RESET:
 		context->comin_count = 0;
@@ -152,6 +173,7 @@
 	} else {
 		value = context->regs[context->ar];
 	}
+	printf("CDC read %X: %X\n", context->ar, value);
 	context->ar++;
 	context->ar &= context->ar_mask;
 	return value;
@@ -161,3 +183,96 @@
 {
 	context->ar = value & context->ar_mask;
 }
+
+//25 MHz clock input (1/2 SCD MCLK)
+//internal /2 divider
+//3 cycles for each SRAM access (though might be crystal frequency rather than internal frequency)
+//6 cycle period for DMA transfer out
+//
+
+void lc8951_run(lc8951 *context, uint32_t cycle)
+{
+	for(; context->cycle < cycle; context->cycle += context->clock_step)
+	{
+		if (context->cycle >= context->decode_end) {
+			context->decode_end = CYCLE_NEVER;
+			context->regs[IFSTAT] &= ~BIT_DECI;
+			context->regs[STAT3] &= ~BIT_VALST;
+			uint16_t block_start = (context->regs[PTL] | (context->regs[PTH] << 8)) & (sizeof(context->buffer)-1);
+			for (int reg = HEAD0; reg < PTL; reg++)
+			{
+				printf("Setting HEAD%d to buffer[%X]\n", reg - HEAD0, block_start);
+				context->regs[reg] =context->buffer[block_start++];
+				block_start &= (sizeof(context->buffer)-1);
+			}
+			printf("Decode done %X:%X:%X mode %X\n", context->regs[HEAD0], context->regs[HEAD1], context->regs[HEAD2], context->regs[HEAD3]);
+		}
+		if (context->transfer_end != CYCLE_NEVER) {
+			//TODO: transfer byte to gate array destination
+			context->dac++;
+			context->regs[DBCL]--;
+			if (!context->regs[DBCL]) {
+				context->regs[DBCH]--;
+				if (!context->regs[DBCH]) {
+					context->regs[IFSTAT] &= ~BIT_DTEI;
+					if (context->cycle != context->transfer_end) {
+						printf("Expected transfer end at %u but ended at %u\n", context->transfer_end, context->cycle);
+					}
+					context->transfer_end = CYCLE_NEVER;
+				}
+			}
+		}
+	}
+}
+
+void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte)
+{
+	lc8951_run(context, cycle);
+	uint16_t current_write_addr = context->regs[WAL] | (context->regs[WAH] << 8);
+	if (sector_offset == 12) {
+		//we've recevied the sync pattern for the next block
+
+		//header/status regs no longer considered "valid"
+		context->regs[STAT3] |= BIT_VALST;
+		if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN|BIT_WRRQ)) {
+			uint16_t block_start = current_write_addr - 2352;
+			context->regs[PTL] = block_start;
+			context->regs[PTH] = block_start >> 8;
+			printf("Decoding block starting at %X\n", block_start);
+			context->ptl_internal = block_start & (sizeof(context->buffer)-1);
+			context->decode_end = context->cycle + 2352 * context->clock_step * 4;
+		}
+	}
+	if (sector_offset >= 12 && sector_offset < 16) {
+		//TODO: Handle SHDREN = 1
+		if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN)) {
+			//monitor only mode
+			context->regs[HEAD0 + sector_offset - 12] = byte;
+		}
+	}
+	if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN|BIT_WRRQ)) {
+		printf("lc8951_write_byte: %X - [%X] = %X\n", sector_offset, current_write_addr, byte);
+		context->buffer[current_write_addr & (sizeof(context->buffer)-1)] = byte;
+		context->regs[WAL]++;
+		if (!context->regs[WAL]) {
+			context->regs[WAH]++;
+		}
+	}
+}
+
+uint32_t lc8951_next_interrupt(lc8951 *context)
+{
+	if ((!context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) {
+		//interrupt already pending
+		return context->cycle;
+	}
+	uint32_t deci_cycle = CYCLE_NEVER;
+	if (context->ifctrl & BIT_DECI) {
+		deci_cycle = context->decode_end;
+	}
+	uint32_t dtei_cycle = CYCLE_NEVER;
+	if (context->ifctrl & BIT_DTEI) {
+		dtei_cycle = context->transfer_end;
+	}
+	return deci_cycle < dtei_cycle ? deci_cycle : dtei_cycle;
+}