changeset 2058:70260f6051dd segacd

Initial work on CDC emulation
author Michael Pavone <pavone@retrodev.com>
date Fri, 21 Jan 2022 20:24:48 -0800
parents 88deea42caf0
children 6399a776e981
files Makefile lc8951.c lc8951.h segacd.c segacd.h
diffstat 5 files changed, 225 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Jan 20 00:56:46 2022 -0800
+++ b/Makefile	Fri Jan 21 20:24:48 2022 -0800
@@ -181,7 +181,7 @@
 M68KOBJS=68kinst.o
 
 ifdef NEW_CORE
-Z80OBJS=z80.o z80inst.o 
+Z80OBJS=z80.o z80inst.o
 M68KOBJS+= m68k.o
 CFLAGS+= -DNEW_CORE
 else
@@ -197,7 +197,7 @@
 endif
 endif
 AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o
-CONFIGOBJS=config.o tern.o util.o paths.o 
+CONFIGOBJS=config.o tern.o util.o paths.o
 NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o
 RENDEROBJS=ppm.o controller_info.o
 ifdef USE_FBDEV
@@ -205,7 +205,7 @@
 else
 RENDEROBJS+= render_sdl.o
 endif
-	
+
 ifdef NOZLIB
 CFLAGS+= -DDISABLE_ZLIB
 else
@@ -214,12 +214,12 @@
 
 MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \
 	realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \
-	$(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o segacd.o
+	$(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o segacd.o lc8951.o
 
 LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \
 	i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \
-	$(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o segacd.o $(LIBZOBJS)
-	
+	$(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o segacd.o lc8951.o $(LIBZOBJS)
+
 ifdef NONUKLEAR
 CFLAGS+= -DDISABLE_NUKLEAR
 else
@@ -278,7 +278,7 @@
 blastem$(EXE) : $(MAINOBJS)
 	$(CC) -o $@ $^ $(LDFLAGS) $(PROFFLAGS)
 	$(FIXUP) ./$@
-	
+
 blastjag$(EXE) : jaguar.o jag_video.o $(RENDEROBJS) serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS)
 	$(CC) -o $@ $^ $(LDFLAGS)
 
@@ -287,7 +287,7 @@
 
 dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o
 	$(CC) -o $@ $^ $(OPT)
-	
+
 jagdis : jagdis.o jagcpu.o tern.o
 	$(CC) -o $@ $^
 
@@ -327,7 +327,7 @@
 
 test_arm : test_arm.o gen_arm.o mem.o gen.o
 	$(CC) -o test_arm test_arm.o gen_arm.o mem.o gen.o
-	
+
 test_int_timing : test_int_timing.o vdp.o
 	$(CC) -o $@ $^
 
@@ -354,7 +354,7 @@
 
 %.o : %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
-  
+
 %.o : %.m
 	$(CC) $(CFLAGS) -c -o $@ $<
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lc8951.c	Fri Jan 21 20:24:48 2022 -0800
@@ -0,0 +1,163 @@
+#include "lc8951.h"
+
+enum {
+	COMIN,
+	IFSTAT,
+	DBCL,
+	DBCH,
+	HEAD0,
+	HEAD1,
+	HEAD2,
+	HEAD3,
+	PTL,
+	PTH,
+	WAL,
+	WAH,
+	STAT0,
+	STAT1,
+	STAT2,
+	STAT3,
+
+	SBOUT = COMIN,
+	IFCTRL = IFSTAT,
+	DACL = HEAD0,
+	DACH = HEAD1,
+	DTTRG = HEAD2,
+	DTACK = HEAD3,
+	WAL_WRITE = PTL,
+	WAH_WRITE = PTH,
+	CTRL0 = WAL,
+	CTRL1 = WAH,
+	PTL_WRITE = STAT0,
+	PTH_WRITE = STAT1,
+	RESET = STAT3
+};
+
+//IFCTRL
+#define BIT_CMDIEN 0x80
+#define BIT_DTEIEN 0x40
+#define BIT_CECIEN 0x20
+#define BIT_CMDBK  0x10
+#define BIT_DTWAI  0x08
+#define BIT_STWAI  0x04
+#define BIT_DOUTEN 0x02
+#define BIT_SOUTEN 0x01
+
+//IFSTAT
+#define BIT_CMDI   0x80
+#define BIT_DTEI   0x40
+#define BIT_DECI   0x20
+#define BIT_DTBSY  0x08
+#define BIT_STBSY  0x04
+#define BIT_DTEN   0x02
+#define BIT_STEN   0x01
+
+//datasheet timing info
+//3 cycles for memory operation
+//6 cycles min for DMA-mode host transfer
+
+void lc8951_init(lc8951 *context)
+{
+	//This seems to vary somewhat between Sega CD models
+	//unclear if the difference is in the lc8951 or gate array
+	context->regs[IFSTAT] = 0xFF;
+	context->ar_mask = 0x1F;
+}
+
+void lc8951_reg_write(lc8951 *context, uint8_t value)
+{
+	switch (context->ar)
+	{
+	case SBOUT:
+		context->regs[context->ar] = value;
+		if (context->ifctrl & BIT_SOUTEN) {
+			context->regs[IFSTAT] &= ~BIT_STBSY;
+		}
+		break;
+	case IFCTRL:
+		context->ifctrl = value;
+		if (!(value & BIT_SOUTEN)) {
+			context->regs[IFSTAT] |= BIT_STBSY;
+		}
+		if (!(value & BIT_DOUTEN)) {
+			context->regs[IFSTAT] |= BIT_DTBSY;
+		}
+		break;
+	case DBCL:
+		context->regs[context->ar] = value;
+		break;
+	case DBCH:
+		context->regs[context->ar] = value & 0xF;
+		break;
+	case DACL:
+		context->dac &= 0xFF00;
+		context->dac |= value;
+		break;
+	case DACH:
+		context->dac &= 0xFF;
+		context->dac |= value << 8;
+		break;
+	case DTTRG:
+		if (value & BIT_DOUTEN) {
+			context->regs[IFSTAT] &= ~BIT_DTBSY;
+		}
+		break;
+	case DTACK:
+		context->regs[IFSTAT] |= BIT_DTEI;
+		break;
+	case WAL_WRITE:
+		context->regs[WAL] = value;
+		break;
+	case WAH_WRITE:
+		context->regs[WAH] = value;
+		break;
+	case PTL_WRITE:
+		context->regs[PTL] = value;
+		break;
+	case PTH_WRITE:
+		context->regs[PTH] = value;
+		break;
+	case RESET:
+		context->comin_count = 0;
+		context->regs[IFSTAT] = 0xFF;
+		break;
+	default:
+		break;
+	}
+	if (context->ar != SBOUT) {
+		context->ar++;
+		context->ar &= context->ar_mask;
+	}
+}
+
+uint8_t lc8951_reg_read(lc8951 *context)
+{
+	uint8_t value;
+	if (context->ar == COMIN) {
+		if (!context->comin_count) {
+			return 0xFF;
+		}
+		value = context->comin[(context->comin_write - context->comin_count)&sizeof(context->comin)];
+		context->comin_count--;
+		if (!context->comin_count) {
+			context->regs[IFSTAT] |= BIT_CMDI;
+		}
+		return value;
+	}
+	if (context->ar == STAT3) {
+		context->regs[IFSTAT] |= BIT_DECI;
+	}
+	if (context->ar >= sizeof(context->regs)) {
+		value = 0xFF;
+	} else {
+		value = context->regs[context->ar];
+	}
+	context->ar++;
+	context->ar &= context->ar_mask;
+	return value;
+}
+
+void lc8951_ar_write(lc8951 *context, uint8_t value)
+{
+	context->ar = value & context->ar_mask;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lc8951.h	Fri Jan 21 20:24:48 2022 -0800
@@ -0,0 +1,30 @@
+#ifndef LC8951_H_
+#define LC8951_H_
+
+#include <stdint.h>
+
+typedef struct {
+	uint32_t cycles;
+
+	uint8_t buffer[0x4000];
+
+	uint8_t regs[16];
+	uint8_t comin[8];
+
+	uint16_t dac;
+	uint8_t comin_write;
+	uint8_t comin_count;
+	uint8_t ifctrl;
+	uint8_t ctrl0;
+	uint8_t ctrl1;
+	uint8_t ar;
+	uint8_t ar_mask;
+} lc8951;
+
+void lc8951_init(lc8951 *context);
+//void lc8951_run(lc8951 *context, uint32_t cycle);
+void lc8951_reg_write(lc8951 *context, uint8_t value);
+uint8_t lc8951_reg_read(lc8951 *context);
+void lc8951_ar_write(lc8951 *context, uint8_t value);
+
+#endif //LC8951_H_
--- a/segacd.c	Thu Jan 20 00:56:46 2022 -0800
+++ b/segacd.c	Fri Jan 21 20:24:48 2022 -0800
@@ -355,6 +355,10 @@
 	}
 	case GA_MEM_MODE:
 		return cd->gate_array[reg] & 0xFF1F;
+	case GA_CDC_CTRL:
+		return cd->gate_array[reg] | cd->cdc.ar;
+	case GA_CDC_REG_DATA:
+		return lc8951_reg_read(&cd->cdc);
 	case GA_STOP_WATCH:
 	case GA_TIMER:
 		timers_run(cd, m68k->current_cycle);
@@ -457,6 +461,14 @@
 		cd->gate_array[reg] |= value & (BIT_RET|BIT_MEM_MODE|MASK_PRIORITY);
 		break;
 	}
+	case GA_CDC_CTRL:
+		lc8951_ar_write(&cd->cdc, value);
+		cd->gate_array[reg] &= 0xC000;
+		cd->gate_array[reg] = value & 0x0700;
+		break;
+	case GA_CDC_REG_DATA:
+		lc8951_reg_write(&cd->cdc, value);
+		break;
 	case GA_STOP_WATCH:
 		//docs say you should only write zero to reset
 		//mcd-verificator comments suggest any value will reset
@@ -517,6 +529,13 @@
 		//these registers treat all writes as word-wide
 		value16 = value | (value << 8);
 		break;
+	case GA_CDC_CTRL:
+		if (address & 1) {
+			lc8951_ar_write(&cd->cdc, value);
+		} else {
+			cd->gate_array[reg] = value << 8;
+		}
+		return vcontext;
 	default:
 		if (address & 1) {
 			value16 = cd->gate_array[reg] & 0xFF00 | value;
@@ -829,6 +848,7 @@
 	cd->memptr_start_index = 0;
 	cd->gate_array[1] = 1;
 	cd->gate_array[0x1B] = 0x100;
+	lc8951_init(&cd->cdc);
 
 	return cd;
 }
--- a/segacd.h	Thu Jan 20 00:56:46 2022 -0800
+++ b/segacd.h	Fri Jan 21 20:24:48 2022 -0800
@@ -2,6 +2,7 @@
 #define SEGACD_H_
 #include <stdint.h>
 #include "genesis.h"
+#include "lc8951.h"
 
 typedef struct {
 	m68k_context    *m68k;
@@ -25,6 +26,7 @@
 	uint8_t         reset;
 	uint8_t         need_reset;
 	uint8_t         memptr_start_index;
+	lc8951          cdc;
 } segacd_context;
 
 segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info);