1 // DGen v1.29
2 
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"
9 
10 // REMEMBER NOT TO USE ANY STATIC variables, because they
11 // will exist thoughout ALL megadrives!
myfm_write(int a,int v,int md)12 int md::myfm_write(int a, int v, int md)
13 {
14 	int sid = 0;
15 	int pass = 1;
16 
17 	(void)md;
18 	a &= 3;
19 	sid = ((a & 0x02) >> 1);
20 	if ((a & 0x01) == 0) {
21 		fm_sel[sid] = v;
22 		goto end;
23 	}
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;
30 	}
31 	if (fm_sel[sid] == 0x2b) {
32 		dac_enable((uint8_t)v);
33 		pass = 0;
34 	}
35 	if (fm_sel[sid] == 0x27) {
36 		unsigned int now = frame_usecs();
37 
38 		if ((v & 0x01) && ((fm_reg[0][0x27] & 0x01) == 0)) {
39 			// load timer A
40 			fm_ticker[0] = 0;
41 			fm_ticker[1] = now;
42 		}
43 		if ((v & 0x02) && ((fm_reg[0][0x27] & 0x02) == 0)) {
44 			// load timer B
45 			fm_ticker[2] = 0;
46 			fm_ticker[3] = now;
47 		}
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;
55 		}
56 		if (v & 0x20) {
57 			// reset overflow B
58 			fm_tover &= ~0x02;
59 			v &= ~0x20;
60 			fm_reg[0][0x27] &= ~0x20;
61 		}
62 	}
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);
71 		}
72 	}
73 	return 0;
74 }
75 
myfm_read(int a)76 int md::myfm_read(int a)
77 {
78 	fm_timer_callback();
79 	return (fm_tover | (YM2612Read(0, (a & 3)) & ~0x03));
80 }
81 
mysn_write(int d)82 int md::mysn_write(int d)
83 {
84 #ifdef WITH_VGMDUMP
85 	vgm_dump_sn76496(d);
86 #endif
87 	SN76496Write(0, d);
88 	return 0;
89 }
90 
fm_timer_callback()91 int md::fm_timer_callback()
92 {
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();
99 
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;
107 		}
108 	}
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;
116 		}
117 	}
118 	return 0;
119 }
120 
fm_reset()121 void md::fm_reset()
122 {
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);
131 	}
132 	SN76496_init(0,
133 		     (((pal) ? PAL_MCLK : NTSC_MCLK) / 15),
134 		     dgen_soundrate, 16);
135 }
136 
dac_init()137 void md::dac_init()
138 {
139 	dac_enabled = true;
140 	dac_len = 0;
141 #ifndef NDEBUG
142 	memset(dac_data, 0xff, sizeof(dac_data));
143 #endif
144 }
145 
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) },
152 };
153 
dac_submit(uint8_t d)154 void md::dac_submit(uint8_t d)
155 {
156 	unsigned int usecs;
157 	unsigned int index;
158 	unsigned int i;
159 
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);
176 }
177 
dac_enable(uint8_t d)178 void md::dac_enable(uint8_t d)
179 {
180 	dac_enabled = ((d & 0x80) >> 7);
181 }
182 
183 #ifdef WITH_VGMDUMP
184 
vgm_dump_ym2612(uint8_t a1,uint8_t reg,uint8_t data)185 void md::vgm_dump_ym2612(uint8_t a1, uint8_t reg, uint8_t data)
186 {
187 	if (vgm_dump) {
188 		uint8_t buf[] = { (uint8_t)(0x52 + a1), reg, data };
189 
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;
195 
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;
205 			}
206 			vgm_dump_dac_samples = samples;
207 		}
208 	}
209 }
210 
vgm_dump_sn76496(uint8_t data)211 void md::vgm_dump_sn76496(uint8_t data)
212 {
213 	if (vgm_dump) {
214 		uint8_t buf[] = { 0x50, data };
215 
216 		fwrite(buf, sizeof(buf), 1, vgm_dump_file);
217 	}
218 }
219 
vgm_dump_frame()220 void md::vgm_dump_frame()
221 {
222 	unsigned int max = per_frame[pal].samples;
223 
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);
229 
230 		memcpy(&buf[1], &tmp, sizeof(tmp));
231 		fwrite(buf, sizeof(buf), 1, vgm_dump_file);
232 	}
233 	vgm_dump_samples_total += max;
234 	vgm_dump_dac_wait = 0;
235 	vgm_dump_dac_samples = 0;
236 }
237 
238 // Generate VGM 1.70 header as defined by:
239 // http://www.smspower.org/uploads/Music/vgmspec170.txt
vgm_dump_start(const char * name)240 int md::vgm_dump_start(const char *name)
241 {
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;
250 
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.
288 	{
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],
294 		};
295 
296 		if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
297 			goto error;
298 	}
299 	// DAC.
300 	{
301 		uint8_t buf[] = { 0x52, 0x2b, (uint8_t)(dac_enabled << 7) };
302 
303 		if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
304 			goto error;
305 	}
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],
311 		};
312 
313 		if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
314 			goto error;
315 	}
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],
321 		};
322 
323 		if (fwrite(buf, sizeof(buf), 1, vgm_dump_file) != 1)
324 			goto error;
325 	}
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;
337 }
338 
vgm_dump_stop()339 void md::vgm_dump_stop()
340 {
341 	long pos;
342 	uint32_t tmp;
343 
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;
363 }
364 
365 #endif // WITH_VGMDUMP
366