# HG changeset patch # User Michael Pavone # Date 1643507017 28800 # Node ID 02a9846668d1a2cacd3f506a191527a7b8c2c55f # Parent 91e4d2fe5cd9d62c2b6b1d62b034aa96af4ad880 Implement transfer of data from CDC to elsewhere. Other miscellaneous CDD/CDC improvements diff -r 91e4d2fe5cd9 -r 02a9846668d1 cdd_mcu.c --- a/cdd_mcu.c Fri Jan 28 22:48:06 2022 -0800 +++ b/cdd_mcu.c Sat Jan 29 17:43:37 2022 -0800 @@ -153,7 +153,7 @@ switch (context->requested_format) { case SF_ABSOLUTE: - if (context->toc_valid) { + if (context->toc_valid && context->head_pba >= LEADIN_SECTORS) { lba_to_status(context, context->head_pba - LEADIN_SECTORS); context->status_buffer.format = SF_ABSOLUTE; } else { @@ -161,7 +161,7 @@ } break; case SF_RELATIVE: - if (context->toc_valid) { + if (context->toc_valid && context->head_pba >= LEADIN_SECTORS) { uint32_t lba =context->head_pba - LEADIN_SECTORS; for (uint32_t i = 0; i < context->media->num_tracks; i++) { @@ -187,7 +187,49 @@ } } lba_to_status(context, lba); - context->status_buffer.format = SF_ABSOLUTE; + context->status_buffer.format = SF_RELATIVE; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TRACK: + if (context->toc_valid && context->head_pba >= LEADIN_SECTORS) { + uint32_t lba =context->head_pba - LEADIN_SECTORS; + uint32_t i; + for (i = 0; i < context->media->num_tracks; i++) + { + if (lba < context->media->tracks[i].end_lba) { + if (context->media->tracks[i].fake_pregap) { + if (lba > context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } else { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].fake_pregap - lba; + break; + } + } + if (lba < context->media->tracks[i].start_lba) { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].start_lba - lba; + } else { + lba -= context->media->tracks[i].start_lba; + } + break; + } else if (context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } + } + context->status_buffer.b.track.track_high = (i + 1) / 10; + context->status_buffer.b.track.track_low = (i + 1) % 10; + if (context->media->tracks[i].type == TRACK_DATA) { + context->status_buffer.b.track.control = 4; + } else { + //TODO: pre-emphasis flag + //TODO: copy permitted flag + context->status_buffer.b.track.control = 0; + } + context->status_buffer.b.track.adr = 1; + context->status_buffer.format = SF_TRACK; } else { context->status_buffer.format = SF_NOTREADY; } @@ -220,10 +262,13 @@ case SF_TOCN: if (context->toc_valid) { uint32_t lba = context->media->tracks[context->requested_track - 1].start_lba; - if (context->requested_track > 1) { - lba += context->media->tracks[1].fake_pregap; + for (uint32_t i = 0; i < context->requested_track; i++) { + lba += context->media->tracks[i].fake_pregap; } lba_to_status(context, lba); + if (context->media->tracks[context->requested_track - 1].type == TRACK_DATA) { + context->status_buffer.b.tocn.frame_low |= 0x80; + } context->status_buffer.b.tocn.track_low = context->requested_track % 10; context->status_buffer.format = SF_TOCN; } else { @@ -238,6 +283,8 @@ if (context->error_status == DS_STOP) { if (context->requested_format >= SF_TOCO && context->requested_format <= SF_TOCN) { context->status_buffer.status = DS_TOC_READ; + } else if (context->seeking) { + context->status_buffer.status = DS_SEEK; } else { context->status_buffer.status = context->status; } @@ -305,7 +352,11 @@ lba += context->cmd_buffer.b.time.sec_high * 10 + context->cmd_buffer.b.time.sec_low; lba *= 75; lba += context->cmd_buffer.b.time.frame_high * 10 + context->cmd_buffer.b.time.frame_low; - printf("READ/SEEK cmd for lba %d\n", lba); + printf("READ/SEEK cmd for lba %d, MM:SS:FF %u%u:%u%u:%u%u\n", lba, + context->cmd_buffer.b.time.min_high, context->cmd_buffer.b.time.min_low, + context->cmd_buffer.b.time.sec_high, context->cmd_buffer.b.time.sec_low, + context->cmd_buffer.b.time.frame_high, context->cmd_buffer.b.time.frame_low + ); if (lba >= context->media->tracks[0].fake_pregap + context->media->tracks[context->media->num_tracks - 1].end_lba) { context->error_status = DS_CMD_ERROR; break; diff -r 91e4d2fe5cd9 -r 02a9846668d1 cdd_mcu.h --- a/cdd_mcu.h Fri Jan 28 22:48:06 2022 -0800 +++ b/cdd_mcu.h Sat Jan 29 17:43:37 2022 -0800 @@ -69,7 +69,7 @@ uint8_t padding0; uint8_t padding1; uint8_t control; - uint8_t padding2; + uint8_t adr; uint8_t flags; } track; struct { diff -r 91e4d2fe5cd9 -r 02a9846668d1 lc8951.c --- a/lc8951.c Fri Jan 28 22:48:06 2022 -0800 +++ b/lc8951.c Sat Jan 29 17:43:37 2022 -0800 @@ -64,13 +64,17 @@ //3 cycles for memory operation //6 cycles min for DMA-mode host transfer -void lc8951_init(lc8951 *context) +void lc8951_init(lc8951 *context, lcd8951_byte_recv_fun byte_handler, void *handler_data) { //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; context->clock_step = (2 + 2) * 6; // external divider, internal divider + DMA period + context->byte_handler = byte_handler; + context->handler_data = handler_data; + context->decode_end = CYCLE_NEVER; + context->transfer_end = CYCLE_NEVER; } void lc8951_reg_write(lc8951 *context, uint8_t value) @@ -91,6 +95,7 @@ } if (!(value & BIT_DOUTEN)) { context->regs[IFSTAT] |= BIT_DTBSY; + context->transfer_end = CYCLE_NEVER; } break; case DBCL: @@ -108,10 +113,11 @@ context->dac |= value << 8; break; case DTTRG: - if (value & BIT_DOUTEN) { + if (context->ifctrl & 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; + printf("DTTRG: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end); } break; case DTACK: @@ -208,23 +214,39 @@ 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); + if (context->byte_handler(context->handler_data, context->buffer[context->dac & (sizeof(context->buffer)-1)])) { + context->dac++; + context->regs[DBCL]--; + if (context->regs[DBCL] == 0xFF) { + context->regs[DBCH]--; + if (context->regs[DBCH] == 0xFF) { + context->regs[IFSTAT] &= ~BIT_DTEI; + context->regs[IFSTAT] |= BIT_DTBSY; + 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; } - context->transfer_end = CYCLE_NEVER; } + } else { + // pause transfer + context->transfer_end = CYCLE_NEVER; } } } } +void lc8951_resume_transfer(lc8951 *context) +{ + if (context->transfer_end == CYCLE_NEVER && (context->ifctrl & BIT_DOUTEN)) { + uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); + if (transfer_size) { + context->transfer_end = context->cycle + transfer_size * context->clock_step; + printf("RESUME: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end); + } + } +} + void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte) { lc8951_run(context, cycle); @@ -251,7 +273,6 @@ } } 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]) { @@ -262,7 +283,7 @@ uint32_t lc8951_next_interrupt(lc8951 *context) { - if ((!context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) { + if ((~context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) { //interrupt already pending return context->cycle; } diff -r 91e4d2fe5cd9 -r 02a9846668d1 lc8951.h --- a/lc8951.h Fri Jan 28 22:48:06 2022 -0800 +++ b/lc8951.h Sat Jan 29 17:43:37 2022 -0800 @@ -3,35 +3,40 @@ #include +typedef uint8_t (*lcd8951_byte_recv_fun)(void *data, uint8_t byte); + typedef struct { + lcd8951_byte_recv_fun byte_handler; + void *handler_data; uint32_t cycle; uint32_t clock_step; uint32_t decode_end; uint32_t transfer_end; - uint8_t buffer[0x4000]; + uint8_t buffer[0x4000]; - uint8_t regs[16]; - uint8_t comin[8]; + 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; - uint8_t decoding; + uint8_t comin_write; + uint8_t comin_count; + uint8_t ifctrl; + uint8_t ctrl0; + uint8_t ctrl1; + uint8_t ar; + uint8_t ar_mask; + uint8_t decoding; uint16_t ptl_internal; } lc8951; -void lc8951_init(lc8951 *context); +void lc8951_init(lc8951 *context, lcd8951_byte_recv_fun byte_handler, void *handler_data); 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); void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte); uint32_t lc8951_next_interrupt(lc8951 *context); +void lc8951_resume_transfer(lc8951 *context); #endif //LC8951_H_ diff -r 91e4d2fe5cd9 -r 02a9846668d1 segacd.c --- a/segacd.c Fri Jan 28 22:48:06 2022 -0800 +++ b/segacd.c Sat Jan 29 17:43:37 2022 -0800 @@ -73,6 +73,18 @@ #define BIT_DMNA 0x0002 #define BIT_RET 0x0001 +//GA_CDC_CTRL +#define BIT_EDT 0x8000 +#define BIT_DSR 0x4000 + +enum { + DST_MAIN_CPU = 2, + DST_SUB_CPU, + DST_PCM_RAM, + DST_PROG_RAM, + DST_WORD_RAM = 7 +}; + //GA_INT_MASK #define BIT_MASK_IEN1 0x0002 #define BIT_MASK_IEN2 0x0004 @@ -383,10 +395,25 @@ case GA_MEM_MODE: return cd->gate_array[reg] & 0xFF1F; case GA_CDC_CTRL: + cdd_run(cd, m68k->current_cycle); return cd->gate_array[reg] | cd->cdc.ar; case GA_CDC_REG_DATA: cdd_run(cd, m68k->current_cycle); return lc8951_reg_read(&cd->cdc); + case GA_CDC_HOST_DATA: { + uint16_t dst = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; + if (dst == DST_SUB_CPU) { + cdd_run(cd, m68k->current_cycle); + if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { + cd->gate_array[GA_CDC_CTRL] &= ~BIT_DSR; + lc8951_resume_transfer(&cd->cdc); + } + calculate_target_cycle(cd->m68k); + return cd->gate_array[reg]; + } else { + return 0xFFFF; + } + } case GA_STOP_WATCH: case GA_TIMER: timers_run(cd, m68k->current_cycle); @@ -508,6 +535,11 @@ lc8951_reg_write(&cd->cdc, value); calculate_target_cycle(m68k); break; + case GA_CDC_DMA_ADDR: + cdd_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value; + cd->cdc_dst_low = 0; + break; case GA_STOP_WATCH: //docs say you should only write zero to reset //mcd-verificator comments suggest any value will reset @@ -621,6 +653,73 @@ return sub_gate_write16(address, vcontext, value16); } +static uint8_t handle_cdc_byte(void *vsys, uint8_t value) +{ + segacd_context *cd = vsys; + uint16_t dest = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; + uint32_t dma_addr = cd->gate_array[GA_CDC_DMA_ADDR] << 3; + dma_addr |= cd->cdc_dst_low; + switch (dest) + { + case DST_MAIN_CPU: + case DST_SUB_CPU: + if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { + //host reg is already full, pause transfer + return 0; + } + if (cd->cdc_dst_low) { + cd->gate_array[GA_CDC_HOST_DATA] &= 0xFF00; + cd->gate_array[GA_CDC_HOST_DATA] |= value; + cd->cdc_dst_low = 0; + cd->gate_array[GA_CDC_CTRL] |= BIT_DSR; + } else { + cd->gate_array[GA_CDC_HOST_DATA] &= 0xFF; + cd->gate_array[GA_CDC_HOST_DATA] |= value << 8; + cd->cdc_dst_low = 1; + return 1; + } + break; + case DST_PCM_RAM: + dma_addr &= (1 << 13) - 1; + //TODO: write to currently visible 8K bank of PCM RAM I guess? + dma_addr++; + cd->cdc_dst_low = dma_addr & 7; + cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; + break; + case DST_PROG_RAM: + ((uint8_t*)cd->prog_ram)[dma_addr ^ 1] = value; + m68k_invalidate_code_range(cd->m68k, dma_addr, dma_addr + 1); + dma_addr++; + cd->cdc_dst_low = dma_addr & 7; + cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; + break; + case DST_WORD_RAM: + if (cd->gate_array[GA_MEM_MODE] & BIT_MEM_MODE) { + //1M mode, write to bank assigned to Sub CPU + dma_addr &= (1 << 17) - 1; + ((uint8_t*)cd->m68k->mem_pointers[1])[dma_addr ^ 1] = value; + m68k_invalidate_code_range(cd->m68k, 0x0C0000 + dma_addr, 0x0C0000 + dma_addr + 1); + } else { + //2M mode, check if Sub CPU has access + if (!(cd->gate_array[GA_MEM_MODE] & BIT_RET)) { + dma_addr &= (1 << 18) - 1; + ((uint8_t*)cd->word_ram)[dma_addr ^ 1] = value; + m68k_invalidate_code_range(cd->m68k, 0x080000 + dma_addr, 0x080000 + dma_addr + 1); + } + } + dma_addr++; + cd->cdc_dst_low = dma_addr & 7; + cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; + break; + default: + printf("Invalid CDC transfer destination %d\n", dest); + } + if (cd->cdc.cycle == cd->cdc.transfer_end) { + cd->gate_array[GA_CDC_CTRL] |= BIT_EDT; + } + return 1; +} + static uint8_t can_main_access_prog(segacd_context *cd) { //TODO: use actual busack @@ -725,6 +824,19 @@ return cd->gate_array[offset] & 0xFFE7; case GA_HINT_VECTOR: return cd->rom_mut[0x72/2]; + case GA_CDC_HOST_DATA: { + uint16_t dst = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; + if (dst == DST_MAIN_CPU) { + if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { + cd->gate_array[GA_CDC_CTRL] &= ~BIT_DSR; + lc8951_resume_transfer(&cd->cdc); + } + calculate_target_cycle(cd->m68k); + return cd->gate_array[offset]; + } else { + return 0xFFFF; + } + } case GA_CDC_DMA_ADDR: //TODO: open bus maybe? return 0xFFFF; @@ -927,7 +1039,7 @@ cd->memptr_start_index = 0; cd->gate_array[1] = 1; cd->gate_array[0x1B] = 0x100; - lc8951_init(&cd->cdc); + lc8951_init(&cd->cdc, handle_cdc_byte, cd); if (media->chain && media->type != MEDIA_CDROM) { media = media->chain; } diff -r 91e4d2fe5cd9 -r 02a9846668d1 segacd.h --- a/segacd.h Fri Jan 28 22:48:06 2022 -0800 +++ b/segacd.h Sat Jan 29 17:43:37 2022 -0800 @@ -29,6 +29,7 @@ uint8_t memptr_start_index; lc8951 cdc; cdd_mcu cdd; + uint8_t cdc_dst_low; } segacd_context; segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info);