# HG changeset patch # User Michael Pavone # Date 1669168622 28800 # Node ID 0d1d5dccdd28da7c43648d08c99010a29b0bbd0b # Parent 8e8db914120912641859cba97238e79f3271977a Initial implementation of oscilloscope debug view diff -r 8e8db9141209 -r 0d1d5dccdd28 Makefile --- a/Makefile Wed Sep 21 23:16:39 2022 -0700 +++ b/Makefile Tue Nov 22 17:57:02 2022 -0800 @@ -196,7 +196,7 @@ endif endif endif -AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o rf5c164.o +AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o rf5c164.o oscilloscope.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o diff -r 8e8db9141209 -r 0d1d5dccdd28 bindings.c --- a/bindings.c Wed Sep 21 23:16:39 2022 -0700 +++ b/bindings.c Tue Nov 22 17:57:02 2022 -0800 @@ -42,7 +42,8 @@ UI_PLANE_DEBUG, UI_VRAM_DEBUG, UI_CRAM_DEBUG, - UI_COMPOSITE_DEBUG + UI_COMPOSITE_DEBUG, + UI_OSCILLOSCOPE_DEBUG, } ui_action; typedef struct { @@ -432,7 +433,10 @@ case UI_VRAM_DEBUG: case UI_CRAM_DEBUG: case UI_COMPOSITE_DEBUG: - if (allow_content_binds) { + case UI_OSCILLOSCOPE_DEBUG: + if (allow_content_binds && current_system->toggle_debug_view) { + current_system->toggle_debug_view(current_system, binding->subtype_a - UI_PLANE_DEBUG + DEBUG_PLANE); + /* vdp_context *vdp = NULL; if (current_system->type == SYSTEM_GENESIS || current_system->type == SYSTEM_SEGACD) { genesis_context *gen = (genesis_context *)current_system; @@ -452,7 +456,7 @@ default: return; } vdp_toggle_debug_view(vdp, debug_type); - } + }*/ break; } } @@ -671,6 +675,8 @@ *subtype_a = UI_CRAM_DEBUG; } else if (!strcmp(target + 3, "compositing_debug")) { *subtype_a = UI_COMPOSITE_DEBUG; + } else if (!strcmp(target + 3, "oscilloscope")) { + *subtype_a = UI_OSCILLOSCOPE_DEBUG; } else { warning("Unreconized UI binding type %s\n", target); return 0; diff -r 8e8db9141209 -r 0d1d5dccdd28 config.c --- a/config.c Wed Sep 21 23:16:39 2022 -0700 +++ b/config.c Tue Nov 22 17:57:02 2022 -0800 @@ -285,7 +285,7 @@ *pads = tern_insert_node(*pads, key, dupe_tree(val.ptrval)); } -#define CONFIG_VERSION 5 +#define CONFIG_VERSION 6 static tern_node *migrate_config(tern_node *config, int from_version) { tern_node *def_config = parse_bundled_config("default.cfg"); @@ -361,6 +361,10 @@ config = tern_insert_path(config, "io\0ea_multitap\0""3\0", (tern_val){.ptrval = strdup(tap13)}, TVAL_PTR); config = tern_insert_path(config, "io\0ea_multitap\0""4\0", (tern_val){.ptrval = strdup(tap14)}, TVAL_PTR); } + case 5: { + char *binding_o = tern_find_path_default(config, "bindings\0keys\0o\0", (tern_val){.ptrval = "ui.oscilloscope"}, TVAL_PTR).ptrval; + config = tern_insert_path(config, "bindings\0keys\0o\0", (tern_val){.ptrval = strdup(binding_o)}, TVAL_PTR); + } } char buffer[16]; sprintf(buffer, "%d", CONFIG_VERSION); diff -r 8e8db9141209 -r 0d1d5dccdd28 default.cfg --- a/default.cfg Wed Sep 21 23:16:39 2022 -0700 +++ b/default.cfg Tue Nov 22 17:57:02 2022 -0800 @@ -21,6 +21,7 @@ v ui.vram_debug c ui.cram_debug n ui.compositing_debug + o ui.oscilloscope m ui.vgm_log esc ui.exit ` ui.save_state diff -r 8e8db9141209 -r 0d1d5dccdd28 genesis.c --- a/genesis.c Wed Sep 21 23:16:39 2022 -0700 +++ b/genesis.c Tue Nov 22 17:57:02 2022 -0800 @@ -467,6 +467,9 @@ gen->reset_cycle = CYCLE_NEVER; } if (v_context->frame != gen->last_frame) { + if (gen->ym->scope) { + scope_render(gen->ym->scope); + } //printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", gen->last_frame, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot); uint32_t elapsed = v_context->frame - gen->last_frame; gen->last_frame = v_context->frame; @@ -1712,6 +1715,33 @@ gen->header.vgm_logging = 0; } +static void toggle_debug_view(system_header *system, uint8_t debug_view) +{ + genesis_context *gen = (genesis_context *)system; + if (debug_view < DEBUG_OSCILLOSCOPE) { + vdp_toggle_debug_view(gen->vdp, debug_view); + } else if (debug_view == DEBUG_OSCILLOSCOPE) { + if (gen->ym->scope) { + oscilloscope *scope = gen->ym->scope; + gen->ym->scope = NULL; + gen->psg->scope = NULL; + if (gen->expansion) { + segacd_context *cd = gen->expansion; + cd->pcm.scope = NULL; + } + scope_close(scope); + } else { + oscilloscope *scope = create_oscilloscope(); + ym_enable_scope(gen->ym, scope); + psg_enable_scope(gen->psg, scope); + if (gen->expansion) { + segacd_context *cd = gen->expansion; + rf5c164_enable_scope(&cd->pcm, scope); + } + } + } +} + static void *tmss_rom_write_16(uint32_t address, void *context, uint16_t value) { m68k_context *m68k = context; @@ -1886,6 +1916,7 @@ gen->header.deserialize = deserialize; gen->header.start_vgm_log = start_vgm_log; gen->header.stop_vgm_log = stop_vgm_log; + gen->header.toggle_debug_view = toggle_debug_view; gen->header.type = SYSTEM_GENESIS; gen->header.info = *rom; set_region(gen, rom, force_region); diff -r 8e8db9141209 -r 0d1d5dccdd28 psg.c --- a/psg.c Wed Sep 21 23:16:39 2022 -0700 +++ b/psg.c Tue Nov 22 17:57:02 2022 -0800 @@ -21,6 +21,21 @@ context->pan = 0xFF; } +void psg_enable_scope(psg_context *context, oscilloscope *scope) +{ + context->scope = scope; + static const char *names[] = { + "PSG #1", + "PSG #2", + "PSG #3", + "PSG Noise", + }; + for (int i = 0; i < 4; i++) + { + context->scope_channel[i] = scope_add_channel(scope, names[i], 53693175 / context->clock_inc); + } +} + void psg_free(psg_context *context) { render_free_source(context->audio); @@ -91,6 +106,7 @@ void psg_run(psg_context * context, uint32_t cycles) { while (context->cycles < cycles) { + uint8_t trigger[4] = {0,0,0,0}; for (int i = 0; i < 4; i++) { if (context->counters[i]) { context->counters[i] -= 1; @@ -98,6 +114,7 @@ if (!context->counters[i]) { context->counters[i] = context->counter_load[i]; context->output_state[i] = !context->output_state[i]; + trigger[i] = context->output_state[i]; if (i == 3 && context->output_state[i]) { context->noise_out = context->lsfr & 1; context->lsfr = (context->lsfr >> 1) | (context->lsfr << 15); @@ -114,9 +131,10 @@ int16_t left_accum = 0, right_accum = 0; uint8_t pan_left = 0x10, pan_right = 0x1; + int16_t value = 0; for (int i = 0; i < 3; i++) { if (context->output_state[i]) { - int16_t value = volume_table[context->volume[i]]; + value = volume_table[context->volume[i]]; if (context->pan & pan_left) { left_accum += value; } @@ -126,9 +144,13 @@ pan_left <<= 1; pan_right <<= 1; } + if (context->scope) { + scope_add_sample(context->scope, context->scope_channel[i], value, trigger[i]); + } } + value = 0; if (context->noise_out) { - int16_t value = volume_table[context->volume[3]]; + value = volume_table[context->volume[3]]; if (context->pan & pan_left) { left_accum += value; } @@ -136,6 +158,9 @@ right_accum += value; } } + if (context->scope) { + scope_add_sample(context->scope, context->scope_channel[3], value, trigger[3]); + } render_put_stereo_sample(context->audio, left_accum, right_accum); diff -r 8e8db9141209 -r 0d1d5dccdd28 psg.h --- a/psg.h Wed Sep 21 23:16:39 2022 -0700 +++ b/psg.h Tue Nov 22 17:57:02 2022 -0800 @@ -10,10 +10,12 @@ #include "serialize.h" #include "render_audio.h" #include "vgm.h" +#include "oscilloscope.h" typedef struct { audio_source *audio; vgm_writer *vgm; + oscilloscope *scope; uint32_t clock_inc; uint32_t cycles; uint16_t lsfr; @@ -21,6 +23,7 @@ uint16_t counters[4]; uint8_t volume[4]; uint8_t output_state[4]; + uint8_t scope_channel[4]; uint8_t noise_out; uint8_t noise_use_tone; uint8_t noise_type; @@ -30,6 +33,7 @@ void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div); +void psg_enable_scope(psg_context *context, oscilloscope *scope); void psg_free(psg_context *context); void psg_adjust_master_clock(psg_context * context, uint32_t master_clock); void psg_write(psg_context * context, uint8_t value); diff -r 8e8db9141209 -r 0d1d5dccdd28 rf5c164.c --- a/rf5c164.c Wed Sep 21 23:16:39 2022 -0700 +++ b/rf5c164.c Tue Nov 22 17:57:02 2022 -0800 @@ -92,10 +92,15 @@ pcm->channels[pcm->cur_channel].cur_ptr = pcm->channels[pcm->cur_channel].regs[ST] << 19; //printf("chan %d START %X (%X raw)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); pcm->channels[pcm->cur_channel].state = NORMAL; + pcm->channels[pcm->cur_channel].trigger = 1; } else if (pcm->channels[pcm->cur_channel].state == LOOP) { + uint32_t old_ptr = pcm->channels[pcm->cur_channel].cur_ptr; pcm->channels[pcm->cur_channel].cur_ptr = (pcm->channels[pcm->cur_channel].regs[LSH] << 19) | (pcm->channels[pcm->cur_channel].regs[LSL] << 11); //printf("chan %d LOOP %X (%X raw)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); pcm->channels[pcm->cur_channel].state = NORMAL; + pcm->channels[pcm->cur_channel].trigger = old_ptr != pcm->channels[pcm->cur_channel].cur_ptr; + } else { + pcm->channels[pcm->cur_channel].trigger = 0; } } write_if_not_sounding(pcm); @@ -144,8 +149,13 @@ int16_t left = (sample * (pcm->channels[pcm->cur_channel].regs[PAN] >> 4)) >> 5; int16_t right = (sample * (pcm->channels[pcm->cur_channel].regs[PAN] & 0xF)) >> 5; //printf("chan %d, raw %X, sample %d, left %d, right %d, ptr %X (raw %X)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].sample, sample, left, right, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); + if (pcm->scope) { + scope_add_sample(pcm->scope, pcm->channels[pcm->cur_channel].scope_channel, sample, pcm->channels[pcm->cur_channel].trigger); + } pcm->left += left; pcm->right += right; + } else if (pcm->scope) { + scope_add_sample(pcm->scope, pcm->channels[pcm->cur_channel].scope_channel, 0, 0); } write_if_not_sounding(pcm); CHECK; @@ -228,3 +238,22 @@ return 0xFF; } } + +void rf5c164_enable_scope(rf5c164* pcm, oscilloscope *scope) +{ + static const char *names[] = { + "Richo #1", + "Richo #2", + "Richo #3", + "Richo #4", + "Richo #5", + "Richo #6", + "Richo #7", + "Richo #8", + }; + pcm->scope = scope; + for (int i = 0; i < 8; i ++) + { + pcm->channels[i].scope_channel = scope_add_channel(scope, names[i], 50000000 / (pcm->clock_step * 96)); + } +} diff -r 8e8db9141209 -r 0d1d5dccdd28 rf5c164.h --- a/rf5c164.h Wed Sep 21 23:16:39 2022 -0700 +++ b/rf5c164.h Tue Nov 22 17:57:02 2022 -0800 @@ -2,16 +2,20 @@ #define RF5C164_H_ #include #include "render_audio.h" +#include "oscilloscope.h" typedef struct { uint32_t cur_ptr; uint8_t regs[7]; uint8_t sample; uint8_t state; + uint8_t scope_channel; + uint8_t trigger; } rf5c164_channel; typedef struct { audio_source *audio; + oscilloscope *scope; uint32_t cycle; uint32_t clock_step; uint16_t ram[64*1024]; @@ -33,5 +37,6 @@ void rf5c164_run(rf5c164* pcm, uint32_t cycle); void rf5c164_write(rf5c164* pcm, uint16_t address, uint8_t value); uint8_t rf5c164_read(rf5c164* pcm, uint16_t address); +void rf5c164_enable_scope(rf5c164* pcm, oscilloscope *scope); #endif //RF5C164_H_ diff -r 8e8db9141209 -r 0d1d5dccdd28 sms.c --- a/sms.c Wed Sep 21 23:16:39 2022 -0700 +++ b/sms.c Tue Nov 22 17:57:02 2022 -0800 @@ -426,6 +426,9 @@ } if (sms->vdp->frame != sms->last_frame) { + if (sms->psg->scope) { + scope_render(sms->psg->scope); + } uint32_t elapsed = sms->vdp->frame - sms->last_frame; sms->last_frame = sms->vdp->frame; if (system->enter_debugger_frames) { @@ -648,6 +651,22 @@ setup_io_devices(config, &system->info, &sms->io); } +static void toggle_debug_view(system_header *system, uint8_t debug_view) +{ + sms_context *sms = (sms_context *)system; + if (debug_view < DEBUG_OSCILLOSCOPE) { + vdp_toggle_debug_view(sms->vdp, debug_view); + } else if (debug_view == DEBUG_OSCILLOSCOPE) { + if (sms->psg->scope) { + oscilloscope *scope = sms->psg->scope; + sms->psg->scope = NULL; + scope_close(scope); + } else { + oscilloscope *scope = create_oscilloscope(); + psg_enable_scope(sms->psg, scope); + } + } +} sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region) { @@ -750,6 +769,7 @@ sms->header.config_updated = config_updated; sms->header.serialize = serialize; sms->header.deserialize = deserialize; + sms->header.toggle_debug_view = toggle_debug_view; sms->header.type = SYSTEM_SMS; return sms; diff -r 8e8db9141209 -r 0d1d5dccdd28 system.h --- a/system.h Wed Sep 21 23:16:39 2022 -0700 +++ b/system.h Tue Nov 22 17:57:02 2022 -0800 @@ -22,6 +22,15 @@ DEBUGGER_GDB } debugger_type; +enum { + DEBUG_PLANE, + DEBUG_VRAM, + DEBUG_CRAM, + DEBUG_COMPOSITE, + DEBUG_OSCILLOSCOPE, + NUM_DEBUG_TYPES +}; + typedef void (*system_fun)(system_header *); typedef uint16_t (*system_fun_r16)(system_header *); typedef void (*system_str_fun)(system_header *, char *); @@ -65,6 +74,7 @@ system_ptr8_sizet_fun deserialize; system_str_fun start_vgm_log; system_fun stop_vgm_log; + system_u8_fun toggle_debug_view; rom_info info; arena *arena; char *next_rom; diff -r 8e8db9141209 -r 0d1d5dccdd28 vdp.c --- a/vdp.c Wed Sep 21 23:16:39 2022 -0700 +++ b/vdp.c Tue Nov 22 17:57:02 2022 -0800 @@ -264,7 +264,7 @@ if (headless) { free(context->fb); } - for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++) + for (int i = 0; i < NUM_DEBUG_TYPES; i++) { if (context->enabled_debuggers & (1 << i)) { vdp_toggle_debug_view(context, i); @@ -1900,7 +1900,7 @@ jump_end = 0x1D5; } - if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM | 1 << VDP_DEBUG_COMPOSITE)) { + if (context->enabled_debuggers & (1 << DEBUG_CRAM | 1 << DEBUG_COMPOSITE)) { uint32_t line = context->vcounter; if (line >= jump_end) { line -= jump_end - jump_start; @@ -1912,8 +1912,8 @@ } else { line += context->border_top; } - if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) { - uint32_t *fb = context->debug_fbs[VDP_DEBUG_CRAM] + context->debug_fb_pitch[VDP_DEBUG_CRAM] * line / sizeof(uint32_t); + if (context->enabled_debuggers & (1 << DEBUG_CRAM)) { + uint32_t *fb = context->debug_fbs[DEBUG_CRAM] + context->debug_fb_pitch[DEBUG_CRAM] * line / sizeof(uint32_t); if (context->regs[REG_MODE_2] & BIT_MODE_5) { for (int i = 0; i < 64; i++) { @@ -1933,10 +1933,10 @@ } } if ( - context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE) + context->enabled_debuggers & (1 << DEBUG_COMPOSITE) && line < (context->inactive_start + context->border_bot + context->border_top) ) { - uint32_t *fb = context->debug_fbs[VDP_DEBUG_COMPOSITE] + context->debug_fb_pitch[VDP_DEBUG_COMPOSITE] * line / sizeof(uint32_t); + uint32_t *fb = context->debug_fbs[DEBUG_COMPOSITE] + context->debug_fb_pitch[DEBUG_COMPOSITE] * line / sizeof(uint32_t); for (int i = 0; i < LINEBUF_SIZE; i++) { *(fb++) = context->debugcolors[context->layer_debug_buf[i]]; @@ -1970,9 +1970,9 @@ static void vdp_update_per_frame_debug(vdp_context *context) { - if (context->enabled_debuggers & (1 << VDP_DEBUG_PLANE)) { + if (context->enabled_debuggers & (1 << DEBUG_PLANE)) { uint32_t pitch; - uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_PLANE], &pitch); + uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[DEBUG_PLANE], &pitch); uint16_t hscroll_mask; uint16_t v_mul; uint16_t vscroll_mask = 0x1F | (context->regs[REG_SCROLL] & 0x30) << 1; @@ -1997,7 +1997,7 @@ break; } uint16_t table_address; - switch(context->debug_modes[VDP_DEBUG_PLANE] % 3) + switch(context->debug_modes[DEBUG_PLANE] % 3) { case 0: table_address = context->regs[REG_SCROLL_A] << 10 & 0xE000; @@ -2067,14 +2067,14 @@ } } } - render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_PLANE], 1024); + render_framebuffer_updated(context->debug_fb_indices[DEBUG_PLANE], 1024); } - if (context->enabled_debuggers & (1 << VDP_DEBUG_VRAM)) { + if (context->enabled_debuggers & (1 << DEBUG_VRAM)) { uint32_t pitch; - uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_VRAM], &pitch); + uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[DEBUG_VRAM], &pitch); - uint8_t pal = (context->debug_modes[VDP_DEBUG_VRAM] % 4) << 4; + uint8_t pal = (context->debug_modes[DEBUG_VRAM] % 4) << 4; for (int y = 0; y < 512; y++) { uint32_t *line = fb + y * pitch / sizeof(uint32_t); @@ -2096,13 +2096,13 @@ } } - render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_VRAM], 1024); + render_framebuffer_updated(context->debug_fb_indices[DEBUG_VRAM], 1024); } - if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) { + if (context->enabled_debuggers & (1 << DEBUG_CRAM)) { uint32_t starting_line = 512 - 32*4; - uint32_t *line = context->debug_fbs[VDP_DEBUG_CRAM] - + context->debug_fb_pitch[VDP_DEBUG_CRAM] * starting_line / sizeof(uint32_t); + uint32_t *line = context->debug_fbs[DEBUG_CRAM] + + context->debug_fb_pitch[DEBUG_CRAM] * starting_line / sizeof(uint32_t); if (context->regs[REG_MODE_2] & BIT_MODE_5) { for (int pal = 0; pal < 4; pal ++) { @@ -2118,14 +2118,14 @@ } *(cur++) = 0xFF000000; } - line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); + line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t); } cur = line; for (int x = 0; x < 512; x++) { *(cur++) = 0xFF000000; } - line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); + line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t); } } else { for (int pal = 0; pal < 2; pal ++) @@ -2142,22 +2142,22 @@ } *(cur++) = 0xFF000000; } - line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); + line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t); } cur = line; for (int x = 0; x < 512; x++) { *(cur++) = 0xFF000000; } - line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); + line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t); } } - render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_CRAM], 512); - context->debug_fbs[VDP_DEBUG_CRAM] = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_CRAM], &context->debug_fb_pitch[VDP_DEBUG_CRAM]); - } - if (context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)) { - render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], LINEBUF_SIZE); - context->debug_fbs[VDP_DEBUG_COMPOSITE] = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], &context->debug_fb_pitch[VDP_DEBUG_COMPOSITE]); + render_framebuffer_updated(context->debug_fb_indices[DEBUG_CRAM], 512); + context->debug_fbs[DEBUG_CRAM] = render_get_framebuffer(context->debug_fb_indices[DEBUG_CRAM], &context->debug_fb_pitch[DEBUG_CRAM]); + } + if (context->enabled_debuggers & (1 << DEBUG_COMPOSITE)) { + render_framebuffer_updated(context->debug_fb_indices[DEBUG_COMPOSITE], LINEBUF_SIZE); + context->debug_fbs[DEBUG_COMPOSITE] = render_get_framebuffer(context->debug_fb_indices[DEBUG_COMPOSITE], &context->debug_fb_pitch[DEBUG_COMPOSITE]); } } @@ -4655,7 +4655,7 @@ static void vdp_debug_window_close(uint8_t which) { //TODO: remove need for current_vdp global, and find the VDP via current_system instead - for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++) + for (int i = 0; i < NUM_DEBUG_TYPES; i++) { if (current_vdp->enabled_debuggers & (1 << i) && which == current_vdp->debug_fb_indices[i]) { vdp_toggle_debug_view(current_vdp, i); @@ -4675,22 +4675,22 @@ char *caption; switch(debug_type) { - case VDP_DEBUG_PLANE: + case DEBUG_PLANE: caption = "BlastEm - VDP Plane Debugger"; width = height = 1024; break; - case VDP_DEBUG_VRAM: + case DEBUG_VRAM: caption = "BlastEm - VDP VRAM Debugger"; width = 1024; height = 512; break; - case VDP_DEBUG_CRAM: + case DEBUG_CRAM: caption = "BlastEm - VDP CRAM Debugger"; width = 512; height = 512; fetch_immediately = 1; break; - case VDP_DEBUG_COMPOSITE: + case DEBUG_COMPOSITE: caption = "BlastEm - VDP Plane Composition Debugger"; width = LINEBUF_SIZE; height = context->inactive_start + context->border_top + context->border_bot; @@ -4716,7 +4716,7 @@ if (active < FRAMEBUFFER_USER_START) { return; } - for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++) + for (int i = 0; i < NUM_DEBUG_TYPES; i++) { if (context->enabled_debuggers & (1 << i) && context->debug_fb_indices[i] == active) { context->debug_modes[i]++; diff -r 8e8db9141209 -r 0d1d5dccdd28 vdp.h --- a/vdp.h Wed Sep 21 23:16:39 2022 -0700 +++ b/vdp.h Tue Nov 22 17:57:02 2022 -0800 @@ -157,14 +157,6 @@ } fifo_entry; enum { - VDP_DEBUG_PLANE, - VDP_DEBUG_VRAM, - VDP_DEBUG_CRAM, - VDP_DEBUG_COMPOSITE, - VDP_NUM_DEBUG_TYPES -}; - -enum { VDP_GENESIS, VDP_GAMEGEAR, VDP_SMS2, @@ -179,13 +171,13 @@ //pointer to current framebuffer uint32_t *fb; uint8_t *done_composite; - uint32_t *debug_fbs[VDP_NUM_DEBUG_TYPES]; + uint32_t *debug_fbs[NUM_DEBUG_TYPES]; char *kmod_msg_buffer; uint32_t kmod_buffer_storage; uint32_t kmod_buffer_length; uint32_t timer_start_cycle; uint32_t output_pitch; - uint32_t debug_fb_pitch[VDP_NUM_DEBUG_TYPES]; + uint32_t debug_fb_pitch[NUM_DEBUG_TYPES]; fifo_entry fifo[FIFO_SIZE]; int32_t fifo_write; int32_t fifo_read; @@ -251,8 +243,8 @@ uint8_t tmp_buf_a[SCROLL_BUFFER_SIZE]; uint8_t tmp_buf_b[SCROLL_BUFFER_SIZE]; uint8_t enabled_debuggers; - uint8_t debug_fb_indices[VDP_NUM_DEBUG_TYPES]; - uint8_t debug_modes[VDP_NUM_DEBUG_TYPES]; + uint8_t debug_fb_indices[NUM_DEBUG_TYPES]; + uint8_t debug_modes[NUM_DEBUG_TYPES]; uint8_t pushed_frame; uint8_t type; uint8_t cram_latch; diff -r 8e8db9141209 -r 0d1d5dccdd28 ym2612.c --- a/ym2612.c Wed Sep 21 23:16:39 2022 -0700 +++ b/ym2612.c Tue Nov 22 17:57:02 2022 -0800 @@ -156,9 +156,11 @@ memset(context->part2_regs, 0, sizeof(context->part2_regs)); memset(context->operators, 0, sizeof(context->operators)); FILE* savedlogs[NUM_CHANNELS]; + uint8_t saved_scope_channel[NUM_CHANNELS]; for (int i = 0; i < NUM_CHANNELS; i++) { savedlogs[i] = context->channels[i].logfile; + saved_scope_channel[i] = context->channels[i].scope_channel; } memset(context->channels, 0, sizeof(context->channels)); memset(context->ch3_supp, 0, sizeof(context->ch3_supp)); @@ -179,6 +181,7 @@ for (int i = 0; i < NUM_CHANNELS; i++) { context->channels[i].lr = 0xC0; context->channels[i].logfile = savedlogs[i]; + context->channels[i].scope_channel = saved_scope_channel[i]; if (i < 3) { context->part1_regs[REG_LR_AMS_PMS - YM_PART1_START + i] = 0xC0; } else { @@ -496,7 +499,9 @@ ym_operator * operator = context->operators + op; ym_channel * chan = context->channels + channel; uint16_t phase = operator->phase_counter >> 10 & 0x3FF; + uint32_t old_phase = operator->phase_counter; operator->phase_counter += operator->phase_inc;//ym_calc_phase_inc(context, operator, op); + operator->phase_overflow = (old_phase & 0xFFFFF) > (operator->phase_counter & 0xFFFFF); int16_t mod = 0; if (op & 3) { if (operator->mod_src[0]) { @@ -568,21 +573,27 @@ if (op % 4 == 3) { if (chan->algorithm < 4) { chan->output = operator->output; + chan->phase_overflow = operator->phase_overflow; } else if(chan->algorithm == 4) { - chan->output = operator->output + context->operators[channel * 4 + 2].output; + ym_operator *other_op = context->operators + channel * 4 + 2; + chan->output = operator->output + other_op->output; + if (operator->phase_inc < other_op->phase_inc) { + chan->phase_overflow = operator->phase_overflow; + } else { + chan->phase_overflow = other_op->phase_overflow; + } } else { output = 0; + uint32_t lowest_phase_inc = 0xFFFFFFFF; for (uint32_t op = ((chan->algorithm == 7) ? 0 : 1) + channel*4; op < (channel+1)*4; op++) { output += context->operators[op].output; + if (context->operators[op].phase_inc < lowest_phase_inc) { + lowest_phase_inc = context->operators[op].phase_inc; + chan->phase_overflow = context->operators[op].phase_overflow; + } } chan->output = output; } - if (first_key_on) { - int16_t value = context->channels[channel].output & 0x3FE0; - if (value & 0x2000) { - value |= 0xC000; - } - } } //puts("operator update done"); } @@ -611,6 +622,9 @@ if (context->channels[i].logfile) { fwrite(&value, sizeof(value), 1, context->channels[i].logfile); } + if (context->scope) { + scope_add_sample(context->scope, context->channels[i].scope_channel, value, context->channels[i].phase_overflow); + } if (context->channels[i].lr & 0x80) { left += (value * context->volume_mult) / context->volume_div; } else if (context->zero_offset) { @@ -1382,3 +1396,21 @@ context->last_status_cycle = context->write_cycle; } } + +void ym_enable_scope(ym2612_context *context, oscilloscope *scope) +{ + static const char *names[] = { + "YM2612 #1", + "YM2612 #2", + "YM2612 #3", + "YM2612 #4", + "YM2612 #5", + "YM2612 #6" + }; + context->scope = scope; + for (int i = 0; i < NUM_CHANNELS; i++) + { + //TODO: calculate actual sample rate based on current clock settings + context->channels[i].scope_channel = scope_add_channel(scope, names[i], 53267); + } +} diff -r 8e8db9141209 -r 0d1d5dccdd28 ym2612.h --- a/ym2612.h Wed Sep 21 23:16:39 2022 -0700 +++ b/ym2612.h Tue Nov 22 17:57:02 2022 -0800 @@ -11,6 +11,7 @@ #include "serialize.h" #include "render_audio.h" #include "vgm.h" +#include "oscilloscope.h" #define NUM_PART_REGS (0xB7-0x30) #define NUM_CHANNELS 6 @@ -35,6 +36,7 @@ uint8_t env_phase; uint8_t ssg; uint8_t inverted; + uint8_t phase_overflow; } ym_operator; typedef struct { @@ -52,6 +54,8 @@ uint8_t pms; uint8_t lr; uint8_t keyon; + uint8_t scope_channel; + uint8_t phase_overflow; } ym_channel; typedef struct { @@ -69,45 +73,46 @@ typedef struct { audio_source *audio; - vgm_writer *vgm; - uint32_t clock_inc; - uint32_t current_cycle; - uint32_t write_cycle; - uint32_t busy_start; - uint32_t busy_cycles; - uint32_t last_status_cycle; - uint32_t invalid_status_decay; - uint32_t status_address_mask; - int32_t volume_mult; - int32_t volume_div; - ym_operator operators[NUM_OPERATORS]; - ym_channel channels[NUM_CHANNELS]; - int16_t zero_offset; - uint16_t timer_a; - uint16_t timer_a_load; - uint16_t env_counter; - ym_supp ch3_supp[3]; - uint8_t timer_b; - uint8_t sub_timer_b; - uint8_t timer_b_load; - uint8_t ch3_mode; - uint8_t current_op; - uint8_t current_env_op; + vgm_writer *vgm; + oscilloscope *scope; + uint32_t clock_inc; + uint32_t current_cycle; + uint32_t write_cycle; + uint32_t busy_start; + uint32_t busy_cycles; + uint32_t last_status_cycle; + uint32_t invalid_status_decay; + uint32_t status_address_mask; + int32_t volume_mult; + int32_t volume_div; + ym_operator operators[NUM_OPERATORS]; + ym_channel channels[NUM_CHANNELS]; + int16_t zero_offset; + uint16_t timer_a; + uint16_t timer_a_load; + uint16_t env_counter; + ym_supp ch3_supp[3]; + uint8_t timer_b; + uint8_t sub_timer_b; + uint8_t timer_b_load; + uint8_t ch3_mode; + uint8_t current_op; + uint8_t current_env_op; - uint8_t timer_control; - uint8_t dac_enable; - uint8_t lfo_enable; - uint8_t lfo_freq; - uint8_t lfo_counter; - uint8_t lfo_am_step; - uint8_t lfo_pm_step; - uint8_t csm_keyon; - uint8_t status; - uint8_t last_status; - uint8_t selected_reg; - uint8_t selected_part; - uint8_t part1_regs[YM_PART1_REGS]; - uint8_t part2_regs[YM_PART2_REGS]; + uint8_t timer_control; + uint8_t dac_enable; + uint8_t lfo_enable; + uint8_t lfo_freq; + uint8_t lfo_counter; + uint8_t lfo_am_step; + uint8_t lfo_pm_step; + uint8_t csm_keyon; + uint8_t status; + uint8_t last_status; + uint8_t selected_reg; + uint8_t selected_part; + uint8_t part1_regs[YM_PART1_REGS]; + uint8_t part2_regs[YM_PART2_REGS]; } ym2612_context; enum { @@ -154,6 +159,7 @@ void ym_print_timer_info(ym2612_context *context); void ym_serialize(ym2612_context *context, serialize_buffer *buf); void ym_deserialize(deserialize_buffer *buf, void *vcontext); +void ym_enable_scope(ym2612_context *context, oscilloscope *scope); #endif //YM2612_H_