cd: rewrite pcm
[picodrive.git] / pico / cd / pcm.c
... / ...
CommitLineData
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
14void 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
48unsigned 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
61void 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
119end:
120 Pico_mcd->pcm.update_cycles = cycles + steps * 384;
121 Pico_mcd->pcm_mixpos += steps;
122}
123
124void 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
161out:
162 Pico_mcd->pcm_mixbuf_dirty = 0;
163 Pico_mcd->pcm_mixpos = 0;
164}
165
166// vim:shiftwidth=2:ts=2:expandtab