Mercurial > repos > blastem
diff ymf262.c @ 2558:3f58fec775df
Initial work on YMF262 (aka OPL3) emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 19 Jan 2025 00:31:16 -0800 |
parents | |
children | eb588f22ec76 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ymf262.c Sun Jan 19 00:31:16 2025 -0800 @@ -0,0 +1,129 @@ +#include <stdlib.h> +#include <string.h> +#include "ymf262.h" +#include "render_audio.h" + +void ymf262_init(ymf262_context *context, uint32_t master_clock, uint32_t clock_div, uint32_t options) +{ + memset(context, 0, sizeof(*context)); + context->clock_inc = clock_div * 8; + context->audio = render_audio_source("YMF262", master_clock, context->clock_inc * OPL3_NUM_OPERATORS, 2); + ymf262_reset(context); +} + +void ymf262_reset(ymf262_context *context) +{ + //TODO: implement me +} + +void ymf262_free(ymf262_context *context) +{ + render_free_source(context->audio); + free(context); +} + +void ymf262_adjust_master_clock(ymf262_context *context, uint32_t master_clock) +{ + render_audio_adjust_clock(context->audio, master_clock, context->clock_inc * OPL3_NUM_OPERATORS); +} + +void ymf262_adjust_cycles(ymf262_context *context, uint32_t deduction) +{ + context->cycle -= deduction; +} + +void ymf262_run(ymf262_context *context, uint32_t to_cycle) +{ + for (; context->cycle < to_cycle; context->cycle += context->clock_inc) + { + context->current_op++; + if (context->current_op == OPL3_NUM_OPERATORS) { + context->current_op = 0; + int16_t left = 0, right = 0; + render_put_stereo_sample(context->audio, left, right); + } + } +} + +void ymf262_address_write_part1(ymf262_context *context, uint8_t address) +{ + context->selected_reg = address; + context->selected_part = 0; +} +void ymf262_address_write_part2(ymf262_context *context, uint8_t address) +{ + context->selected_reg = address; + context->selected_part = 0; +} + +#define OPL3_NTS 0x08 + +void ymf262_data_write(ymf262_context *context, uint8_t value) +{ + if (!context->selected_reg) { + return; + } + uint8_t old = 0; + if (context->selected_reg >= OPL3_PARAM_START && context->selected_reg < OPL3_PARAM_END) { + if (context->selected_part) { + old = context->part2_regs[context->selected_reg - OPL3_PARAM_START]; + context->part2_regs[context->selected_reg - OPL3_PARAM_START] = value; + } else { + old = context->part1_regs[context->selected_reg - OPL3_PARAM_START]; + context->part1_regs[context->selected_reg - OPL3_PARAM_START] = value; + } + } else if (context->selected_part) { + if (context->selected_reg <= sizeof(context->timer_test)) { + old = context->timer_test[context->selected_reg - 1]; + context->timer_test[context->selected_reg - 1] = value; + } else if (context->selected_reg == OPL3_NTS) { + old = context->nts; + context->nts = value; + } else { + return; + } + } else { + switch (context->selected_reg) + { + case 0x01: + old = context->part2_test; + context->part2_test = value; + break; + case 0x04: + old = context->connection_sel; + context->connection_sel = value; + break; + case 0x05: + old = context->opl3_mode; + context->opl3_mode = value; + break; + default: + return; + } + } + if (value != old) { + if (context->vgm) { + if (context->selected_reg) { + vgm_ymf262_part2_write(context->vgm, context->cycle, context->selected_reg, value); + } else { + vgm_ymf262_part1_write(context->vgm, context->cycle, context->selected_reg, value); + } + } + } +} + +void ymf262_vgm_log(ymf262_context *context, uint32_t master_clock, vgm_writer *vgm) +{ + vgm_ymf262_init(vgm, 8 * master_clock / context->clock_inc); + context->vgm = vgm; + //TODO: write initial state +} + +uint8_t ymf262_read_status(ymf262_context *context, uint32_t cycle, uint32_t port) +{ + if (port) { + //TODO: Investigate behavior of invalid status reads + return 0xFF; + } + return context->status; +}