initial import
[picodrive.git] / Pico / sound / sound.c
1 // This is part of Pico Library\r
2 \r
3 // (c) Copyright 2004 Dave, All rights reserved.\r
4 // (c) Copyright 2006 notaz, All rights reserved.\r
5 // Free for non-commercial use.\r
6 \r
7 // For commercial use, separate licencing terms must be obtained.\r
8 \r
9 \r
10 #include <string.h>\r
11 #include "sound.h"\r
12 #include "ym2612.h"\r
13 #include "sn76496.h"\r
14 \r
15 #ifndef __GNUC__\r
16 #pragma warning (disable:4244)\r
17 #endif\r
18 \r
19 #if defined(_USE_MZ80)\r
20 #include "../../cpu/mz80/mz80.h"\r
21 #elif defined(_USE_DRZ80)\r
22 #include "../../cpu/DrZ80/drz80.h"\r
23 #endif\r
24 \r
25 #include "../PicoInt.h"\r
26 \r
27 \r
28 //int z80CycleAim = 0;\r
29 \r
30 // dac\r
31 short *dac_out;\r
32 unsigned short dac_info[312]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample\r
33 \r
34 // for Pico\r
35 int PsndRate=0;\r
36 int PsndLen=0; // number of mono samples, multiply by 2 for stereo\r
37 short *PsndOut=NULL; // PCM data buffer\r
38 \r
39 // from ym2612.c\r
40 extern int   *ym2612_dacen;\r
41 extern INT32 *ym2612_dacout;\r
42 void YM2612TimerHandler(int c,int cnt);\r
43 \r
44 // sn76496\r
45 extern int *sn76496_regs;\r
46 \r
47 \r
48 static void dac_recalculate()\r
49 {\r
50   int i, dac_cnt, pos, len, lines = Pico.m.pal ? 312 : 262, mid = Pico.m.pal ? 68 : 93;\r
51 \r
52   if(PsndLen <= lines) {\r
53     // shrinking algo\r
54     dac_cnt = 0;//lines - PsndLen;\r
55     len=1; pos=0;\r
56     dac_info[225] = 1;\r
57 \r
58     for(i=226; i != 225; i++) {\r
59       if (i >= lines) i = 0;\r
60       len = 0;\r
61       if(dac_cnt < 0) {\r
62         len=1;\r
63         pos++;\r
64         dac_cnt += lines;\r
65       }\r
66       dac_cnt -= PsndLen;\r
67       dac_info[i] = (pos<<4)|len;\r
68     }\r
69   } else {\r
70     // stretching\r
71     dac_cnt = PsndLen/2;\r
72     pos=0;\r
73     for(i = 225; i != 224; i++) {\r
74       if (i >= lines) i = 0;\r
75       len=0;\r
76       while(dac_cnt >= 0) {\r
77         dac_cnt -= lines;\r
78         len++;\r
79       }\r
80           if (i == mid) // midpoint\r
81         while(pos+len < PsndLen/2) {\r
82           dac_cnt -= lines;\r
83           len++;\r
84         }\r
85       dac_cnt += PsndLen;\r
86       dac_info[i] = (pos<<4)|len;\r
87       pos+=len;\r
88     }\r
89     // last sample\r
90     for(len = 0, i = pos; i < PsndLen; i++) len++;\r
91     dac_info[224] = (pos<<4)|len;\r
92   }\r
93 //  dprintf("rate is %i, len %i", PsndRate, PsndLen);\r
94 //  for(i=0; i < lines; i++)\r
95 //    dprintf("%03i : %03i : %i", i, dac_info[i]>>4, dac_info[i]&0xf);\r
96 //exit(8);\r
97 }\r
98 \r
99 \r
100 void sound_reset()\r
101 {\r
102   extern int z80stopCycle;\r
103   void *ym2612_regs;\r
104 \r
105   // init even if we are not going to use them, just in case we ever enable it\r
106   YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);\r
107   // also clear the internal registers+addr line\r
108   ym2612_regs = YM2612GetRegs();\r
109   memset(ym2612_regs, 0, 0x200+4);\r
110 \r
111   SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);\r
112 \r
113   // calculate PsndLen\r
114   PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);\r
115 \r
116   // recalculate dac info\r
117   dac_recalculate();\r
118   z80stopCycle = 0;\r
119 }\r
120 \r
121 \r
122 // to be called after changing sound rate or chips\r
123 void sound_rerate()\r
124 {\r
125   unsigned int state[28];\r
126 \r
127   YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);\r
128   // feed it back it's own registers, just like after loading state\r
129   YM2612PicoStateLoad();\r
130 \r
131   memcpy(state, sn76496_regs, 28*4); // remember old state\r
132   SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);\r
133   memcpy(sn76496_regs, state, 28*4); // restore old state\r
134 \r
135   // calculate PsndLen\r
136   PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);\r
137 \r
138   // recalculate dac info\r
139   dac_recalculate();\r
140 }\r
141 \r
142 \r
143 // This is called once per raster (aka line), but not necessarily for every line\r
144 int sound_timers_and_dac(int raster)\r
145 {\r
146   if(raster >= 0 && PsndOut && (PicoOpt&1) && *ym2612_dacen) {\r
147     short dout = (short) *ym2612_dacout;\r
148     int pos=dac_info[raster], len=pos&0xf;\r
149     short *d;\r
150     pos>>=4;\r
151 \r
152     if(PicoOpt&8) { // only left channel for stereo (will be copied to right by ym2612 mixing code)\r
153       d=PsndOut+pos*2;\r
154       while(len--) { *d = dout; d += 2; }\r
155     } else {\r
156       d=PsndOut+pos;\r
157       while(len--) *d++ = dout;\r
158     }\r
159   }\r
160 \r
161   //dprintf("s: %03i", raster);\r
162 \r
163   // Our raster lasts 63.61323/64.102564 microseconds (NTSC/PAL)\r
164   YM2612PicoTick(1);\r
165 \r
166   return 0;\r
167 }\r
168 \r
169 \r
170 int sound_render(int offset, int length)\r
171 {\r
172   int stereo = (PicoOpt & 8) >> 3;\r
173   offset <<= stereo;\r
174 \r
175   // PSG\r
176   if(PicoOpt & 2)\r
177     SN76496Update(PsndOut+offset, length, stereo);\r
178 \r
179   // Add in the stereo FM buffer\r
180   if(PicoOpt & 1) {\r
181     YM2612UpdateOne(PsndOut+offset, length, stereo);\r
182   } else {\r
183     // YM2612 upmixes to stereo, so we have to do this manually here\r
184     int i;\r
185         short *s = PsndOut+offset;\r
186         for (i = 0; i < length; i++) {\r
187       *(s+1) = *s; s+=2;\r
188     }\r
189   }\r
190 \r
191   return 0;\r
192 }\r
193 \r
194 \r
195 \r
196 #if defined(_USE_MZ80)\r
197 \r
198 // memhandlers for mz80 core\r
199 unsigned char mz80_read(UINT32 a,  struct MemoryReadByte *w)  { return z80_read(a); }\r
200 void mz80_write(UINT32 a, UINT8 d, struct MemoryWriteByte *w) { z80_write(d, a); }\r
201 \r
202 // structures for mz80 core\r
203 static struct MemoryReadByte mz80_mem_read[]=\r
204 {\r
205   {0x0000,0xffff,mz80_read},\r
206   {(UINT32) -1,(UINT32) -1,NULL}\r
207 };\r
208 static struct MemoryWriteByte mz80_mem_write[]=\r
209 {\r
210   {0x0000,0xffff,mz80_write},\r
211   {(UINT32) -1,(UINT32) -1,NULL}\r
212 };\r
213 static struct z80PortRead mz80_io_read[] ={\r
214   {(UINT16) -1,(UINT16) -1,NULL}\r
215 };\r
216 static struct z80PortWrite mz80_io_write[]={\r
217   {(UINT16) -1,(UINT16) -1,NULL}\r
218 };\r
219 \r
220 #elif defined(_USE_DRZ80)\r
221 \r
222 static struct DrZ80 drZ80;\r
223 \r
224 static unsigned int DrZ80_rebasePC(unsigned short a)\r
225 {\r
226   drZ80.Z80PC_BASE = (unsigned int) Pico.zram;\r
227   return drZ80.Z80PC_BASE + a;\r
228 }\r
229 \r
230 static unsigned int DrZ80_rebaseSP(unsigned short a)\r
231 {\r
232   drZ80.Z80SP_BASE = (unsigned int) Pico.zram;\r
233   return drZ80.Z80SP_BASE + a;\r
234 }\r
235 \r
236 static unsigned char DrZ80_in(unsigned short p)\r
237 {\r
238   return 0xff;\r
239 }\r
240 \r
241 static void DrZ80_out(unsigned short p,unsigned char d)\r
242 {\r
243 }\r
244 \r
245 static void DrZ80_irq_callback()\r
246 {\r
247   drZ80.Z80_IRQ = 0; // lower irq when accepted\r
248 }\r
249 \r
250 #endif\r
251 \r
252 // z80 functionality wrappers\r
253 void z80_init()\r
254 {\r
255 #if defined(_USE_MZ80)\r
256   struct mz80context z80;\r
257 \r
258   // z80\r
259   mz80init();\r
260   // Modify the default context\r
261   mz80GetContext(&z80);\r
262   \r
263   // point mz80 stuff\r
264   z80.z80Base=Pico.zram;\r
265   z80.z80MemRead=mz80_mem_read;\r
266   z80.z80MemWrite=mz80_mem_write;\r
267   z80.z80IoRead=mz80_io_read;\r
268   z80.z80IoWrite=mz80_io_write;\r
269   \r
270   mz80SetContext(&z80);\r
271 \r
272 #elif defined(_USE_DRZ80)\r
273 \r
274   memset(&drZ80, 0, sizeof(struct DrZ80));\r
275   drZ80.z80_rebasePC=DrZ80_rebasePC;\r
276   drZ80.z80_rebaseSP=DrZ80_rebaseSP;\r
277   drZ80.z80_read8   =z80_read;\r
278   drZ80.z80_read16  =z80_read16;\r
279   drZ80.z80_write8  =z80_write;\r
280   drZ80.z80_write16 =z80_write16;\r
281   drZ80.z80_in      =DrZ80_in;\r
282   drZ80.z80_out     =DrZ80_out;\r
283   drZ80.z80_irq_callback=DrZ80_irq_callback;\r
284 #endif\r
285 }\r
286 \r
287 void z80_reset()\r
288 {\r
289 #if defined(_USE_MZ80)\r
290   mz80reset();\r
291 #elif defined(_USE_DRZ80)\r
292   memset(&drZ80, 0, 0x54);\r
293   drZ80.Z80F  = (1<<2);  // set ZFlag\r
294   drZ80.Z80F2 = (1<<2);  // set ZFlag\r
295   drZ80.Z80IX = 0xFFFF << 16;\r
296   drZ80.Z80IY = 0xFFFF << 16;\r
297   drZ80.Z80IM = 0; // 1?\r
298   drZ80.Z80PC = drZ80.z80_rebasePC(0);\r
299   drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ?\r
300 #endif\r
301   Pico.m.z80_fakeval = 0; // for faking when Z80 is disabled\r
302 }\r
303 \r
304 void z80_resetCycles()\r
305 {\r
306 #if defined(_USE_MZ80)\r
307   mz80GetElapsedTicks(1);\r
308 #endif\r
309 }\r
310 \r
311 void z80_int()\r
312 {\r
313 #if defined(_USE_MZ80)\r
314   mz80int(0);\r
315 #elif defined(_USE_DRZ80)\r
316   drZ80.z80irqvector = 0xFF; // default IRQ vector RST opcode\r
317   drZ80.Z80_IRQ = 1;\r
318 #endif\r
319 }\r
320 \r
321 // returns number of cycles actually executed\r
322 int z80_run(int cycles)\r
323 {\r
324 #if defined(_USE_MZ80)\r
325   int ticks_pre = mz80GetElapsedTicks(0);\r
326   mz80exec(cycles);\r
327   return mz80GetElapsedTicks(0) - ticks_pre;\r
328 #elif defined(_USE_DRZ80)\r
329   return cycles - DrZ80Run(&drZ80, cycles);\r
330 #else\r
331   return cycles;\r
332 #endif\r
333 }\r
334 \r
335 void z80_pack(unsigned char *data)\r
336 {\r
337 #if defined(_USE_MZ80)\r
338   struct mz80context mz80;\r
339   *(int *)data = 0x00005A6D; // "mZ"\r
340   mz80GetContext(&mz80);\r
341   memcpy(data+4, &mz80.z80clockticks, sizeof(mz80)-5*4); // don't save base&memhandlers\r
342 #elif defined(_USE_DRZ80)\r
343   *(int *)data = 0x015A7244; // "DrZ" v1\r
344   drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);\r
345   drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);\r
346   memcpy(data+4, &drZ80, 0x54);\r
347 #endif\r
348 }\r
349 \r
350 void z80_unpack(unsigned char *data)\r
351 {\r
352 #if defined(_USE_MZ80)\r
353   if(*(int *)data == 0x00005A6D) { // "mZ" save?\r
354     struct mz80context mz80;\r
355     mz80GetContext(&mz80);\r
356     memcpy(&mz80.z80clockticks, data+4, sizeof(mz80)-5*4);\r
357     mz80SetContext(&mz80);\r
358   } else {\r
359     z80_reset();\r
360     z80_int();\r
361   }\r
362 #elif defined(_USE_DRZ80)\r
363   if(*(int *)data == 0x015A7244) { // "DrZ" v1 save?\r
364     memcpy(&drZ80, data+4, 0x54);\r
365     // update bases\r
366     drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);\r
367     drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);\r
368   } else {\r
369     z80_reset();\r
370     drZ80.Z80IM = 1;\r
371     z80_int(); // try to goto int handler, maybe we won't execute trash there?\r
372   }\r
373 #endif\r
374 }\r
375 \r
376 void z80_exit()\r
377 {\r
378 #if defined(_USE_MZ80)\r
379   mz80shutdown();\r
380 #endif\r
381 }\r
382 \r
383 #if defined(__DEBUG_PRINT) || defined(WIN32)\r
384 void z80_debug(char *dstr)\r
385 {\r
386 #if defined(_USE_DRZ80)\r
387   sprintf(dstr, "%sZ80 state: PC: %04x SP: %04x\n", dstr, drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);\r
388 #endif\r
389 }\r
390 #endif\r