cd: rewrite pcm
[picodrive.git] / pico / cd / pcm.c
1 /*
2  * Emulation routines for the RF5C164 PCM chip
3  * (C) notaz, 2007, 2013
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #include <assert.h>
10 #include "../pico_int.h"
11
12 #define PCM_STEP_SHIFT 11
13
14 void pcd_pcm_write(unsigned int a, unsigned int d)
15 {
16   unsigned int cycles = SekCyclesDoneS68k();
17   if ((int)(cycles - Pico_mcd->pcm.update_cycles) >= 384)
18     pcd_pcm_sync(cycles);
19
20   if (a < 7)
21   {
22     Pico_mcd->pcm.ch[Pico_mcd->pcm.cur_ch].regs[a] = d;
23   }
24   else if (a == 7) // control register
25   {
26     if (d & 0x40)
27       Pico_mcd->pcm.cur_ch = d & 7;
28     else
29       Pico_mcd->pcm.bank = d & 0xf;
30     Pico_mcd->pcm.control = d;
31     elprintf(EL_CD, "pcm control %02x", Pico_mcd->pcm.control);
32   }
33   else if (a == 8 && Pico_mcd->pcm.enabled != (u_char)~d)
34   {
35     // sound on/off
36     int was_enabled = Pico_mcd->pcm.enabled;
37     int i;
38
39     for (i = 0; i < 8; i++)
40       if (!(was_enabled & (1 << i)))
41         Pico_mcd->pcm.ch[i].addr =
42           Pico_mcd->pcm.ch[i].regs[6] << (PCM_STEP_SHIFT + 8);
43
44     Pico_mcd->pcm.enabled = ~d;
45   }
46 }
47
48 unsigned int pcd_pcm_read(unsigned int a)
49 {
50   unsigned int d, cycles = SekCyclesDoneS68k();
51   if ((int)(cycles - Pico_mcd->pcm.update_cycles) >= 384)
52     pcd_pcm_sync(cycles);
53
54   d = Pico_mcd->pcm.ch[(a >> 1) & 7].addr >> PCM_STEP_SHIFT;
55   if (a & 1)
56     d >>= 8;
57
58   return d & 0xff;
59 }
60
61 void pcd_pcm_sync(unsigned int to)
62 {
63   unsigned int cycles = Pico_mcd->pcm.update_cycles;
64   int mul_l, mul_r, inc, smp;
65   struct pcm_chan *ch;
66   unsigned int addr;
67   int c, s, steps;
68   int *out;
69
70   if ((int)(to - cycles) < 384)
71     return;
72
73   steps = (to - cycles) / 384;
74
75   // PCM disabled or all channels off
76   if (!(Pico_mcd->pcm.control & 0x80) || !Pico_mcd->pcm.enabled)
77     goto end;
78
79   out = Pico_mcd->pcm_mixbuf + Pico_mcd->pcm_mixpos * 2;
80   Pico_mcd->pcm_mixbuf_dirty = 1;
81   if (Pico_mcd->pcm_mixpos + steps > PCM_MIXBUF_LEN)
82     // shouldn't happen
83     steps = PCM_MIXBUF_LEN - Pico_mcd->pcm_mixpos;
84
85   for (c = 0; c < 8; c++)
86   {
87     if (!(Pico_mcd->pcm.enabled & (1 << c)))
88       continue; // channel disabled
89
90     ch = &Pico_mcd->pcm.ch[c];
91     addr = ch->addr;
92     inc = *(unsigned short *)&ch->regs[2];
93     mul_l = ((int)ch->regs[0] * (ch->regs[1] & 0xf)) >> (5+1); 
94     mul_r = ((int)ch->regs[0] * (ch->regs[1] >>  4)) >> (5+1);
95
96     for (s = 0; s < steps; s++, addr = (addr + inc) & 0x7FFFFFF)
97     {
98       smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT];
99
100       // test for loop signal
101       if (smp == 0xff)
102       {
103         addr = *(unsigned short *)&ch->regs[4]; // loop_addr
104         smp = Pico_mcd->pcm_ram[addr];
105         addr <<= PCM_STEP_SHIFT;
106         if (smp == 0xff)
107           break;
108       }
109
110       if (smp & 0x80)
111         smp = -(smp & 0x7f);
112
113       out[s*2  ] += smp * mul_l; // max 128 * 119 = 15232
114       out[s*2+1] += smp * mul_r;
115     }
116     ch->addr = addr;
117   }
118
119 end:
120   Pico_mcd->pcm.update_cycles = cycles + steps * 384;
121   Pico_mcd->pcm_mixpos += steps;
122 }
123
124 void pcd_pcm_update(int *buf32, int length, int stereo)
125 {
126   int step, *pcm;
127   int p = 0;
128
129   pcd_pcm_sync(SekCyclesDoneS68k());
130
131   if (!Pico_mcd->pcm_mixbuf_dirty || !(PicoOpt & POPT_EN_MCD_PCM))
132     goto out;
133
134   step = (Pico_mcd->pcm_mixpos << 16) / length;
135   pcm = Pico_mcd->pcm_mixbuf;
136
137   if (stereo) {
138     while (length-- > 0) {
139       *buf32++ += pcm[0];
140       *buf32++ += pcm[1];
141
142       p += step;
143       pcm += (p >> 16) * 2;
144       p &= 0xffff;
145     }
146   }
147   else {
148     while (length-- > 0) {
149       // mostly unused
150       *buf32++ += pcm[0];
151
152       p += step;
153       pcm += (p >> 16) * 2;
154       p &= 0xffff;
155     }
156   }
157
158   memset(Pico_mcd->pcm_mixbuf, 0,
159     Pico_mcd->pcm_mixpos * 2 * sizeof(Pico_mcd->pcm_mixbuf[0]));
160
161 out:
162   Pico_mcd->pcm_mixbuf_dirty = 0;
163   Pico_mcd->pcm_mixpos = 0;
164 }
165
166 // vim:shiftwidth=2:ts=2:expandtab