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
)
19 sid
= ((a
& 0x02) >> 1);
20 if ((a
& 0x01) == 0) {
25 vgm_dump_ym2612(sid
, fm_sel
[sid
], v
);
27 if (fm_sel
[sid
] == 0x2a) {
28 dac_submit((uint8_t)v
);
31 if (fm_sel
[sid
] == 0x2b) {
32 dac_enable((uint8_t)v
);
35 if (fm_sel
[sid
] == 0x27) {
36 unsigned int now
= frame_usecs();
38 if ((v
& 0x01) && ((fm_reg
[0][0x27] & 0x01) == 0)) {
43 if ((v
& 0x02) && ((fm_reg
[0][0x27] & 0x02) == 0)) {
48 // (v & 0x04) enable/disable timer A
49 // (v & 0x08) enable/disable timer B
54 fm_reg
[0][0x27] &= ~0x10;
60 fm_reg
[0][0x27] &= ~0x20;
64 fm_reg
[sid
][(fm_sel
[sid
])] = v
;
76 int md::myfm_read(int a
)
79 return (fm_tover
| (YM2612Read(0, (a
& 3)) & ~0x03));
82 int md::mysn_write(int d
)
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]);
103 if (fm_ticker
[0] >= amax
) {
104 if (fm_reg
[0][0x27] & 0x04)
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]);
112 if (fm_ticker
[2] >= bmax
) {
113 if (fm_reg
[0][0x27] & 0x08)
115 fm_ticker
[2] -= bmax
;
123 memset(fm_sel
, 0, sizeof(fm_sel
));
125 memset(fm_ticker
, 0, sizeof(fm_ticker
));
126 memset(fm_reg
, 0, sizeof(fm_reg
));
133 (((pal
) ? PAL_MCLK
: NTSC_MCLK
) / 15),
142 memset(dac_data
, 0xff, sizeof(dac_data
));
146 static const struct {
147 unsigned int samples
;
150 { (44100 / 60), (1000000 / 60) },
151 { (44100 / 50), (1000000 / 50) },
154 void md::dac_submit(uint8_t d
)
162 if (dac_len
== elemof(dac_data
))
164 usecs
= frame_usecs();
165 index
= ((usecs
<< 10) /
166 ((per_frame
[pal
].usecs
<< 10) /
168 if (index
>= elemof(dac_data
))
172 d
= dac_data
[dac_len
- 1];
173 for (i
= dac_len
; (i
< index
); ++i
)
175 dac_len
= (index
+ 1);
178 void md::dac_enable(uint8_t d
)
180 dac_enabled
= ((d
& 0x80) >> 7);
185 void md::vgm_dump_ym2612(uint8_t a1
, uint8_t reg
, uint8_t data
)
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
;
196 if (usecs
> per_frame
[pal
].usecs
)
197 usecs
= per_frame
[pal
].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
)
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
;
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 };
251 if (vgm_dump
== true)
253 vgm_dump_file
= dgen_fopen("vgm", name
, DGEN_WRITE
);
254 if (vgm_dump_file
== NULL
)
256 // 0x00: file identifier.
257 memcpy(&buf
[0x00], "Vgm ", 4);
258 // 0x04: EoF offset. Not known yet.
259 // 0x08: version number (1.70).
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.
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.
274 // 0x2b: SN76489 flags.
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);
283 if (fwrite(buf
, sizeof(buf
), 1, vgm_dump_file
) != 1)
285 // Dump YM2612 registers directly.
286 YM2612_dump(0, ym2612_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)
301 uint8_t buf
[] = { 0x52, 0x2b, (uint8_t)(dac_enabled
<< 7) };
303 if (fwrite(buf
, sizeof(buf
), 1, vgm_dump_file
) != 1)
307 for (i
= 0x30; (i
!= 0x9e); ++i
) {
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)
317 for (i
= 0xb0; (i
!= 0xb6); ++i
) {
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)
326 vgm_dump_samples_total
= 0;
327 vgm_dump_dac_wait
= 0;
328 vgm_dump_dac_samples
= 0;
333 fclose(vgm_dump_file
);
334 vgm_dump_file
= NULL
;
339 void md::vgm_dump_stop()
346 // Append end of sound data.
347 fputc(0x66, vgm_dump_file
);
348 pos
= ftell(vgm_dump_file
);
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;
365 #endif // WITH_VGMDUMP