Add VCS links
[debian-dgen.git] / myfm.cpp
blob6660dbdb66bd3e1fd317bb5bfe51c074c33a4a6b
1 // DGen v1.29
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include "md.h"
8 #include "rc-vars.h"
10 // REMEMBER NOT TO USE ANY STATIC variables, because they
11 // will exist thoughout ALL megadrives!
12 int md::myfm_write(int a, int v, int md)
14 int sid = 0;
15 int pass = 1;
17 (void)md;
18 a &= 3;
19 sid = ((a & 0x02) >> 1);
20 if ((a & 0x01) == 0) {
21 fm_sel[sid] = v;
22 goto end;
24 #ifdef WITH_VGMDUMP
25 vgm_dump_ym2612(sid, fm_sel[sid], v);
26 #endif
27 if (fm_sel[sid] == 0x2a) {
28 dac_submit((uint8_t)v);
29 pass = 0;
31 if (fm_sel[sid] == 0x2b) {
32 dac_enable((uint8_t)v);
33 pass = 0;
35 if (fm_sel[sid] == 0x27) {
36 unsigned int now = frame_usecs();
38 if ((v & 0x01) && ((fm_reg[0][0x27] & 0x01) == 0)) {
39 // load timer A
40 fm_ticker[0] = 0;
41 fm_ticker[1] = now;
43 if ((v & 0x02) && ((fm_reg[0][0x27] & 0x02) == 0)) {
44 // load timer B
45 fm_ticker[2] = 0;
46 fm_ticker[3] = now;
48 // (v & 0x04) enable/disable timer A
49 // (v & 0x08) enable/disable timer B
50 if (v & 0x10) {
51 // reset overflow A
52 fm_tover &= ~0x01;
53 v &= ~0x10;
54 fm_reg[0][0x27] &= ~0x10;
56 if (v & 0x20) {
57 // reset overflow B
58 fm_tover &= ~0x02;
59 v &= ~0x20;
60 fm_reg[0][0x27] &= ~0x20;
63 // stash all values
64 fm_reg[sid][(fm_sel[sid])] = v;
65 end:
66 if (pass) {
67 YM2612Write(0, a, v);
68 if (dgen_mjazz) {
69 YM2612Write(1, a, v);
70 YM2612Write(2, a, v);
73 return 0;
76 int md::myfm_read(int a)
78 fm_timer_callback();
79 return (fm_tover | (YM2612Read(0, (a & 3)) & ~0x03));
82 int md::mysn_write(int d)
84 #ifdef WITH_VGMDUMP
85 vgm_dump_sn76496(d);
86 #endif
87 SN76496Write(0, d);
88 return 0;
91 int md::fm_timer_callback()
93 // periods in microseconds for timers A and B
94 int amax = (18 * (1024 -
95 (((fm_reg[0][0x24] << 2) |
96 (fm_reg[0][0x25] & 0x03)) & 0x3ff)));
97 int bmax = (288 * (256 - (fm_reg[0][0x26] & 0xff)));
98 unsigned int now = frame_usecs();
100 if ((fm_reg[0][0x27] & 0x01) && ((now - fm_ticker[1]) > 0)) {
101 fm_ticker[0] += (now - fm_ticker[1]);
102 fm_ticker[1] = now;
103 if (fm_ticker[0] >= amax) {
104 if (fm_reg[0][0x27] & 0x04)
105 fm_tover |= 0x01;
106 fm_ticker[0] -= amax;
109 if ((fm_reg[0][0x27] & 0x02) && ((now - fm_ticker[3]) > 0)) {
110 fm_ticker[2] += (now - fm_ticker[3]);
111 fm_ticker[3] = now;
112 if (fm_ticker[2] >= bmax) {
113 if (fm_reg[0][0x27] & 0x08)
114 fm_tover |= 0x02;
115 fm_ticker[2] -= bmax;
118 return 0;
121 void md::fm_reset()
123 memset(fm_sel, 0, sizeof(fm_sel));
124 fm_tover = 0x00;
125 memset(fm_ticker, 0, sizeof(fm_ticker));
126 memset(fm_reg, 0, sizeof(fm_reg));
127 YM2612ResetChip(0);
128 if (dgen_mjazz) {
129 YM2612ResetChip(1);
130 YM2612ResetChip(2);
132 SN76496_init(0,
133 (((pal) ? PAL_MCLK : NTSC_MCLK) / 15),
134 dgen_soundrate, 16);
137 void md::dac_init()
139 dac_enabled = true;
140 dac_len = 0;
141 #ifndef NDEBUG
142 memset(dac_data, 0xff, sizeof(dac_data));
143 #endif
146 static const struct {
147 unsigned int samples;
148 unsigned int usecs;
149 } per_frame[2] = {
150 { (44100 / 60), (1000000 / 60) },
151 { (44100 / 50), (1000000 / 50) },
154 void md::dac_submit(uint8_t d)
156 unsigned int usecs;
157 unsigned int index;
158 unsigned int i;
160 if (!dac_enabled)
161 return;
162 if (dac_len == elemof(dac_data))
163 return;
164 usecs = frame_usecs();
165 index = ((usecs << 10) /
166 ((per_frame[pal].usecs << 10) /
167 elemof(dac_data)));
168 if (index >= elemof(dac_data))
169 return;
170 dac_data[index] = d;
171 if (dac_len)
172 d = dac_data[dac_len - 1];
173 for (i = dac_len; (i < index); ++i)
174 dac_data[i] = d;
175 dac_len = (index + 1);
178 void md::dac_enable(uint8_t d)
180 dac_enabled = ((d & 0x80) >> 7);
183 #ifdef WITH_VGMDUMP
185 void md::vgm_dump_ym2612(uint8_t a1, uint8_t reg, uint8_t data)
187 if (vgm_dump) {
188 uint8_t buf[] = { (uint8_t)(0x52 + a1), reg, data };
190 fwrite(buf, sizeof(buf), 1, vgm_dump_file);
191 if ((a1 == 0) && (reg == 0x2a)) {
192 unsigned int usecs = frame_usecs();
193 unsigned int samples;
194 unsigned int diff;
196 if (usecs > per_frame[pal].usecs)
197 usecs = per_frame[pal].usecs;
198 samples = ((usecs *
199 ((per_frame[pal].samples << 20) /
200 per_frame[pal].usecs)) >> 20);
201 diff = (samples - vgm_dump_dac_samples);
202 if ((diff > 0) && (diff <= 16)) {
203 fputc((0x70 + (diff - 1)), vgm_dump_file);
204 vgm_dump_dac_wait += diff;
206 vgm_dump_dac_samples = samples;
211 void md::vgm_dump_sn76496(uint8_t data)
213 if (vgm_dump) {
214 uint8_t buf[] = { 0x50, data };
216 fwrite(buf, sizeof(buf), 1, vgm_dump_file);
220 void md::vgm_dump_frame()
222 unsigned int max = per_frame[pal].samples;
224 if (!vgm_dump)
225 return;
226 if (vgm_dump_dac_wait < max) {
227 uint8_t buf[] = { 0x61, 0x00, 0x00 };
228 uint16_t tmp = h2le16(max - vgm_dump_dac_wait);
230 memcpy(&buf[1], &tmp, sizeof(tmp));
231 fwrite(buf, sizeof(buf), 1, vgm_dump_file);
233 vgm_dump_samples_total += max;
234 vgm_dump_dac_wait = 0;
235 vgm_dump_dac_samples = 0;
238 // Generate VGM 1.70 header as defined by:
239 // http://www.smspower.org/uploads/Music/vgmspec170.txt
240 int md::vgm_dump_start(const char *name)
242 uint8_t ym2612_buf[0x200];
243 uint8_t buf[0x100] = { 0 };
244 union {
245 uint32_t u32;
246 uint16_t u16;
247 } tmp;
248 unsigned int i;
249 int err;
251 if (vgm_dump == true)
252 vgm_dump_stop();
253 vgm_dump_file = dgen_fopen("vgm", name, DGEN_WRITE);
254 if (vgm_dump_file == NULL)
255 return -1;
256 // 0x00: file identifier.
257 memcpy(&buf[0x00], "Vgm ", 4);
258 // 0x04: EoF offset. Not known yet.
259 // 0x08: version number (1.70).
260 tmp.u32 = 0x0170;
261 memcpy(&buf[0x08], &tmp.u32, 4);
262 // 0x0c: SN76489 (PSG) clock.
263 tmp.u32 = h2le32(clk0);
264 memcpy(&buf[0x0c], &tmp.u32, 4);
265 // 0x18: total # samples. Not known yet.
266 // 0x24: rate.
267 tmp.u32 = h2le32(vhz);
268 memcpy(&buf[0x24], &tmp.u32, 4);
269 // 0x28: SN76489 (PSG) feedback.
270 tmp.u16 = h2le16(0x0009);
271 memcpy(&buf[0x28], &tmp.u16, 2);
272 // 0x2a: SN76489 shift register width.
273 buf[0x2a] = 16;
274 // 0x2b: SN76489 flags.
275 buf[0x2b] = 0x00;
276 // 0x2c: YM2612 clock.
277 tmp.u32 = h2le32(clk1);
278 memcpy(&buf[0x2c], &tmp.u32, 4);
279 // 0x34: VGM data offset.
280 tmp.u32 = h2le32(sizeof(buf) - 0x34);
281 memcpy(&buf[0x34], &tmp.u32, 4);
282 // Dump VGM header.
283 if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
284 goto error;
285 // Dump YM2612 registers directly.
286 YM2612_dump(0, ym2612_buf);
287 // Timers.
289 uint8_t buf[] = {
290 0x52, 0x24, (uint8_t)fm_reg[0][0x24],
291 0x52, 0x25, (uint8_t)fm_reg[0][0x25],
292 0x52, 0x26, (uint8_t)fm_reg[0][0x26],
293 0x52, 0x27, (uint8_t)fm_reg[0][0x27],
296 if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
297 goto error;
299 // DAC.
301 uint8_t buf[] = { 0x52, 0x2b, (uint8_t)(dac_enabled << 7) };
303 if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
304 goto error;
306 // FM CH1-CH3.
307 for (i = 0x30; (i != 0x9e); ++i) {
308 uint8_t buf[] = {
309 0x52, (uint8_t)i, ym2612_buf[i],
310 0x53, (uint8_t)i, ym2612_buf[i | 0x100],
313 if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
314 goto error;
316 // FM CH4-CH6.
317 for (i = 0xb0; (i != 0xb6); ++i) {
318 uint8_t buf[] = {
319 0x52, (uint8_t)i, ym2612_buf[i],
320 0x53, (uint8_t)i, ym2612_buf[i | 0x100],
323 if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
324 goto error;
326 vgm_dump_samples_total = 0;
327 vgm_dump_dac_wait = 0;
328 vgm_dump_dac_samples = 0;
329 vgm_dump = true;
330 return 0;
331 error:
332 err = errno;
333 fclose(vgm_dump_file);
334 vgm_dump_file = NULL;
335 errno = err;
336 return -1;
339 void md::vgm_dump_stop()
341 long pos;
342 uint32_t tmp;
344 if (!vgm_dump)
345 return;
346 // Append end of sound data.
347 fputc(0x66, vgm_dump_file);
348 pos = ftell(vgm_dump_file);
349 // Fill EoF offset.
350 fseek(vgm_dump_file, 0x04, SEEK_SET);
351 tmp = h2le32(pos - 4);
352 fwrite(&tmp, sizeof(tmp), 1, vgm_dump_file);
353 // Fill total number of samples.
354 fseek(vgm_dump_file, 0x18, SEEK_SET);
355 tmp = h2le32(vgm_dump_samples_total);
356 fwrite(&tmp, sizeof(tmp), 1, vgm_dump_file);
357 fclose(vgm_dump_file);
358 vgm_dump_file = NULL;
359 vgm_dump_samples_total = 0;
360 vgm_dump_dac_wait = 0;
361 vgm_dump_dac_samples = 0;
362 vgm_dump = false;
365 #endif // WITH_VGMDUMP