comparison 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
comparison
equal deleted inserted replaced
2557:75dd7536c467 2558:3f58fec775df
1 #include <stdlib.h>
2 #include <string.h>
3 #include "ymf262.h"
4 #include "render_audio.h"
5
6 void ymf262_init(ymf262_context *context, uint32_t master_clock, uint32_t clock_div, uint32_t options)
7 {
8 memset(context, 0, sizeof(*context));
9 context->clock_inc = clock_div * 8;
10 context->audio = render_audio_source("YMF262", master_clock, context->clock_inc * OPL3_NUM_OPERATORS, 2);
11 ymf262_reset(context);
12 }
13
14 void ymf262_reset(ymf262_context *context)
15 {
16 //TODO: implement me
17 }
18
19 void ymf262_free(ymf262_context *context)
20 {
21 render_free_source(context->audio);
22 free(context);
23 }
24
25 void ymf262_adjust_master_clock(ymf262_context *context, uint32_t master_clock)
26 {
27 render_audio_adjust_clock(context->audio, master_clock, context->clock_inc * OPL3_NUM_OPERATORS);
28 }
29
30 void ymf262_adjust_cycles(ymf262_context *context, uint32_t deduction)
31 {
32 context->cycle -= deduction;
33 }
34
35 void ymf262_run(ymf262_context *context, uint32_t to_cycle)
36 {
37 for (; context->cycle < to_cycle; context->cycle += context->clock_inc)
38 {
39 context->current_op++;
40 if (context->current_op == OPL3_NUM_OPERATORS) {
41 context->current_op = 0;
42 int16_t left = 0, right = 0;
43 render_put_stereo_sample(context->audio, left, right);
44 }
45 }
46 }
47
48 void ymf262_address_write_part1(ymf262_context *context, uint8_t address)
49 {
50 context->selected_reg = address;
51 context->selected_part = 0;
52 }
53 void ymf262_address_write_part2(ymf262_context *context, uint8_t address)
54 {
55 context->selected_reg = address;
56 context->selected_part = 0;
57 }
58
59 #define OPL3_NTS 0x08
60
61 void ymf262_data_write(ymf262_context *context, uint8_t value)
62 {
63 if (!context->selected_reg) {
64 return;
65 }
66 uint8_t old = 0;
67 if (context->selected_reg >= OPL3_PARAM_START && context->selected_reg < OPL3_PARAM_END) {
68 if (context->selected_part) {
69 old = context->part2_regs[context->selected_reg - OPL3_PARAM_START];
70 context->part2_regs[context->selected_reg - OPL3_PARAM_START] = value;
71 } else {
72 old = context->part1_regs[context->selected_reg - OPL3_PARAM_START];
73 context->part1_regs[context->selected_reg - OPL3_PARAM_START] = value;
74 }
75 } else if (context->selected_part) {
76 if (context->selected_reg <= sizeof(context->timer_test)) {
77 old = context->timer_test[context->selected_reg - 1];
78 context->timer_test[context->selected_reg - 1] = value;
79 } else if (context->selected_reg == OPL3_NTS) {
80 old = context->nts;
81 context->nts = value;
82 } else {
83 return;
84 }
85 } else {
86 switch (context->selected_reg)
87 {
88 case 0x01:
89 old = context->part2_test;
90 context->part2_test = value;
91 break;
92 case 0x04:
93 old = context->connection_sel;
94 context->connection_sel = value;
95 break;
96 case 0x05:
97 old = context->opl3_mode;
98 context->opl3_mode = value;
99 break;
100 default:
101 return;
102 }
103 }
104 if (value != old) {
105 if (context->vgm) {
106 if (context->selected_reg) {
107 vgm_ymf262_part2_write(context->vgm, context->cycle, context->selected_reg, value);
108 } else {
109 vgm_ymf262_part1_write(context->vgm, context->cycle, context->selected_reg, value);
110 }
111 }
112 }
113 }
114
115 void ymf262_vgm_log(ymf262_context *context, uint32_t master_clock, vgm_writer *vgm)
116 {
117 vgm_ymf262_init(vgm, 8 * master_clock / context->clock_inc);
118 context->vgm = vgm;
119 //TODO: write initial state
120 }
121
122 uint8_t ymf262_read_status(ymf262_context *context, uint32_t cycle, uint32_t port)
123 {
124 if (port) {
125 //TODO: Investigate behavior of invalid status reads
126 return 0xFF;
127 }
128 return context->status;
129 }