# HG changeset patch # User Michael Pavone # Date 1707284831 28800 # Node ID 61c0bfe1088712c1b74bb0820d28efb542aa93ac # Parent fb8d6ebf9d5fb3e56d8118f6229951c130b1a6ba Somewhat busted support for Pico ADPCM diff -r fb8d6ebf9d5f -r 61c0bfe10887 Makefile --- a/Makefile Tue Feb 06 06:34:49 2024 -0800 +++ b/Makefile Tue Feb 06 21:47:11 2024 -0800 @@ -234,12 +234,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 coleco.o \ + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o coleco.o pico_pcm.o \ segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o oscilloscope.o LIBOBJS=libblastem.o system.o genesis.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 coleco.o $(LIBZOBJS) \ + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o coleco.o pico_pcm.o $(LIBZOBJS) \ segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o ifdef NONUKLEAR diff -r fb8d6ebf9d5f -r 61c0bfe10887 genesis.c --- 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)) { diff -r fb8d6ebf9d5f -r 61c0bfe10887 genesis.h --- a/genesis.h Tue Feb 06 06:34:49 2024 -0800 +++ b/genesis.h Tue Feb 06 21:47:11 2024 -0800 @@ -17,6 +17,7 @@ #include "ym2612.h" #include "vdp.h" #include "psg.h" +#include "pico_pcm.h" #include "io.h" #include "romdb.h" #include "arena.h" @@ -31,6 +32,7 @@ vdp_context *vdp; ym2612_context *ym; psg_context *psg; + pico_pcm *adpcm; uint16_t *cart; uint16_t *lock_on; uint16_t *work_ram; diff -r fb8d6ebf9d5f -r 61c0bfe10887 pico_pcm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pico_pcm.c Tue Feb 06 21:47:11 2024 -0800 @@ -0,0 +1,294 @@ +#include "pico_pcm.h" +#include "backend.h" + +#define PCM_RESET 0x8000 +#define PCM_INT_EN 0x4000 +#define PCM_ENABLED 0x0800 +#define PCM_FILTER 0x00C0 +#define PCM_VOLUME 0x0007 + +void pico_pcm_reset(pico_pcm *pcm) +{ + pcm->fifo_read = sizeof(pcm->fifo); + pcm->fifo_write = 0; + pcm->adpcm_state = 0; + pcm->output = 0; + pcm->nibble_store = 0; + pcm->counter = 0; + pcm->samples = 0; + pcm->rate = 0; + pcm->ctrl &= 0x7FFF; +} + +void pico_pcm_init(pico_pcm *pcm, uint32_t master_clock, uint32_t divider) +{ + pcm->audio = render_audio_source("PICO ADPCM", master_clock, divider * 4, 1); + pcm->scope = NULL; + pcm->scope_channel = 0; + pcm->clock_inc = divider * 4; + pico_pcm_reset(pcm); +} + +void pico_pcm_free(pico_pcm *pcm) +{ + render_free_source(pcm->audio); +} + +void pico_pcm_enable_scope(pico_pcm *pcm, oscilloscope *scope, uint32_t master_clock) +{ +#ifndef IS_LIB + pcm->scope = scope; + pcm->scope_channel = scope_add_channel(scope, "PICO ADPCM", master_clock / pcm->clock_inc); +#endif +} + +static uint8_t pcm_fifo_read(pico_pcm *pcm) +{ + if (pcm->fifo_read == sizeof(pcm->fifo)) { + return 0; + } + uint8_t ret = pcm->fifo[pcm->fifo_read++]; + pcm->fifo_read &= sizeof(pcm->fifo) - 1; + if (pcm->fifo_read == pcm->fifo_write) { + pcm->fifo_read = sizeof(pcm->fifo); + } + return ret; +} + +int16_t upd7755_calc_sample(uint8_t sample, uint8_t *state) +{ + //Tables from MAME + static const int16_t sample_delta[256] = { + 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10, + 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13, + 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15, + 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19, + 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23, + 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29, + 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33, + 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43, + 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53, + 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64, + 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76, + 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96, + 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117, + 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143, + 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175, + 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 + }; + static const int state_delta[16] = {-1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3}; + int16_t ret = sample_delta[(*state << 4) + sample]; + int diff = state_delta[*state]; + if (diff >= 0 || *state > 0) { + *state += diff; + if (*state > 15) { + *state = 15; + } + } + return ret; +} + +void pico_pcm_run(pico_pcm *pcm, uint32_t cycle) +{ + while (pcm->cycle < cycle) + { + pcm->cycle += pcm->clock_inc; + //TODO: Figure out actual attenuation + int16_t shift = pcm->ctrl & PCM_VOLUME; +#ifndef IS_LIB + if (pcm->scope) { + scope_add_sample(pcm->scope, pcm->scope_channel, (pcm->output >> shift) * 128, 0); + } +#endif + render_put_mono_sample(pcm->audio, (pcm->output >> shift) * 128); + if (!(pcm->ctrl & PCM_ENABLED)) { + continue; + } + if (pcm->counter) { + pcm->counter--; + } else if (pcm->samples) { + pcm->samples--; + uint8_t sample; + if (pcm->nibble_store) { + sample = pcm->nibble_store & 0xF; + pcm->nibble_store = 0; + } else { + uint8_t byte = pcm_fifo_read(pcm); + sample = byte >> 4; + pcm->nibble_store = 0x80 | (byte & 0xF); + } + uint8_t old_state = pcm->adpcm_state; + pcm->output += upd7755_calc_sample(sample, &pcm->adpcm_state); + if (pcm->output > 255) { + pcm->output = 255; + } else if (pcm->output < -256) { + pcm->output = -256; + } + //printf("Sample %d, old_state %d, new_state %d, output %d\n", sample, old_state, pcm->adpcm_state, pcm->output); + pcm->counter = pcm->rate; + } else { + uint8_t cmd = pcm_fifo_read(pcm); + if (cmd) { + pcm->ctrl |= 0x8000; + } else { + pcm->ctrl &= 0x7FFF; + } + switch (cmd & 0xC0) + { + case 0: + pcm->output = 0; + pcm->adpcm_state = 0; + pcm->counter = (cmd & 0x3F) * 160; + break; + case 0x40: + pcm->rate = (cmd & 0x3F); + pcm->samples = 256; + break; + case 0x80: + pcm->rate = (cmd & 0x3F); + //FIXME: this probably does not happen instantly + pcm->samples = pcm_fifo_read(pcm) + 1; + break; + case 0xC0: + //FIXME: this probably does not happen instantly + //TODO: Does repeat mode even work on this chip? + // Does it work on a uPD7759 in slave mode? + // Is this correct behavior if it does work? + pcm->counter = pcm->rate = pcm_fifo_read(pcm) & 0x3F; + pcm->samples = (pcm_fifo_read(pcm) + 1) * ((cmd & 7) + 1); + break; + } + } + } +} + +// RI??E???FF???VVV +// R: 1 = Reset request, 0 = normal operation +// I: 1 = interrupts enabled, 0 = disabled +// E: 1 = Enabled? Sega code always sets this to 1 outside of reset +// F: Low-pass Filter 1 = 6 kHz, 2 = 12 kHz 3 = 16 kHz +// V: volume, probably attenuation value since converter defaults to "0" +void pico_pcm_ctrl_write(pico_pcm *pcm, uint16_t value) +{ + if (value & PCM_RESET) { + pico_pcm_reset(pcm); + } + pcm->ctrl &= 0x8000; + pcm->ctrl |= value & ~PCM_RESET; + //TODO: update low-pass filter +} + +void pico_pcm_data_write(pico_pcm *pcm, uint16_t value) +{ + if (pcm->fifo_read == sizeof(pcm->fifo)) { + pcm->fifo_read = pcm->fifo_write; + } + pcm->fifo[pcm->fifo_write++] = value >> 8; + pcm->fifo_write &= sizeof(pcm->fifo)-1; + pcm->fifo[pcm->fifo_write++] = value; + pcm->fifo_write &= sizeof(pcm->fifo)-1; +} + +uint16_t pico_pcm_ctrl_read(pico_pcm *pcm) +{ + return pcm->ctrl; +} + +uint16_t pico_pcm_data_read(pico_pcm *pcm) +{ + if (pcm->fifo_read == sizeof(pcm->fifo)) { + return sizeof(pcm->fifo) - 1; + } + return (pcm->fifo_read - pcm->fifo_write) & (sizeof(pcm->fifo)-1); +} + +#define FIFO_THRESHOLD 48 +uint32_t pico_pcm_next_int(pico_pcm *pcm) +{ + if (!(pcm->ctrl & PCM_INT_EN)) { + return CYCLE_NEVER; + } + uint32_t fifo_bytes; + if (pcm->fifo_read == sizeof(pcm->fifo)) { + fifo_bytes = 0; + } else if (pcm->fifo_read == pcm->fifo_write) { + fifo_bytes = sizeof(pcm->fifo); + } else { + fifo_bytes = (pcm->fifo_write - pcm->fifo_read) & (sizeof(pcm->fifo) - 1); + } + if (fifo_bytes < FIFO_THRESHOLD) { + return pcm->cycle; + } + uint32_t cycles_to_threshold = pcm->counter + 1; + if (pcm->samples) { + uint16_t samples = pcm->samples; + if (pcm->nibble_store) { + cycles_to_threshold += pcm->rate + 1; + samples--; + } + uint16_t bytes = (samples >> 1) + (samples & 1); + if (bytes > (fifo_bytes - FIFO_THRESHOLD)) { + cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * (pcm->rate + 1) * 2; + fifo_bytes = 0; + } else { + cycles_to_threshold += bytes * (pcm->rate + 1) * 2; + fifo_bytes -= bytes; + } + } + uint8_t fifo_read = pcm->fifo_read; + uint8_t cmd = 0; + while (fifo_bytes >= FIFO_THRESHOLD) + { + if (cmd) { + switch(cmd & 0xC0) + { + case 0: + cycles_to_threshold += 640 * (cmd & 0x3F) + 1; + break; + case 0x40: + cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * ((cmd & 0x3F) + 1) * 2; + fifo_bytes = 0; + break; + case 0x80: { + uint32_t samples = pcm->fifo[fifo_read++]; + fifo_bytes--; + fifo_read &= sizeof(pcm->fifo) - 1; + if (fifo_bytes < FIFO_THRESHOLD) { + break; + } + uint32_t bytes = (samples +1) >> 1; + if (bytes > (fifo_bytes - FIFO_THRESHOLD)) { + cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * ((cmd & 0x3F) + 1) * 2; + fifo_bytes = 0; + } + break; } + case 0xC0: { + uint32_t rate = pcm->fifo[fifo_read++] & 0x3F; + fifo_bytes--; + fifo_read &= sizeof(pcm->fifo) - 1; + uint32_t samples = pcm->fifo[fifo_read++] & 0x3F; + fifo_bytes--; + fifo_read &= sizeof(pcm->fifo) - 1; + if (fifo_bytes < FIFO_THRESHOLD) { + break; + } + samples++; + samples *= (cmd & 7) + 1; + uint32_t bytes = (samples + 1) >> 1; + if (bytes > (fifo_bytes - FIFO_THRESHOLD)) { + cycles_to_threshold += (fifo_bytes - FIFO_THRESHOLD + 1) * ((cmd & 0x3F) + 1) * 2; + fifo_bytes = 0; + } + break; } + } + cmd = 0; + } else { + cycles_to_threshold++; + cmd = pcm->fifo[fifo_read++]; + fifo_bytes--; + fifo_read &= sizeof(pcm->fifo) - 1; + } + } + + return pcm->cycle + cycles_to_threshold * pcm->clock_inc; +} diff -r fb8d6ebf9d5f -r 61c0bfe10887 pico_pcm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pico_pcm.h Tue Feb 06 21:47:11 2024 -0800 @@ -0,0 +1,38 @@ +#ifndef PICO_PCM_H_ +#define PICO_PCM_H_ + +#include +#include "render_audio.h" +#include "oscilloscope.h" + +typedef struct { + audio_source *audio; + oscilloscope *scope; + uint32_t clock_inc; + uint32_t cycle; + uint16_t ctrl; + + uint16_t counter; + uint16_t rate; + uint16_t samples; + int16_t output; + + uint8_t fifo[0x40]; + uint8_t fifo_read; + uint8_t fifo_write; + uint8_t adpcm_state; + uint8_t nibble_store; + uint8_t scope_channel; +} pico_pcm; + +void pico_pcm_init(pico_pcm *pcm, uint32_t master_clock, uint32_t divider); +void pico_pcm_free(pico_pcm *pcm); +void pico_pcm_enable_scope(pico_pcm *pcm, oscilloscope *scope, uint32_t master_clock); +void pico_pcm_run(pico_pcm *pcm, uint32_t cycle); +void pico_pcm_ctrl_write(pico_pcm *pcm, uint16_t value); +void pico_pcm_data_write(pico_pcm *pcm, uint16_t value); +uint16_t pico_pcm_ctrl_read(pico_pcm *pcm); +uint16_t pico_pcm_data_read(pico_pcm *pcm); +uint32_t pico_pcm_next_int(pico_pcm *pcm); + +#endif //PICO_PCM_H_