# HG changeset patch # User Michael Pavone # Date 1646633032 28800 # Node ID cd057d6fe030b845f8c8fee58c1fb69eb744b624 # Parent e93ced356a2135586cd49a4688d789ce7be71fbf Initial stab at subcode emulation diff -r e93ced356a21 -r cd057d6fe030 cdd_mcu.c --- a/cdd_mcu.c Sat Mar 05 14:17:59 2022 -0800 +++ b/cdd_mcu.c Sun Mar 06 22:03:52 2022 -0800 @@ -7,6 +7,7 @@ #define SECTOR_CLOCKS (CD_BLOCK_CLKS/75) #define NIBBLE_CLOCKS (CDD_MCU_DIVIDER * 77) #define BYTE_CLOCKS (SECTOR_CLOCKS/2352) // 96 +#define SUBCODE_CLOCKS (SECTOR_CLOCKS/98) #define PROCESSING_DELAY 54000 //approximate, based on Wondermega M1 measurements //lead in start max diameter 46 mm @@ -38,20 +39,26 @@ void cdd_mcu_init(cdd_mcu *context, system_media *media) { context->next_int_cycle = CYCLE_NEVER; - context->last_subcode_cycle = CYCLE_NEVER; + context->next_subcode_int_cycle = CYCLE_NEVER; + context->last_sector_cycle = CYCLE_NEVER; context->last_nibble_cycle = CYCLE_NEVER; context->next_byte_cycle = 0; + context->next_subcode_cycle = CYCLE_NEVER; context->requested_format = SF_NOTREADY; context->media = media; context->current_status_nibble = -1; context->current_cmd_nibble = -1; context->current_sector_byte = -1; + context->current_subcode_byte = -1; + context->current_subcode_dest = 0; } enum { GAO_CDD_CTRL, GAO_CDD_STATUS, - GAO_CDD_CMD = GAO_CDD_STATUS+5 + GAO_CDD_CMD = GAO_CDD_STATUS+5, + GAO_SUBCODE_ADDR = (0x68-0x36)/2, + GAO_SUBCODE_START = (0x100-0x36)/2 }; //GAO_CDD_CTRL #define BIT_MUTE 0x100 @@ -376,7 +383,7 @@ context->error_status = DS_CMD_ERROR; break; } - if (context->requested_format == SF_TOCT || context->requested_format == SF_TOCN) { + if (context->requested_format == SF_TOCT || context->requested_format == SF_TOCN || context->requested_format == SF_TOCO) { context->requested_format = SF_ABSOLUTE; } if (!context->toc_valid) { @@ -541,12 +548,12 @@ gate_array[GAO_CDD_CTRL] |= BIT_MUTE; return; } - uint32_t next_subcode = context->last_subcode_cycle + SECTOR_CLOCKS; + uint32_t next_subcode = context->last_sector_cycle + SECTOR_CLOCKS; uint32_t next_nibble; if (context->current_status_nibble > 0) { next_nibble = context->last_nibble_cycle + NIBBLE_CLOCKS; } else if (!context->current_status_nibble) { - next_nibble = context->last_subcode_cycle + PROCESSING_DELAY; + next_nibble = context->last_sector_cycle + PROCESSING_DELAY; } else { next_nibble = CYCLE_NEVER; } @@ -555,14 +562,22 @@ for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) { if (context->cycle >= next_subcode) { - context->last_subcode_cycle = context->cycle; + context->last_sector_cycle = context->cycle; next_subcode = context->cycle + SECTOR_CLOCKS; update_status(context, gate_array); next_nibble = context->cycle + PROCESSING_DELAY; context->current_status_nibble = 0; gate_array[GAO_CDD_STATUS] |= BIT_DRS; + if (context->next_subcode_int_cycle != CYCLE_NEVER) { + context->subcode_int_pending = 1; + } if ((context->status == DS_PLAY || context->status == DS_PAUSE) && context->head_pba >= LEADIN_SECTORS) { context->current_sector_byte = 0; + context->current_subcode_byte = 0; + context->next_subcode_cycle = context->cycle; + context->next_subcode_int_cycle = cd_block_to_mclks(next_subcode); + } else { + context->next_subcode_int_cycle = CYCLE_NEVER; } } if (context->cycle >= next_nibble) { @@ -618,7 +633,7 @@ cdd_fader_data(fader, 0); if (context->current_sector_byte >= 0) { next_subcode += BYTE_CLOCKS; - context->last_subcode_cycle += BYTE_CLOCKS; + context->last_sector_cycle += BYTE_CLOCKS; } } if (context->current_sector_byte == 2352) { @@ -626,6 +641,36 @@ } context->next_byte_cycle += BYTE_CLOCKS; } + if (context->cycle >= context->next_subcode_cycle) { + uint8_t byte; + if (!context->current_subcode_byte) { + byte = 0x9F; + //This probably happens after the second sync symbol, but doing it here simplifies things a little + context->current_subcode_dest &= 0x7E; + gate_array[GAO_SUBCODE_ADDR] = (context->current_subcode_dest - 96) & 0x7E; + } else if (context->current_subcode_byte == 1) { + byte = 0xFD; + } else { + byte = context->media->read_subcodes(context->media, context->current_subcode_byte - 2); + } + int offset = GAO_SUBCODE_START + (context->current_subcode_dest >> 1); + if (context->current_subcode_dest & 1) { + gate_array[offset] &= 0xFF00; + gate_array[offset] |= byte; + } else { + gate_array[offset] &= 0x00FF; + gate_array[offset] |= byte << 8; + } + context->current_subcode_byte++; + if (context->current_subcode_byte == 98) { + context->current_subcode_byte = 0; + } else if (context->current_subcode_byte == 32) { + gate_array[GAO_SUBCODE_ADDR] |= 0x80; + } + context->current_subcode_dest++; + context->current_subcode_dest &= 0x7F; + context->next_subcode_cycle += SUBCODE_CLOCKS; + } } } @@ -643,13 +688,13 @@ void cdd_hock_enabled(cdd_mcu *context) { - context->last_subcode_cycle = context->cycle; + context->last_sector_cycle = context->cycle; context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS + PROCESSING_DELAY + 7 * NIBBLE_CLOCKS); } void cdd_hock_disabled(cdd_mcu *context) { - context->last_subcode_cycle = CYCLE_NEVER; + context->last_sector_cycle = CYCLE_NEVER; context->next_int_cycle = CYCLE_NEVER; context->last_nibble_cycle = CYCLE_NEVER; context->current_status_nibble = -1; @@ -667,11 +712,11 @@ if (context->next_int_cycle != CYCLE_NEVER) { context->next_int_cycle -= deduction; } - if (context->last_subcode_cycle != CYCLE_NEVER) { - if (context->last_subcode_cycle > cd_deduction) { - context->last_subcode_cycle -= cd_deduction; + if (context->last_sector_cycle != CYCLE_NEVER) { + if (context->last_sector_cycle > cd_deduction) { + context->last_sector_cycle -= cd_deduction; } else { - context->last_subcode_cycle = 0; + context->last_sector_cycle = 0; } } if (context->last_nibble_cycle != CYCLE_NEVER) { @@ -682,4 +727,7 @@ } } context->next_byte_cycle -= cd_deduction; + if (context->next_subcode_cycle != CYCLE_NEVER) { + context->next_subcode_cycle -= cd_deduction; + } } diff -r e93ced356a21 -r cd057d6fe030 cdd_mcu.h --- a/cdd_mcu.h Sat Mar 05 14:17:59 2022 -0800 +++ b/cdd_mcu.h Sun Mar 06 22:03:52 2022 -0800 @@ -136,12 +136,16 @@ system_media *media; uint32_t cycle; //this is in CD block CLKS uint32_t next_int_cycle; //this is in SCD MCLKS - uint32_t last_subcode_cycle; + uint32_t next_subcode_int_cycle; + uint32_t last_sector_cycle; uint32_t last_nibble_cycle; uint32_t next_byte_cycle; + uint32_t next_subcode_cycle; int current_status_nibble; int current_cmd_nibble; int current_sector_byte; + int current_subcode_byte; + int current_subcode_dest; uint32_t head_pba; uint32_t seek_pba; uint32_t pause_pba; @@ -154,6 +158,7 @@ uint8_t cmd_recv_wait; uint8_t cmd_recv_pending; uint8_t int_pending; + uint8_t subcode_int_pending; uint8_t toc_valid; uint8_t first_cmd_received; uint8_t seeking; diff -r e93ced356a21 -r cd057d6fe030 cdimage.c --- a/cdimage.c Sat Mar 05 14:17:59 2022 -0800 +++ b/cdimage.c Sun Mar 06 22:03:52 2022 -0800 @@ -107,6 +107,16 @@ if (track) { lba -= media->tracks[track - 1].end_lba; } + if (media->tracks[track].has_subcodes) { + if (!media->tmp_buffer) { + media->tmp_buffer = calloc(1, 96); + } + fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); + int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); + if (bytes != 96) { + fprintf(stderr, "Only read %d subcode bytes\n", bytes); + } + } fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); } } @@ -153,6 +163,16 @@ } } +static uint8_t bin_subcode_read(system_media *media, uint32_t offset) +{ + if (media->in_fake_pregap || !media->tracks[media->cur_track].has_subcodes) { + //TODO: Fake PQ subcodes + return 0; + } + //TODO: Translate "cooked" subcodes back to raw format + return media->tmp_buffer[offset]; +} + uint8_t parse_cue(system_media *media) { char *line = media->buffer; @@ -318,10 +338,11 @@ tracks[0].fake_pregap = 2 * 75; } - fseek(tracks[0].f, tracks[0].sector_bytes == 2352 ? 16 : 0, SEEK_SET); + fseek(tracks[0].f, tracks[0].sector_bytes >= 2352 ? 16 : 0, SEEK_SET); media->size = fread(media->buffer, 1, 2048, tracks[0].f); media->seek = bin_seek; media->read = bin_read; + media->read_subcodes = bin_subcode_read; } uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; media->type = valid ? MEDIA_CDROM : MEDIA_CART; @@ -379,8 +400,10 @@ //TODO: record whether subcode is in raw format or not if (startswith(cmd, "RW_RAW")) { tracks[track].sector_bytes += 96; + tracks[track].has_subcodes = SUBCODES_RAW; } else if (startswith(cmd, "RW")) { tracks[track].sector_bytes += 96; + tracks[track].has_subcodes = SUBCODES_COOKED; } } } @@ -485,6 +508,7 @@ media->size = fread(media->buffer, 1, 2048, tracks[0].f); media->seek = bin_seek; media->read = bin_read; + media->read_subcodes = bin_subcode_read; } uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; media->type = valid ? MEDIA_CDROM : MEDIA_CART; @@ -508,11 +532,13 @@ .start_lba = 0, .end_lba = file_size(f), .sector_bytes = 2048, + .has_subcodes = SUBCODES_NONE, .need_swap = 0, .type = TRACK_DATA }; media->type = MEDIA_CDROM; media->seek = bin_seek; media->read = bin_read; + media->read_subcodes = bin_subcode_read; return media->size; } diff -r e93ced356a21 -r cd057d6fe030 segacd.c --- a/segacd.c Sat Mar 05 14:17:59 2022 -0800 +++ b/segacd.c Sun Mar 06 22:03:52 2022 -0800 @@ -55,6 +55,8 @@ GA_FONT_DATA1, GA_FONT_DATA2, GA_FONT_DATA3, + GA_SUBCODE_START = 0x80, + GA_SUBCODE_MIRROR = 0xC0, GA_HINT_VECTOR = GA_CDC_REG_DATA }; @@ -387,48 +389,57 @@ context->int_cycle = CYCLE_NEVER; uint8_t mask = context->status & 0x7; uint32_t cdc_cycle = CYCLE_NEVER; - if (mask < 5) { - if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN5) { - cdc_cycle = lc8951_next_interrupt(&cd->cdc); - //CDC interrupts only generated on falling edge of !INT signal - if (cd->cdc_int_ack) { - if (cdc_cycle > cd->cdc.cycle) { - cd->cdc_int_ack = 0; - } else { - cdc_cycle = CYCLE_NEVER; - } - } - if (cdc_cycle < context->int_cycle) { - context->int_cycle = cdc_cycle; - context->int_num = 5; + if (mask < 6) { + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN6) { + uint32_t subcode_cycle = cd->cdd.subcode_int_pending ? context->current_cycle : cd->cdd.next_subcode_int_cycle; + if (subcode_cycle != CYCLE_NEVER) { + context->int_cycle = subcode_cycle; + context->int_num = 6; } } - if (mask < 4) { - if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN4) { - uint32_t cdd_cycle = cd->cdd.int_pending ? context->current_cycle : cd->cdd.next_int_cycle; - if (cdd_cycle < context->int_cycle) { - context->int_cycle = cdd_cycle; - context->int_num = 4; + if (mask < 5) { + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN5) { + cdc_cycle = lc8951_next_interrupt(&cd->cdc); + //CDC interrupts only generated on falling edge of !INT signal + if (cd->cdc_int_ack) { + if (cdc_cycle > cd->cdc.cycle) { + cd->cdc_int_ack = 0; + } else { + cdc_cycle = CYCLE_NEVER; + } + } + if (cdc_cycle < context->int_cycle) { + context->int_cycle = cdc_cycle; + context->int_num = 5; } } - if (mask < 3) { - uint32_t next_timer; - if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN3) { - uint32_t next_timer_cycle = next_timer_int(cd); - if (next_timer_cycle < context->int_cycle) { - context->int_cycle = next_timer_cycle; - context->int_num = 3; + if (mask < 4) { + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN4) { + uint32_t cdd_cycle = cd->cdd.int_pending ? context->current_cycle : cd->cdd.next_int_cycle; + if (cdd_cycle < context->int_cycle) { + context->int_cycle = cdd_cycle; + context->int_num = 4; } } - if (mask < 2) { - if (cd->int2_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2)) { - context->int_cycle = cd->int2_cycle; - context->int_num = 2; + if (mask < 3) { + uint32_t next_timer; + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN3) { + uint32_t next_timer_cycle = next_timer_int(cd); + if (next_timer_cycle < context->int_cycle) { + context->int_cycle = next_timer_cycle; + context->int_num = 3; + } } - if (mask < 1) { - if (cd->graphics_int_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN1)) { - context->int_cycle = cd->graphics_int_cycle; - context->int_num = 1; + if (mask < 2) { + if (cd->int2_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2)) { + context->int_cycle = cd->int2_cycle; + context->int_num = 2; + } + if (mask < 1) { + if (cd->graphics_int_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN1)) { + context->int_cycle = cd->graphics_int_cycle; + context->int_num = 1; + } } } } @@ -521,6 +532,7 @@ value |= pixel << (i * 4); } return value; + } case GA_STAMP_SIZE: case GA_IMAGE_BUFFER_LINES: //these two have bits that change based on graphics operations @@ -529,8 +541,10 @@ case GA_TRACE_VECTOR_BASE: //write only return 0xFFFF; - } default: + if (reg >= GA_SUBCODE_MIRROR) { + return cd->gate_array[GA_SUBCODE_START + (reg & 0x3F)]; + } return cd->gate_array[reg]; } } @@ -655,6 +669,10 @@ calculate_target_cycle(m68k); break; case GA_INT_MASK: + if (!(cd->gate_array[reg] & BIT_MASK_IEN6)) { + //subcode interrupts can't be made pending when they are disabled in this reg + cd->cdd.subcode_int_pending = 0; + } 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; @@ -903,6 +921,9 @@ case 5: cd->cdc_int_ack = 1; break; + case 6: + cd->cdd.subcode_int_pending = 0; + break; } context->int_ack = 0; calculate_target_cycle(context); diff -r e93ced356a21 -r cd057d6fe030 segacd.h --- a/segacd.h Sat Mar 05 14:17:59 2022 -0800 +++ b/segacd.h Sun Mar 06 22:03:52 2022 -0800 @@ -9,7 +9,7 @@ m68k_context *m68k; system_media *media; genesis_context *genesis; - uint16_t gate_array[0x100]; + uint16_t gate_array[0xC0]; uint16_t *rom; //unaltered ROM, needed for mirrored locations uint16_t *rom_mut; //ROM with low 16-bit of HINT vector modified by register write uint16_t *prog_ram; diff -r e93ced356a21 -r cd057d6fe030 system.h --- a/system.h Sat Mar 05 14:17:59 2022 -0800 +++ b/system.h Sun Mar 06 22:03:52 2022 -0800 @@ -90,6 +90,12 @@ TRACK_DATA } track_type; +enum { + SUBCODES_NONE, + SUBCODES_RAW, + SUBCODES_COOKED +}; + typedef struct { FILE *f; uint32_t file_offset; @@ -99,6 +105,7 @@ uint32_t end_lba; uint16_t sector_bytes; uint8_t need_swap; + uint8_t has_subcodes; track_type type; } track_info; @@ -112,8 +119,10 @@ char *extension; system_media *chain; track_info *tracks; + uint8_t *tmp_buffer; seek_fun seek; read_fun read; + read_fun read_subcodes; uint32_t num_tracks; uint32_t cur_track; uint32_t size;