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