# HG changeset patch # User Michael Pavone # Date 1643793007 28800 # Node ID bafb757e1cd2929ca45954787ed60ad056dcb8c2 # Parent 5a2b759f6b2df0b001004e456fd7141936ff154c Implement CD audio diff -r 5a2b759f6b2d -r bafb757e1cd2 Makefile --- 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 diff -r 5a2b759f6b2d -r bafb757e1cd2 cdd_fader.c --- /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 + +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; + } + } + } + } +} diff -r 5a2b759f6b2d -r bafb757e1cd2 cdd_fader.h --- /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_ diff -r 5a2b759f6b2d -r bafb757e1cd2 cdd_mcu.c --- 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; } diff -r 5a2b759f6b2d -r bafb757e1cd2 cdd_mcu.h --- 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); diff -r 5a2b759f6b2d -r bafb757e1cd2 cue.c --- 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) diff -r 5a2b759f6b2d -r bafb757e1cd2 genesis.c --- 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); } diff -r 5a2b759f6b2d -r bafb757e1cd2 lc8951.c --- 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; } diff -r 5a2b759f6b2d -r bafb757e1cd2 segacd.c --- 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; } diff -r 5a2b759f6b2d -r bafb757e1cd2 segacd.h --- 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 #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; diff -r 5a2b759f6b2d -r bafb757e1cd2 system.h --- 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 {