cff531af |
1 | /* |
2 | * Emulation routines for the RF5C164 PCM chip |
33be04ca |
3 | * (C) notaz, 2007, 2013 |
cff531af |
4 | * |
5 | * This work is licensed under the terms of MAME license. |
6 | * See COPYING file in the top-level directory. |
7 | */ |
6cadc2da |
8 | |
efcba75f |
9 | #include "../pico_int.h" |
abe0ea43 |
10 | |
33be04ca |
11 | #define PCM_STEP_SHIFT 11 |
abe0ea43 |
12 | |
33be04ca |
13 | void pcd_pcm_write(unsigned int a, unsigned int d) |
abe0ea43 |
14 | { |
33be04ca |
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 | } |
021e47b3 |
32 | else if (a == 8) |
33be04ca |
33 | { |
33be04ca |
34 | Pico_mcd->pcm.enabled = ~d; |
35 | } |
021e47b3 |
36 | Pico_mcd->pcm_regs_dirty = 1; |
abe0ea43 |
37 | } |
38 | |
33be04ca |
39 | unsigned int pcd_pcm_read(unsigned int a) |
abe0ea43 |
40 | { |
33be04ca |
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; |
abe0ea43 |
50 | } |
51 | |
33be04ca |
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; |
021e47b3 |
59 | int enabled; |
33be04ca |
60 | int *out; |
61 | |
62 | if ((int)(to - cycles) < 384) |
63 | return; |
64 | |
65 | steps = (to - cycles) / 384; |
021e47b3 |
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; |
33be04ca |
69 | |
70 | // PCM disabled or all channels off |
021e47b3 |
71 | enabled = Pico_mcd->pcm.enabled; |
72 | if (!(Pico_mcd->pcm.control & 0x80)) |
73 | enabled = 0; |
74 | if (!enabled && !Pico_mcd->pcm_regs_dirty) |
33be04ca |
75 | goto end; |
76 | |
77 | out = Pico_mcd->pcm_mixbuf + Pico_mcd->pcm_mixpos * 2; |
78 | Pico_mcd->pcm_mixbuf_dirty = 1; |
021e47b3 |
79 | Pico_mcd->pcm_regs_dirty = 0; |
33be04ca |
80 | |
81 | for (c = 0; c < 8; c++) |
82 | { |
021e47b3 |
83 | ch = &Pico_mcd->pcm.ch[c]; |
84 | |
85 | if (!(enabled & (1 << c))) { |
86 | ch->addr = ch->regs[6] << (PCM_STEP_SHIFT + 8); |
33be04ca |
87 | continue; // channel disabled |
021e47b3 |
88 | } |
33be04ca |
89 | |
33be04ca |
90 | addr = ch->addr; |
0d8d97f8 |
91 | inc = ch->regs[2] + (ch->regs[3]<<8); |
cce32a34 |
92 | mul_l = (int)ch->regs[0] * (ch->regs[1] & 0xf); |
93 | mul_r = (int)ch->regs[0] * (ch->regs[1] >> 4); |
33be04ca |
94 | |
fe8611f9 |
95 | for (s = 0; s < steps; s++) |
33be04ca |
96 | { |
97 | smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT]; |
98 | |
99 | // test for loop signal |
100 | if (smp == 0xff) |
101 | { |
0d8d97f8 |
102 | addr = ch->regs[4] + (ch->regs[5]<<8); // loop_addr |
33be04ca |
103 | smp = Pico_mcd->pcm_ram[addr]; |
104 | addr <<= PCM_STEP_SHIFT; |
105 | if (smp == 0xff) |
106 | break; |
fe8611f9 |
107 | } else |
108 | addr = (addr + inc) & 0x07FFFFFF; |
33be04ca |
109 | |
110 | if (smp & 0x80) |
111 | smp = -(smp & 0x7f); |
112 | |
cce32a34 |
113 | out[s*2 ] += (smp * mul_l) >> 5; // max 127 * 255 * 15 / 32 = 15180 |
114 | out[s*2+1] += (smp * mul_r) >> 5; |
33be04ca |
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 | } |
abe0ea43 |
123 | |
91ea9406 |
124 | void pcd_pcm_update(s32 *buf32, int length, int stereo) |
abe0ea43 |
125 | { |
33be04ca |
126 | int step, *pcm; |
127 | int p = 0; |
128 | |
129 | pcd_pcm_sync(SekCyclesDoneS68k()); |
130 | |
9f1d5acd |
131 | if (!Pico_mcd->pcm_mixbuf_dirty || !(PicoIn.opt & POPT_EN_MCD_PCM) || !buf32) |
33be04ca |
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; |
abe0ea43 |
164 | } |
165 | |
33be04ca |
166 | // vim:shiftwidth=2:ts=2:expandtab |