changeset 2080:bafb757e1cd2

Implement CD audio
author Michael Pavone <pavone@retrodev.com>
date Wed, 02 Feb 2022 01:10:07 -0800
parents 5a2b759f6b2d
children cfd53c94fffb
files Makefile cdd_fader.c cdd_fader.h cdd_mcu.c cdd_mcu.h cue.c genesis.c lc8951.c segacd.c segacd.h system.h
diffstat 11 files changed, 173 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Feb 01 01:14:27 2022 -0800
+++ b/Makefile	Wed Feb 02 01:10:07 2022 -0800
@@ -215,12 +215,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 lc8951.o cue.o cdd_mcu.o cd_graphics.o
+	segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o cdd_fader.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 $(LIBZOBJS) \
-	segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o
+	segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o cdd_fader.o
 
 ifdef NONUKLEAR
 CFLAGS+= -DDISABLE_NUKLEAR
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdd_fader.c	Wed Feb 02 01:10:07 2022 -0800
@@ -0,0 +1,65 @@
+#include "cdd_fader.h"
+#include <stdio.h>
+
+void cdd_fader_init(cdd_fader *fader)
+{
+	fader->audio = render_audio_source(16934400, 384, 2);
+	fader->cur_attenuation = 0x4000;
+	fader->dst_attenuation = 0x4000;
+	fader->attenuation_step = 0;
+}
+void cdd_fader_attenuation_write(cdd_fader *fader, uint16_t attenuation)
+{
+	fader->dst_attenuation = attenuation & 0xFFF0;
+	fader->flags = attenuation & 0xE;
+	if (fader->dst_attenuation > fader->cur_attenuation) {
+		fader->attenuation_step = (fader->dst_attenuation - fader->cur_attenuation) >> 4;
+	} else if (fader->dst_attenuation < fader->cur_attenuation) {
+		fader->attenuation_step = (fader->cur_attenuation - fader->dst_attenuation) >> 4;
+	} else {
+		fader->attenuation_step = 0;
+	}
+}
+
+void cdd_fader_data(cdd_fader *fader, uint8_t byte)
+{
+	fader->bytes[fader->byte_counter++] = byte;
+	if (fader->byte_counter == sizeof(fader->bytes)) {
+		fader->byte_counter = 0;
+		int32_t left = (fader->bytes[1] << 8) | fader->bytes[0];
+		int32_t right = (fader->bytes[3] << 8) | fader->bytes[2];
+		if (left & 0x8000) {
+			left |= 0xFFFF0000;
+		}
+		if (right & 0x8000) {
+			right |= 0xFFFF0000;
+		}
+		if (!fader->cur_attenuation) {
+			left = right = 0;
+		} else if (fader->cur_attenuation >= 4) {
+			left *= fader->cur_attenuation & 0x7FF0;
+			right *= fader->cur_attenuation & 0x7FF0;
+			left >>= 14;
+			right >>= 14;
+		} else {
+			//TODO: FIXME
+			left = right = 0;
+		}
+		render_put_stereo_sample(fader->audio, left, right);
+		if (fader->attenuation_step) {
+			if (fader->dst_attenuation > fader->cur_attenuation) {
+				fader->cur_attenuation += fader->attenuation_step;
+				if (fader->cur_attenuation >= fader->dst_attenuation) {
+					fader->cur_attenuation = fader->dst_attenuation;
+					fader->attenuation_step = 0;
+				}
+			} else {
+				fader->cur_attenuation -= fader->attenuation_step;
+				if (fader->cur_attenuation <= fader->dst_attenuation) {
+					fader->cur_attenuation = fader->dst_attenuation;
+					fader->attenuation_step = 0;
+				}
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cdd_fader.h	Wed Feb 02 01:10:07 2022 -0800
@@ -0,0 +1,21 @@
+#ifndef CDD_FADER_H_
+#define CDD_FADER_H_
+
+#include "render_audio.h"
+
+typedef struct {
+	audio_source *audio;
+	uint16_t     cur_attenuation;
+	uint16_t     dst_attenuation;
+	uint16_t     attenuation_step;
+	uint8_t      flags;
+	uint8_t      bytes[4];
+	uint8_t      byte_counter;
+} cdd_fader;
+
+void cdd_fader_init(cdd_fader *fader);
+void cdd_fader_attenuation_write(cdd_fader *fader, uint16_t attenuation);
+void cdd_fader_data(cdd_fader *fader, uint8_t byte);
+void cdd_fader_pause(cdd_fader *fader);
+
+#endif //CDD_FADER_H_
--- a/cdd_mcu.c	Tue Feb 01 01:14:27 2022 -0800
+++ b/cdd_mcu.c	Wed Feb 02 01:10:07 2022 -0800
@@ -39,7 +39,7 @@
 	context->next_int_cycle = CYCLE_NEVER;
 	context->last_subcode_cycle = CYCLE_NEVER;
 	context->last_nibble_cycle = CYCLE_NEVER;
-	context->last_byte_cycle = CYCLE_NEVER;
+	context->last_byte_cycle = 0;
 	context->requested_format = SF_NOTREADY;
 	context->media = media;
 	context->current_status_nibble = -1;
@@ -52,6 +52,11 @@
 	GAO_CDD_STATUS,
 	GAO_CDD_CMD = GAO_CDD_STATUS+5
 };
+//GAO_CDD_CTRL
+#define BIT_MUTE 0x100
+#define BIT_HOCK 0x0004
+#define BIT_DRS  0x0002
+#define BIT_DTS  0x0001
 
 static uint8_t checksum(uint8_t *vbuffer)
 {
@@ -105,15 +110,19 @@
 	context->status_buffer.b.time.frame_low = frames % 10;
 }
 
-static void update_status(cdd_mcu *context)
+static void update_status(cdd_mcu *context, uint16_t *gate_array)
 {
+	gate_array[GAO_CDD_CTRL] |= BIT_MUTE;
 	switch (context->status)
 	{
 	case DS_PLAY:
 		handle_seek(context);
 		if (!context->seeking) {
 			context->head_pba++;
-			context->media->seek(context->media, context->head_pba - LEADIN_SECTORS);
+			uint8_t track = context->media->seek(context->media, context->head_pba - LEADIN_SECTORS);
+			if (context->media->tracks[track].type == TRACK_AUDIO) {
+				gate_array[GAO_CDD_CTRL] &= ~BIT_MUTE;
+			}
 		}
 		break;
 	case DS_PAUSE:
@@ -287,7 +296,7 @@
 			context->error_status = DS_STOP;
 		}
 		if (context->requested_format != SF_TOCN) {
-			context->status_buffer.b.time.flags = 1; //TODO: populate these
+			context->status_buffer.b.time.flags = !!(gate_array[GAO_CDD_CTRL] & BIT_MUTE); //TODO: populate these
 		}
 	} else {
 		// Did not receive our first command so just send zeroes
@@ -432,34 +441,37 @@
 	}
 }
 
-#define BIT_HOCK 0x4
-#define BIT_DRS  0x2
-#define BIT_DTS  0x1
-
-void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951* cdc)
+void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951* cdc, cdd_fader* fader)
 {
 	uint32_t cd_cycle = mclks_to_cd_block(cycle);
+	uint32_t next_byte = context->last_byte_cycle + BYTE_CLOCKS;
 	if (!(gate_array[GAO_CDD_CTRL] & BIT_HOCK)) {
 		//it's a little unclear if this gates the actual cd block clock or just handshaking
 		//assum it's actually the clock for now
-		context->cycle = cd_cycle;
+		for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) {
+			if (context->cycle >= next_byte) {
+				cdd_fader_data(fader, 0);
+				next_byte = context->cycle + BYTE_CLOCKS;
+				context->last_byte_cycle = context->cycle;
+			}
+		}
+		gate_array[GAO_CDD_CTRL] |= BIT_MUTE;
 		return;
 	}
 	uint32_t next_subcode = context->last_subcode_cycle + SECTOR_CLOCKS;
 	uint32_t next_nibble = context->current_status_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER;
 	uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER;
-	uint32_t next_byte = context->current_sector_byte >= 0 ? context->last_byte_cycle + BYTE_CLOCKS : CYCLE_NEVER;
+
 	for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER)
 	{
 		if (context->cycle >= next_subcode) {
 			context->last_subcode_cycle = context->cycle;
 			next_subcode = context->cycle + SECTOR_CLOCKS;
-			update_status(context);
+			update_status(context, gate_array);
 			next_nibble = context->cycle;
 			context->current_status_nibble = 0;
 			gate_array[GAO_CDD_STATUS] |= BIT_DRS;
 			if (context->status == DS_PLAY && !context->seeking) {
-				next_byte = context->cycle;
 				context->current_sector_byte = 0;
 			}
 		}
@@ -508,15 +520,18 @@
 			}
 		}
 		if (context->cycle >= next_byte) {
-			uint8_t byte = context->media->read(context->media, context->current_sector_byte);
-			lc8951_write_byte(cdc, cd_block_to_mclks(context->cycle), context->current_sector_byte++, byte);
+			if (context->current_sector_byte >= 0) {
+				uint8_t byte = context->media->read(context->media, context->current_sector_byte);
+				lc8951_write_byte(cdc, cd_block_to_mclks(context->cycle), context->current_sector_byte++, byte);
+				cdd_fader_data(fader, gate_array[GAO_CDD_CTRL] & BIT_MUTE ? 0 : byte);
+			} else {
+				cdd_fader_data(fader, 0);
+			}
 			context->last_byte_cycle = context->cycle;
 			if (context->current_sector_byte == 2352) {
 				context->current_sector_byte = -1;
-				next_byte = CYCLE_NEVER;
-			} else {
-				next_byte = context->cycle + BYTE_CLOCKS;
 			}
+			next_byte = context->cycle + BYTE_CLOCKS;
 		}
 	}
 }
@@ -544,7 +559,6 @@
 	context->last_subcode_cycle = CYCLE_NEVER;
 	context->next_int_cycle = CYCLE_NEVER;
 	context->last_nibble_cycle = CYCLE_NEVER;
-	context->last_byte_cycle = CYCLE_NEVER;
 	context->current_status_nibble = -1;
 	context->current_cmd_nibble = -1;
 }
--- a/cdd_mcu.h	Tue Feb 01 01:14:27 2022 -0800
+++ b/cdd_mcu.h	Wed Feb 02 01:10:07 2022 -0800
@@ -2,6 +2,7 @@
 #define CDD_MCU_H_
 #include "system.h"
 #include "lc8951.h"
+#include "cdd_fader.h"
 
 typedef enum {
 	SF_ABSOLUTE,
@@ -159,7 +160,7 @@
 } cdd_mcu;
 
 void cdd_mcu_init(cdd_mcu *context, system_media *media);
-void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951* cdc);
+void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951 *cdc, cdd_fader *fader);
 void cdd_hock_enabled(cdd_mcu *context);
 void cdd_hock_disabled(cdd_mcu *context);
 void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array);
--- a/cue.c	Tue Feb 01 01:14:27 2022 -0800
+++ b/cue.c	Wed Feb 02 01:10:07 2022 -0800
@@ -43,22 +43,28 @@
 
 }
 
-static void bin_seek(system_media *media, uint32_t sector)
+enum {
+	FAKE_DATA = 1,
+	FAKE_AUDIO,
+};
+
+static uint8_t bin_seek(system_media *media, uint32_t sector)
 {
 	media->cur_sector = sector;
 	uint32_t lba = sector;
-	for (uint32_t i = 0; i < media->num_tracks; i++)
+	uint8_t track;
+	for (track = 0; track < media->num_tracks; track++)
 	{
-		if (lba < media->tracks[i].fake_pregap) {
-			media->in_fake_pregap = 1;
+		if (lba < media->tracks[track].fake_pregap) {
+			media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO;
 			break;
 		}
-		lba -= media->tracks[i].fake_pregap;
-		if (lba < media->tracks[i].start_lba) {
-			media->in_fake_pregap = 1;
+		lba -= media->tracks[track].fake_pregap;
+		if (lba < media->tracks[track].start_lba) {
+			media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO;
 			break;
 		}
-		if (lba < media->tracks[i].end_lba) {
+		if (lba < media->tracks[track].end_lba) {
 			media->in_fake_pregap = 0;
 			break;
 		}
@@ -66,6 +72,7 @@
 	if (!media->in_fake_pregap) {
 		fseek(media->f, lba * 2352, SEEK_SET);
 	}
+	return track;
 }
 
 static uint8_t fake_read(uint32_t sector, uint32_t offset)
@@ -91,22 +98,25 @@
 
 static uint8_t bin_read(system_media *media, uint32_t offset)
 {
-	if (media->in_fake_pregap) {
+	if (media->in_fake_pregap == FAKE_DATA) {
 		return fake_read(media->cur_sector, offset);
+	} else if (media->in_fake_pregap == FAKE_AUDIO) {
+		return 0;
 	} else {
 		return fgetc(media->f);
 	}
 }
 
-static void iso_seek(system_media *media, uint32_t sector)
+static uint8_t iso_seek(system_media *media, uint32_t sector)
 {
 	media->cur_sector = sector;
 	if (sector < (2 * 75)) {
-		media->in_fake_pregap = 1;
+		media->in_fake_pregap = FAKE_DATA;
 	} else {
 		media->in_fake_pregap = 0;
 		fseek(media->f, (sector -  2 * 75) * 2048, SEEK_SET);
 	}
+	return 0;
 }
 
 static uint8_t iso_read(system_media *media, uint32_t offset)
--- a/genesis.c	Tue Feb 01 01:14:27 2022 -0800
+++ b/genesis.c	Wed Feb 02 01:10:07 2022 -0800
@@ -402,9 +402,15 @@
 		psg_run(gen->psg, cur_target);
 		//printf("Running YM-2612 to cycle %d\n", cur_target);
 		ym_run(gen->ym, cur_target);
+		if (gen->expansion) {
+			scd_run(gen->expansion, gen_cycle_to_scd(cur_target, gen));
+		}
 	}
 	psg_run(gen->psg, target);
 	ym_run(gen->ym, target);
+	if (gen->expansion) {
+		scd_run(gen->expansion, gen_cycle_to_scd(target, gen));
+	}
 
 	//printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2);
 }
--- a/lc8951.c	Tue Feb 01 01:14:27 2022 -0800
+++ b/lc8951.c	Wed Feb 02 01:10:07 2022 -0800
@@ -149,6 +149,9 @@
 	case RESET:
 		context->comin_count = 0;
 		context->regs[IFSTAT] = 0xFF;
+		context->ifctrl = 0;
+		context->ctrl0 = 0;
+		context->ctrl1 = 0;
 		break;
 	default:
 		break;
@@ -277,7 +280,7 @@
 				context->regs[PTL] = block_start;
 				context->regs[PTH] = block_start >> 8;
 			}
-			printf("Decoding block starting at %X\n", context->regs[PTL] | (context->regs[PTH] << 8));
+			printf("Decoding block starting at %X (WRRQ: %d)\n", context->regs[PTL] | (context->regs[PTH] << 8), !!(context->ctrl0 & BIT_WRRQ));
 			//TODO: Datasheet has some hints about how long decoding takes in the form of how long DECI is asserted
 			context->decode_end = context->cycle + 2352 * context->clock_step * 4;
 		}
--- a/segacd.c	Tue Feb 01 01:14:27 2022 -0800
+++ b/segacd.c	Wed Feb 02 01:10:07 2022 -0800
@@ -77,6 +77,9 @@
 #define BIT_EDT        0x8000
 #define BIT_DSR        0x4000
 
+//GA_CDD_CTRL
+#define BIT_MUTE       0x0100
+
 enum {
 	DST_MAIN_CPU = 2,
 	DST_SUB_CPU,
@@ -306,7 +309,7 @@
 
 static void cdd_run(segacd_context *cd, uint32_t cycle)
 {
-	cdd_mcu_run(&cd->cdd, cycle, cd->gate_array + GA_CDD_CTRL, &cd->cdc);
+	cdd_mcu_run(&cd->cdd, cycle, cd->gate_array + GA_CDD_CTRL, &cd->cdc, &cd->fader);
 	lc8951_run(&cd->cdc, cycle);
 }
 
@@ -594,6 +597,13 @@
 		cd->gate_array[reg] = value & (BIT_MASK_IEN6|BIT_MASK_IEN5|BIT_MASK_IEN4|BIT_MASK_IEN3|BIT_MASK_IEN2|BIT_MASK_IEN1);
 		calculate_target_cycle(m68k);
 		break;
+	case GA_CDD_FADER:
+		cdd_run(cd, m68k->current_cycle);
+		value &= 0x7FFF;
+		cdd_fader_attenuation_write(&cd->fader, value);
+		cd->gate_array[reg] &= 0x8000;
+		cd->gate_array[reg] |= value;
+		break;
 	case GA_CDD_CTRL: {
 		cdd_run(cd, m68k->current_cycle);
 		uint16_t changed = cd->gate_array[reg] ^ value;
@@ -604,6 +614,7 @@
 				cdd_hock_enabled(&cd->cdd);
 			} else {
 				cdd_hock_disabled(&cd->cdd);
+				cd->gate_array[reg] |= BIT_MUTE;
 			}
 			calculate_target_cycle(m68k);
 		}
@@ -802,6 +813,9 @@
 {
 	segacd_context *cd = context->system;
 	scd_peripherals_run(cd, context->current_cycle);
+	if (context->int_ack) {
+		printf("int ack %d\n", context->int_ack);
+	}
 	switch (context->int_ack)
 	{
 	case 1:
@@ -1153,13 +1167,14 @@
 	cd->reset = 1; //active low, so reset is not active on start
 	cd->memptr_start_index = 0;
 	cd->gate_array[1] = 1;
-	cd->gate_array[0x1B] = 0x100;
+	cd->gate_array[GA_CDD_CTRL] = BIT_MUTE; //Data/mute flag is set on start
 	lc8951_init(&cd->cdc, handle_cdc_byte, cd);
 	if (media->chain && media->type != MEDIA_CDROM) {
 		media = media->chain;
 	}
 	cdd_mcu_init(&cd->cdd, media);
 	cd_graphics_init(cd);
+	cdd_fader_init(&cd->fader);
 
 	return cd;
 }
--- a/segacd.h	Tue Feb 01 01:14:27 2022 -0800
+++ b/segacd.h	Wed Feb 02 01:10:07 2022 -0800
@@ -2,7 +2,6 @@
 #define SEGACD_H_
 #include <stdint.h>
 #include "genesis.h"
-#include "lc8951.h"
 #include "cdd_mcu.h"
 
 typedef struct {
@@ -37,6 +36,7 @@
 	uint8_t         memptr_start_index;
 	lc8951          cdc;
 	cdd_mcu         cdd;
+	cdd_fader       fader;
 	uint8_t         cdc_dst_low;
 	uint8_t         cdc_int_ack;
 	uint8_t         graphics_step;
--- a/system.h	Tue Feb 01 01:14:27 2022 -0800
+++ b/system.h	Wed Feb 02 01:10:07 2022 -0800
@@ -98,7 +98,7 @@
 	track_type type;
 } track_info;
 
-typedef void (*seek_fun)(system_media *media, uint32_t sector);
+typedef uint8_t (*seek_fun)(system_media *media, uint32_t sector);
 typedef uint8_t (*read_fun)(system_media *media, uint32_t offset);
 
 struct system_media {