Mercurial > repos > blastem
diff segacd.c @ 2350:f8b5142c06aa
Allow 68K to return mid-instruction. Adjust how 68K interrupt ack works so int2 busy flag timing is more correct. Fix some other SCD timing issues
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 16 Oct 2023 23:30:04 -0700 |
parents | ae073c2167e2 |
children | 03e6ac327ba0 |
line wrap: on
line diff
--- a/segacd.c Fri Oct 13 22:44:36 2023 -0700 +++ b/segacd.c Mon Oct 16 23:30:04 2023 -0700 @@ -11,7 +11,11 @@ #define SCD_MCLKS 50000000 #define SCD_PERIPH_RESET_CLKS (SCD_MCLKS / 10) -#define TIMER_TICK_CLKS 1536 +#define TIMER_TICK_CLKS 1536/*1792*/ + +//TODO: do some logic analyzer captuers to get actual values +#define REFRESH_INTERVAL 259 +#define REFRESH_DELAY 2 enum { GA_SUB_CPU_CTRL, @@ -632,15 +636,13 @@ } context->target_cycle = context->sync_cycle < context->int_cycle ? context->sync_cycle : context->int_cycle; if (context->int_cycle == cdc_cycle && context->int_num == 5) { - uint32_t before = context->target_cycle - 2 * cd->cdc.clock_step; + uint32_t before = cdc_cycle - cd->m68k->options->gen.clock_divider * 158; //divs worst case if (before < context->target_cycle) { - if (before > context->current_cycle) { + while (before <= context->current_cycle) { + before += cd->cdc.clock_step; + } + if (before < context->target_cycle) { context->target_cycle = context->sync_cycle = before; - } else { - before = context->target_cycle - cd->cdc.clock_step; - if (before > context->current_cycle) { - context->target_cycle = context->sync_cycle = before; - } } } } @@ -650,6 +652,15 @@ { m68k_context *m68k = vcontext; segacd_context *cd = m68k->system; + uint32_t before_cycle = m68k->current_cycle - m68k->options->gen.clock_divider * 4; + if (before_cycle >= cd->last_refresh_cycle) { + uint32_t num_refresh = (before_cycle - cd->last_refresh_cycle) / REFRESH_INTERVAL; + uint32_t num_full = (m68k->current_cycle - cd->last_refresh_cycle) / REFRESH_INTERVAL; + cd->last_refresh_cycle = cd->last_refresh_cycle + num_full * REFRESH_INTERVAL; + m68k->current_cycle += num_refresh * REFRESH_DELAY; + } + + uint32_t reg = address >> 1; switch (reg) { @@ -674,7 +685,7 @@ if (dst == DST_SUB_CPU) { if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { cd->gate_array[GA_CDC_CTRL] &= ~BIT_DSR; - lc8951_resume_transfer(&cd->cdc, cd->cdc.cycle); + lc8951_resume_transfer(&cd->cdc); } calculate_target_cycle(cd->m68k); @@ -738,6 +749,14 @@ { m68k_context *m68k = vcontext; segacd_context *cd = m68k->system; + uint32_t before_cycle = m68k->current_cycle - m68k->options->gen.clock_divider * 4; + if (before_cycle >= cd->last_refresh_cycle) { + uint32_t num_refresh = (before_cycle - cd->last_refresh_cycle) / REFRESH_INTERVAL; + uint32_t num_full = (m68k->current_cycle - cd->last_refresh_cycle) / REFRESH_INTERVAL; + cd->last_refresh_cycle = cd->last_refresh_cycle + num_full * REFRESH_INTERVAL; + m68k->current_cycle += num_refresh * REFRESH_DELAY; + } + uint32_t reg = address >> 1; switch (reg) { @@ -831,7 +850,7 @@ lc8951_set_dma_multiple(&cd->cdc, 6); } if ((old_dest < DST_MAIN_CPU || old_dest == 6) && dest >= DST_MAIN_CPU && dest != 6) { - lc8951_resume_transfer(&cd->cdc, m68k->current_cycle); + lc8951_resume_transfer(&cd->cdc); } calculate_target_cycle(m68k); } @@ -878,6 +897,7 @@ case GA_TIMER: timers_run(cd, m68k->current_cycle); cd->gate_array[reg] = value & 0xFF; + cd->timer_value = 0; calculate_target_cycle(m68k); break; case GA_INT_MASK: @@ -1119,10 +1139,6 @@ rf5c164_run(&cd->pcm, cycle); } -//TODO: do some logic analyzer captuers to get actual values -#define REFRESH_INTERVAL 256 -#define REFRESH_DELAY 2 - static m68k_context *sync_components(m68k_context * context, uint32_t address) { segacd_context *cd = context->system; @@ -1146,7 +1162,15 @@ } cd->m68k_pc = address; } - switch (context->int_ack) + calculate_target_cycle(context); + return context; +} + +static m68k_context *int_ack(m68k_context *context) +{ + segacd_context *cd = context->system; + scd_peripherals_run(cd, context->current_cycle); + switch (context->int_pending) { case 1: cd->graphics_int_cycle = CYCLE_NEVER; @@ -1167,8 +1191,15 @@ cd->cdd.subcode_int_pending = 0; break; } - context->int_ack = 0; - calculate_target_cycle(context); + //the Sega CD responds to these exclusively with !VPA which means its a slow + //6800 operation. documentation says these can take between 10 and 19 cycles. + //actual results measurements seem to suggest it's actually between 9 and 18 + //Base 68K core has added 4 cycles for a normal int ack cycle already + //We add 5 + the current cycle count (in 68K cycles) mod 10 to simulate the + //additional variable delay from the use of the 6800 cycle + uint32_t cycle_count = context->current_cycle / context->options->gen.clock_divider; + context->current_cycle += 5 + (cycle_count % 10); + return context; } @@ -1177,7 +1208,10 @@ uint8_t m68k_run = !can_main_access_prog(cd); while (cycle > cd->m68k->current_cycle) { if (m68k_run && !cd->sub_paused_wordram) { - uint32_t start = cd->m68k->current_cycle; + uint32_t num_refresh = (cd->m68k->current_cycle - cd->last_refresh_cycle) / REFRESH_INTERVAL; + cd->last_refresh_cycle = cd->last_refresh_cycle + num_refresh * REFRESH_INTERVAL; + cd->m68k->current_cycle += num_refresh * REFRESH_DELAY; + cd->m68k->sync_cycle = cd->enter_debugger ? cd->m68k->current_cycle + 1 : cycle; if (cd->need_reset) { @@ -1237,6 +1271,7 @@ static uint16_t main_gate_read16(uint32_t address, void *vcontext) { m68k_context *m68k = vcontext; + gen_update_refresh_free_access(m68k); genesis_context *gen = m68k->system; segacd_context *cd = gen->expansion; uint32_t scd_cycle = gen_cycle_to_scd(m68k->current_cycle, gen); @@ -1270,10 +1305,7 @@ if (dst == DST_MAIN_CPU) { if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { cd->gate_array[GA_CDC_CTRL] &= ~BIT_DSR; - //Using the sub CPU's cycle count here is a bit of a hack - //needed to ensure the interrupt does not get triggered prematurely - //because the sub CPU execution granularity is too high - lc8951_resume_transfer(&cd->cdc, cd->m68k->current_cycle); + lc8951_resume_transfer(&cd->cdc); } else { printf("Read of CDC host data with DSR clear at %u\n", scd_cycle); } @@ -1328,14 +1360,28 @@ static void *main_gate_write16(uint32_t address, void *vcontext, uint16_t value) { m68k_context *m68k = vcontext; + gen_update_refresh_free_access(m68k); genesis_context *gen = m68k->system; segacd_context *cd = gen->expansion; uint32_t scd_cycle = gen_cycle_to_scd(m68k->current_cycle, gen); - scd_run(cd, scd_cycle); uint32_t reg = (address & 0x1FF) >> 1; + if (reg != GA_SUB_CPU_CTRL) { + scd_run(cd, scd_cycle); + } switch (reg) { case GA_SUB_CPU_CTRL: { + if ((value & BIT_IFL2) && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2)) { + if (cd->int2_cycle != CYCLE_NEVER) { + scd_run(cd, scd_cycle - 4 * cd->m68k->options->gen.clock_divider); + while (cd->int2_cycle != CYCLE_NEVER && cd->m68k->current_cycle < scd_cycle) { + scd_run(cd, cd->m68k->current_cycle + cd->m68k->options->gen.clock_divider); + } + } + cd->int2_cycle = scd_cycle; + + } + scd_run(cd, scd_cycle); uint8_t old_access = can_main_access_prog(cd); cd->busreq = value & BIT_SBRQ; uint8_t old_reset = cd->reset; @@ -1343,9 +1389,6 @@ if (cd->reset && !old_reset) { cd->need_reset = 1; } - if (value & BIT_IFL2) { - cd->int2_cycle = scd_cycle; - } /*cd->gate_array[reg] &= 0x7FFF; cd->gate_array[reg] |= value & 0x8000;*/ uint8_t new_access = can_main_access_prog(cd); @@ -1362,7 +1405,7 @@ dump_prog_ram(cd); uint16_t dst = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; if (dst == DST_PROG_RAM) { - lc8951_resume_transfer(&cd->cdc, cd->cdc.cycle); + lc8951_resume_transfer(&cd->cdc); } } break; @@ -1395,7 +1438,7 @@ uint16_t dst = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; if (dst == DST_WORD_RAM) { - lc8951_resume_transfer(&cd->cdc, cd->cdc.cycle); + lc8951_resume_transfer(&cd->cdc); } m68k_invalidate_code_range(m68k, cd->base + 0x200000, cd->base + 0x240000); @@ -1587,7 +1630,7 @@ sub_cpu_map[0].buffer = sub_cpu_map[1].buffer = cd->prog_ram; sub_cpu_map[4].buffer = cd->bram; m68k_options *mopts = malloc(sizeof(m68k_options)); - init_m68k_opts(mopts, sub_cpu_map, sizeof(sub_cpu_map) / sizeof(*sub_cpu_map), 4, sync_components); + init_m68k_opts(mopts, sub_cpu_map, sizeof(sub_cpu_map) / sizeof(*sub_cpu_map), 4, sync_components, int_ack); cd->m68k = init_68k_context(mopts, NULL); cd->m68k->system = cd; cd->int2_cycle = CYCLE_NEVER;