Mercurial > repos > blastem
comparison ymf262.c @ 2565:eb588f22ec76
More work on OPL3 emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 26 Jan 2025 23:32:40 -0800 |
parents | 3f58fec775df |
children |
comparison
equal
deleted
inserted
replaced
2564:553a0b4888db | 2565:eb588f22ec76 |
---|---|
54 { | 54 { |
55 context->selected_reg = address; | 55 context->selected_reg = address; |
56 context->selected_part = 0; | 56 context->selected_part = 0; |
57 } | 57 } |
58 | 58 |
59 static void ymf262_update_connections(ymf262_context *context, uint8_t channel, uint8_t csel_bit) | |
60 { | |
61 uint8_t channel_off = channel >= 9 ? channel - 9 : channel; | |
62 uint8_t op = channel_off; | |
63 if (op > 5) { | |
64 op += 6; | |
65 } else if (op > 2) { | |
66 op += 3; | |
67 } | |
68 if (channel >= 9) { | |
69 op += 18; | |
70 } | |
71 if (context->opl3_mode && channel_off < 6 && (context->connection_sel & csel_bit)) { | |
72 if (channel_off > 2) { | |
73 channel -= 3; | |
74 op -= 6; | |
75 } | |
76 uint8_t alg = (context->channels[channel].algorithm & 1) << 1; | |
77 alg |= context->channels[channel + 3].algorithm & 1; | |
78 switch (alg) | |
79 { | |
80 case 0: | |
81 context->operators[op + 3].mod_src[0] = &context->operators[op].output; | |
82 context->operators[op + 6].mod_src[0] = &context->operators[op + 3].output; | |
83 context->operators[op + 9].mod_src[0] = &context->operators[op + 6].output; | |
84 break; | |
85 case 1: | |
86 context->operators[op + 3].mod_src[0] = &context->operators[op].output; | |
87 context->operators[op + 6].mod_src[0] = NULL; | |
88 context->operators[op + 9].mod_src[0] = &context->operators[op + 6].output; | |
89 break; | |
90 case 2: | |
91 context->operators[op + 3].mod_src[0] = NULL; | |
92 context->operators[op + 6].mod_src[0] = &context->operators[op + 3].output; | |
93 context->operators[op + 9].mod_src[0] = &context->operators[op + 6].output; | |
94 break; | |
95 case 3: | |
96 context->operators[op + 3].mod_src[0] = NULL; | |
97 context->operators[op + 6].mod_src[0] = &context->operators[op + 3].output; | |
98 context->operators[op + 9].mod_src[0] = NULL; | |
99 break; | |
100 } | |
101 } else { | |
102 context->operators[op].mod_src[0] = NULL; | |
103 context->operators[op + 3].mod_src[0] = context->channels[channel].algorithm ? NULL : &context->operators[op].output; | |
104 } | |
105 } | |
106 | |
107 void ymf262_calc_phase_inc(ymf262_context *context, ym_channel *channel, ym_operator *operator) | |
108 { | |
109 int32_t inc = channel->fnum; | |
110 //TODO: vibrato? | |
111 if (!channel->block) { | |
112 inc >>= 1; | |
113 } else { | |
114 inc <<= (channel->block-1); | |
115 } | |
116 if (operator->multiple) { | |
117 inc *= operator->multiple; | |
118 inc &= 0xFFFFF; | |
119 } else { | |
120 //0.5 | |
121 inc >>= 1; | |
122 } | |
123 operator->phase_inc = inc; | |
124 } | |
125 | |
59 #define OPL3_NTS 0x08 | 126 #define OPL3_NTS 0x08 |
60 | 127 |
61 void ymf262_data_write(ymf262_context *context, uint8_t value) | 128 void ymf262_data_write(ymf262_context *context, uint8_t value) |
62 { | 129 { |
63 if (!context->selected_reg) { | 130 if (!context->selected_reg) { |
64 return; | 131 return; |
65 } | 132 } |
66 uint8_t old = 0; | 133 uint8_t old = 0; |
67 if (context->selected_reg >= OPL3_PARAM_START && context->selected_reg < OPL3_PARAM_END) { | 134 if (context->selected_reg >= OPL3_PARAM_START && context->selected_reg < OPL3_PARAM_END) { |
135 uint8_t channel, op; | |
68 if (context->selected_part) { | 136 if (context->selected_part) { |
69 old = context->part2_regs[context->selected_reg - OPL3_PARAM_START]; | 137 old = context->part2_regs[context->selected_reg - OPL3_PARAM_START]; |
70 context->part2_regs[context->selected_reg - OPL3_PARAM_START] = value; | 138 context->part2_regs[context->selected_reg - OPL3_PARAM_START] = value; |
139 channel = 9; | |
140 op = 18; | |
71 } else { | 141 } else { |
72 old = context->part1_regs[context->selected_reg - OPL3_PARAM_START]; | 142 old = context->part1_regs[context->selected_reg - OPL3_PARAM_START]; |
73 context->part1_regs[context->selected_reg - OPL3_PARAM_START] = value; | 143 context->part1_regs[context->selected_reg - OPL3_PARAM_START] = value; |
144 channel = 0; | |
145 op = 0; | |
146 } | |
147 if (context->selected_reg < 0xA0 || context->selected_reg >= 0xE0) { | |
148 uint8_t op_off = context->selected_reg & 0x1F; | |
149 if ((op_off >= 0x26 && op_off < 0x28) || (op_off >= 0x2E && op_off < 0x30) || op_off > 0x35) { | |
150 return; | |
151 } | |
152 if (op_off >= 0x30) { | |
153 op_off -= 4; | |
154 } else if (op_off >= 0x28) { | |
155 op_off -= 2; | |
156 } | |
157 op += op_off; | |
158 ym_operator *operator = context->operators + op; | |
159 switch (context->selected_reg & 0xE0) | |
160 { | |
161 case 0x20: | |
162 operator->multiple = value & 0xF; | |
163 operator->rates[PHASE_SUSTAIN] = (value & 0x20) ? 0 : operator->rates[PHASE_RELEASE]; | |
164 operator->am = value & 0x80; | |
165 //TODO: KSR,VIB | |
166 break; | |
167 case 0x40: | |
168 operator->total_level = (value & 0x3F) << 6; | |
169 //TODO: KSL | |
170 break; | |
171 case 0x60: | |
172 //TODO: what should the LSB be? | |
173 operator->rates[PHASE_ATTACK] = (value & 0xF0) >> 3 | 1; | |
174 operator->rates[PHASE_DECAY] = (value & 0xF) << 1 | 1; | |
175 break; | |
176 case 0x80: | |
177 operator->rates[PHASE_RELEASE] = (value & 0xF) << 1 | 1; | |
178 operator->sustain_level = (value & 0xF0) << 3; | |
179 if (operator->sustain_level == 0x780) { | |
180 operator->sustain_level = MAX_ENVELOPE; | |
181 } | |
182 if (!((context->selected_part ? context->part2_regs : context->part1_regs)[context->selected_reg - 0x60] & 0x20)) { | |
183 operator->rates[PHASE_SUSTAIN] = operator->rates[PHASE_RELEASE]; | |
184 } | |
185 break; | |
186 case 0xE0: | |
187 operator->wave = value & (context->opl3_mode ? 0x7 : 0x3); | |
188 break; | |
189 } | |
190 } else { | |
191 uint8_t channel_off = context->selected_reg & 0xF; | |
192 if (channel_off > 8 && context->selected_reg != 0xBD) { | |
193 return; | |
194 } | |
195 uint8_t csel_bit = channel_off > 2 ? channel_off - 3 : channel_off; | |
196 if (channel) { | |
197 csel_bit += 3; | |
198 } | |
199 csel_bit = 1 << csel_bit; | |
200 if (context->selected_reg < 0xC0 && context->opl3_mode && (channel_off > 2 && channel_off < 6) && (context->connection_sel & csel_bit)) { | |
201 //ignore writes to "upper" channel in 4-op mode | |
202 return; | |
203 } | |
204 channel += channel_off; | |
205 op = channel_off; | |
206 if (op > 5) { | |
207 op += 6; | |
208 } else if (op > 2) { | |
209 op += 3; | |
210 } | |
211 ym_channel *chan = context->channels + channel; | |
212 switch(context->selected_reg & 0xF0) | |
213 { | |
214 case 0xA0: | |
215 chan->fnum &= ~0xFF; | |
216 chan->fnum |= value; | |
217 ymf262_calc_phase_inc(context, chan, context->operators + op); | |
218 ymf262_calc_phase_inc(context, chan, context->operators + op + 3); | |
219 if (context->opl3_mode && channel_off < 6 && (context->connection_sel & csel_bit)) { | |
220 //4-op mode | |
221 ymf262_calc_phase_inc(context, chan, context->operators + op + 6); | |
222 ymf262_calc_phase_inc(context, chan, context->operators + op + 9); | |
223 } | |
224 break; | |
225 case 0xB0: | |
226 chan->fnum &= 0xFF; | |
227 chan->fnum |= (value & 0x3) << 8; | |
228 chan->block = (value >> 2) & 7; | |
229 ymf262_calc_phase_inc(context, chan, context->operators + op); | |
230 ymf262_calc_phase_inc(context, chan, context->operators + op + 3); | |
231 if (context->opl3_mode && channel_off < 6 && (context->connection_sel & csel_bit)) { | |
232 //4-op mode | |
233 ymf262_calc_phase_inc(context, chan, context->operators + op + 6); | |
234 ymf262_calc_phase_inc(context, chan, context->operators + op + 9); | |
235 } | |
236 if ((value ^ old) & 0x20) { | |
237 if (value & 0x20) { | |
238 keyon(context->operators + op, chan); | |
239 keyon(context->operators + op + 3, chan); | |
240 if (context->opl3_mode && channel_off < 6 && (context->connection_sel & csel_bit)) { | |
241 //4-op mode | |
242 keyon(context->operators + op + 6, chan); | |
243 keyon(context->operators + op + 9, chan); | |
244 } | |
245 } else { | |
246 keyoff(context->operators + op); | |
247 keyoff(context->operators + op + 3); | |
248 if (context->opl3_mode && channel_off < 6 && (context->connection_sel & csel_bit)) { | |
249 //4-op mode | |
250 keyoff(context->operators + op + 6); | |
251 keyoff(context->operators + op + 9); | |
252 } | |
253 } | |
254 } | |
255 break; | |
256 case 0xC0: | |
257 chan->algorithm = value & 1; | |
258 chan->feedback = value >> 1 & 0x7; | |
259 chan->lr = value & 0xF0; | |
260 ymf262_update_connections(context, channel, csel_bit); | |
261 break; | |
262 } | |
74 } | 263 } |
75 } else if (context->selected_part) { | 264 } else if (context->selected_part) { |
76 if (context->selected_reg <= sizeof(context->timer_test)) { | 265 if (context->selected_reg <= sizeof(context->timer_test)) { |
77 old = context->timer_test[context->selected_reg - 1]; | 266 old = context->timer_test[context->selected_reg - 1]; |
78 context->timer_test[context->selected_reg - 1] = value; | 267 context->timer_test[context->selected_reg - 1] = value; |
81 context->nts = value; | 270 context->nts = value; |
82 } else { | 271 } else { |
83 return; | 272 return; |
84 } | 273 } |
85 } else { | 274 } else { |
275 | |
86 switch (context->selected_reg) | 276 switch (context->selected_reg) |
87 { | 277 { |
88 case 0x01: | 278 case 0x01: |
89 old = context->part2_test; | 279 old = context->part2_test; |
90 context->part2_test = value; | 280 context->part2_test = value; |
91 break; | 281 break; |
92 case 0x04: | 282 case 0x04: |
93 old = context->connection_sel; | 283 old = context->connection_sel; |
94 context->connection_sel = value; | 284 context->connection_sel = value; |
285 if (context->opl3_mode) { | |
286 uint8_t changes = old ^ value; | |
287 for (uint8_t i = 0; i < 6; i++) | |
288 { | |
289 uint8_t csel_bit = 1 << i; | |
290 if (changes & csel_bit) { | |
291 uint8_t channel = i > 2 ? i + 9 : i; | |
292 if (value & csel_bit) { | |
293 //switched to 4-op mode | |
294 ymf262_update_connections(context, channel, csel_bit); | |
295 } else { | |
296 //switched to 2-op mode | |
297 ymf262_update_connections(context, channel, csel_bit); | |
298 ymf262_update_connections(context, channel + 3, csel_bit); | |
299 } | |
300 } | |
301 } | |
302 } | |
95 break; | 303 break; |
96 case 0x05: | 304 case 0x05: |
97 old = context->opl3_mode; | 305 old = context->opl3_mode; |
98 context->opl3_mode = value; | 306 context->opl3_mode = value; |
99 break; | 307 break; |