Mercurial > repos > blastem
diff genesis.c @ 2431:61c0bfe10887
Somewhat busted support for Pico ADPCM
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 06 Feb 2024 21:47:11 -0800 |
parents | da3dc881d3f0 |
children | 2907c3312423 |
line wrap: on
line diff
--- a/genesis.c Tue Feb 06 06:34:49 2024 -0800 +++ b/genesis.c Tue Feb 06 21:47:11 2024 -0800 @@ -681,6 +681,102 @@ return context; } +static void sync_sound_pico(genesis_context * gen, uint32_t target) +{ + while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) + { + uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; + psg_run(gen->psg, cur_target); + pico_pcm_run(gen->adpcm, cur_target); + } + psg_run(gen->psg, target); + pico_pcm_run(gen->adpcm, target); +} + +static void adjust_int_cycle_pico(m68k_context *context, vdp_context *v_context) +{ + genesis_context *gen = context->system; + if (context->sync_cycle - context->current_cycle > gen->max_cycles) { + context->sync_cycle = context->current_cycle + gen->max_cycles; + } + context->int_cycle = CYCLE_NEVER; + uint8_t mask = context->status & 0x7; + if (mask < 6) { + uint32_t next_vint = vdp_next_vint(v_context); + if (next_vint != CYCLE_NEVER) { + context->int_cycle = next_vint; + context->int_num = 6; + } + if (mask < 5) { + uint32_t next_hint = vdp_next_hint(v_context); + if (next_hint != CYCLE_NEVER) { + next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint; + if (next_hint < context->int_cycle) { + context->int_cycle = next_hint; + context->int_num = 5; + + } + } + if (mask < 3) { + uint32_t next_pcm_int = pico_pcm_next_int(gen->adpcm); + if (next_pcm_int != CYCLE_NEVER && next_pcm_int < context->int_cycle) { + context->int_cycle = next_pcm_int; + context->int_num = 3; + } + if (mask < 2 && (v_context->regs[REG_MODE_3] & BIT_EINT_EN) && gen->header.type == SYSTEM_GENESIS) { + uint32_t next_eint_port0 = io_next_interrupt(gen->io.ports, context->current_cycle); + uint32_t next_eint_port1 = io_next_interrupt(gen->io.ports + 1, context->current_cycle); + uint32_t next_eint_port2 = io_next_interrupt(gen->io.ports + 2, context->current_cycle); + uint32_t next_eint = next_eint_port0 < next_eint_port1 + ? (next_eint_port0 < next_eint_port2 ? next_eint_port0 : next_eint_port2) + : (next_eint_port1 < next_eint_port2 ? next_eint_port1 : next_eint_port2); + if (next_eint != CYCLE_NEVER) { + next_eint = next_eint < context->current_cycle ? context->current_cycle : next_eint; + if (next_eint < context->int_cycle) { + context->int_cycle = next_eint; + context->int_num = 2; + } + } + } + } + } + } + if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { + context->int_pending = INT_PENDING_NONE; + } + /*if (context->int_cycle != old_int_cycle) { + printf("int cycle changed to: %d, level: %d @ %d(%d), frame: %d, vcounter: %d, hslot: %d, mask: %d, hint_counter: %d\n", context->int_cycle, context->int_num, v_context->cycles, context->current_cycle, v_context->frame, v_context->vcounter, v_context->hslot, context->status & 0x7, v_context->hint_counter); + old_int_cycle = context->int_cycle; + }*/ + + if (context->status & M68K_STATUS_TRACE || context->trace_pending) { + context->target_cycle = context->current_cycle; + return; + } + + context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; + if (context->should_return || gen->header.enter_debugger || context->wp_hit) { + context->target_cycle = context->current_cycle; + } else if (context->target_cycle < context->current_cycle) { + //Changes to SR can result in an interrupt cycle that's in the past + //This can cause issues with the implementation of STOP though + context->target_cycle = context->current_cycle; + } + if (context->target_cycle == context->int_cycle) { + //Currently delays from Z80 access and refresh are applied only when we sync + //this can cause extra latency when it comes to interrupts + //to prevent this code forces some extra synchronization in the period immediately before an interrupt + if ((context->target_cycle - context->current_cycle) > gen->int_latency_prev1) { + context->target_cycle = context->sync_cycle = context->int_cycle - gen->int_latency_prev1; + } else if ((context->target_cycle - context->current_cycle) > gen->int_latency_prev2) { + context->target_cycle = context->sync_cycle = context->int_cycle - gen->int_latency_prev2; + } else { + context->target_cycle = context->sync_cycle = context->current_cycle; + } + + } +} + static m68k_context* sync_components_pico(m68k_context * context, uint32_t address) { genesis_context * gen = context->system; @@ -692,7 +788,7 @@ } uint32_t mclks = context->current_cycle; - psg_run(gen->psg, mclks); + sync_sound_pico(gen, mclks); vdp_run_context(v_context, mclks); if (mclks >= gen->reset_cycle) { gen->reset_requested = 1; @@ -737,6 +833,7 @@ vgm_adjust_cycles(gen->psg->vgm, deduction); } gen->psg->cycles -= deduction; + gen->adpcm->cycle -= deduction; if (gen->reset_cycle != CYCLE_NEVER) { gen->reset_cycle -= deduction; } @@ -756,7 +853,7 @@ if (!address && (gen->header.enter_debugger || gen->header.save_state)) { context->sync_cycle = context->current_cycle + 1; } - adjust_int_cycle(context, v_context); + adjust_int_cycle_pico(context, v_context); if (gen->reset_cycle < context->target_cycle) { context->target_cycle = gen->reset_cycle; } @@ -809,10 +906,12 @@ static m68k_context *int_ack(m68k_context *context) { genesis_context * gen = context->system; - vdp_context * v_context = gen->vdp; - //printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot); - vdp_run_context(v_context, context->current_cycle); - vdp_int_ack(v_context); + if (gen->header.type != SYSTEM_PICO || context->int_num > 4 || context->int_num < 3) { + vdp_context * v_context = gen->vdp; + //printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot); + vdp_run_context(v_context, context->current_cycle); + vdp_int_ack(v_context); + } //the Genesis responds to these exclusively with !VPA which means its a slow //6800 operation. documentation says these can take between 10 and 19 cycles. @@ -909,7 +1008,11 @@ } else { context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context); //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); - adjust_int_cycle(context, v_context); + if (gen->header.type == SYSTEM_PICO) { + adjust_int_cycle_pico(context, v_context); + } else { + adjust_int_cycle(context, v_context); + } } } else { fatal_error("Illegal write to HV Counter port %X\n", vdp_port); @@ -1239,7 +1342,24 @@ static void* pico_io_write_w(uint32_t location, void *vcontext, uint16_t value) { - printf("Pico IO write.w %X - %X\n", location, value); + uint32_t port = location & 0xFE; + m68k_context *context = vcontext; + genesis_context *gen = context->system; + if (port == 0x10) { + sync_sound_pico(gen, context->current_cycle); + pico_pcm_data_write(gen->adpcm, value); + printf("PICO ADPCM Data: %04X\n", value); + if (context->int_num == 3) { + adjust_int_cycle_pico(context, gen->vdp); + } + } else if (port == 0x12) { + sync_sound_pico(gen, context->current_cycle); + printf("PICO ADPCM Control: %04X\n", value); + pico_pcm_ctrl_write(gen->adpcm, value); + adjust_int_cycle_pico(context, gen->vdp); + } else { + return pico_io_write(location, vcontext, value); + } return vcontext; } @@ -1376,6 +1496,7 @@ { m68k_context *m68k = vcontext; genesis_context *gen = m68k->system; + uint16_t tmp; switch(location >> 1 & 0x7F) { case 0: @@ -1394,9 +1515,14 @@ return gen->pico_page; case 8: //printf("uPD7759 data read @ %u\n", m68k->current_cycle); - return 0xFF; + sync_sound_pico(gen, m68k->current_cycle); + tmp = pico_pcm_data_read(gen->adpcm); + return (location & 1) ? tmp >> 8 : tmp; case 9: //printf("uPD7759 contro/status read @ %u\n", m68k->current_cycle); + sync_sound_pico(gen, m68k->current_cycle); + tmp = pico_pcm_ctrl_read(gen->adpcm); + return (location & 1) ? tmp >> 8 : tmp; return 0; default: printf("Unknown Pico IO read %X @ %u\n", location, m68k->current_cycle); @@ -1408,6 +1534,14 @@ { m68k_context *m68k = vcontext; genesis_context *gen = m68k->system; + uint32_t port = location & 0xFE; + if (port == 0x10) { + sync_sound_pico(gen, m68k->current_cycle); + return pico_pcm_data_read(gen->adpcm); + } else if (port == 0x12) { + sync_sound_pico(gen, m68k->current_cycle); + return pico_pcm_ctrl_read(gen->adpcm); + } uint16_t value = pico_io_read(location, vcontext); return value | (value << 8); } @@ -1778,7 +1912,11 @@ insert_breakpoint(gen->m68k, pc, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter); #endif } - adjust_int_cycle(gen->m68k, gen->vdp); + if (gen->header.type == SYSTEM_PICO) { + adjust_int_cycle_pico(gen->m68k, gen->vdp); + } else { + adjust_int_cycle(gen->m68k, gen->vdp); + } start_68k_context(gen->m68k, pc); } else { if (gen->header.enter_debugger) { @@ -1961,7 +2099,10 @@ free(gen->z80); free(gen->zram); } - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO) { + pico_pcm_free(gen->adpcm); + free(gen->adpcm); + } else { ym_free(gen->ym); } psg_free(gen->psg); @@ -2226,7 +2367,9 @@ } else if (debug_view == DEBUG_OSCILLOSCOPE) { if (gen->psg->scope) { oscilloscope *scope = gen->psg->scope; - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO) { + gen->adpcm->scope = NULL; + } else { gen->ym->scope = NULL; } gen->psg->scope = NULL; @@ -2237,7 +2380,9 @@ scope_close(scope); } else { oscilloscope *scope = create_oscilloscope(); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO) { + pico_pcm_enable_scope(gen->adpcm, scope, gen->normal_clock); + } else { ym_enable_scope(gen->ym, scope, gen->normal_clock); } psg_enable_scope(gen->psg, scope, gen->normal_clock); @@ -2913,6 +3058,10 @@ gen->psg = calloc(1, sizeof(psg_context)); psg_init(gen->psg, gen->master_clock, MCLKS_PER_PSG); + + gen->adpcm = calloc(1, sizeof(pico_pcm)); + pico_pcm_init(gen->adpcm, gen->master_clock, 42); + gen->work_ram = calloc(2, RAM_WORDS); if (!strcmp("random", tern_find_path_default(config, "system\0ram_init\0", (tern_val){.ptrval = "zero"}, TVAL_PTR).ptrval)) {