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 | |
33be04ca |
9 | #include <assert.h> |
efcba75f |
10 | #include "../pico_int.h" |
abe0ea43 |
11 | |
33be04ca |
12 | #define PCM_STEP_SHIFT 11 |
abe0ea43 |
13 | |
33be04ca |
14 | void pcd_pcm_write(unsigned int a, unsigned int d) |
abe0ea43 |
15 | { |
33be04ca |
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 | } |
abe0ea43 |
46 | } |
47 | |
33be04ca |
48 | unsigned int pcd_pcm_read(unsigned int a) |
abe0ea43 |
49 | { |
33be04ca |
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; |
abe0ea43 |
59 | } |
60 | |
33be04ca |
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 | } |
abe0ea43 |
123 | |
33be04ca |
124 | void pcd_pcm_update(int *buf32, int length, int stereo) |
abe0ea43 |
125 | { |
33be04ca |
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; |
abe0ea43 |
164 | } |
165 | |
33be04ca |
166 | // vim:shiftwidth=2:ts=2:expandtab |