cff531af |
1 | /* |
2 | * PicoDrive |
a7f82a77 |
3 | * (C) notaz, 2009,2010,2013 |
cff531af |
4 | * |
5 | * This work is licensed under the terms of MAME license. |
6 | * See COPYING file in the top-level directory. |
7 | */ |
db1d3564 |
8 | #include "../pico_int.h" |
9 | |
db1d3564 |
10 | static int pwm_cycles; |
11 | static int pwm_mult; |
12 | static int pwm_ptr; |
df63f1a6 |
13 | static int pwm_irq_reload; |
8ad1d2ad |
14 | static int pwm_doing_fifo; |
8ce9c3a7 |
15 | static int pwm_silent; |
db1d3564 |
16 | |
045a4c52 |
17 | void p32x_pwm_ctl_changed(void) |
db1d3564 |
18 | { |
df63f1a6 |
19 | int control = Pico32x.regs[0x30 / 2]; |
db1d3564 |
20 | int cycles = Pico32x.regs[0x32 / 2]; |
db1d3564 |
21 | |
22 | cycles = (cycles - 1) & 0x0fff; |
db1d3564 |
23 | pwm_cycles = cycles; |
8ce9c3a7 |
24 | |
25 | // supposedly we should stop FIFO when xMd is 0, |
26 | // but mars test disagrees |
27 | pwm_mult = 0; |
28 | if ((control & 0x0f) != 0) |
29 | pwm_mult = 0x10000 / cycles; |
1d7a28a7 |
30 | |
df63f1a6 |
31 | pwm_irq_reload = (control & 0x0f00) >> 8; |
32 | pwm_irq_reload = ((pwm_irq_reload - 1) & 0x0f) + 1; |
33 | |
34 | if (Pico32x.pwm_irq_cnt == 0) |
35 | Pico32x.pwm_irq_cnt = pwm_irq_reload; |
db1d3564 |
36 | } |
37 | |
c1931173 |
38 | static void do_pwm_irq(SH2 *sh2, unsigned int m68k_cycles) |
df63f1a6 |
39 | { |
9e1fa0a6 |
40 | p32x_trigger_irq(sh2, m68k_cycles, P32XI_PWM); |
df63f1a6 |
41 | |
42 | if (Pico32x.regs[0x30 / 2] & P32XP_RTP) { |
43 | p32x_event_schedule(m68k_cycles, P32X_EVENT_PWM, pwm_cycles / 3 + 1); |
44 | // note: might recurse |
45 | p32x_dreq1_trigger(); |
46 | } |
47 | } |
48 | |
8ce9c3a7 |
49 | static int convert_sample(unsigned int v) |
50 | { |
51 | if (v == 0) |
52 | return 0; |
53 | if (v > pwm_cycles) |
54 | v = pwm_cycles; |
55 | return ((int)v - pwm_cycles / 2) * pwm_mult; |
56 | } |
57 | |
c1931173 |
58 | #define consume_fifo(sh2, m68k_cycles) { \ |
df63f1a6 |
59 | int cycles_diff = ((m68k_cycles) * 3) - Pico32x.pwm_cycle_p; \ |
a7f82a77 |
60 | if (cycles_diff >= pwm_cycles) \ |
c1931173 |
61 | consume_fifo_do(sh2, m68k_cycles, cycles_diff); \ |
a7f82a77 |
62 | } |
63 | |
c1931173 |
64 | static void consume_fifo_do(SH2 *sh2, unsigned int m68k_cycles, |
65 | int sh2_cycles_diff) |
db1d3564 |
66 | { |
8ce9c3a7 |
67 | struct Pico32xMem *mem = Pico32xMem; |
68 | unsigned short *fifo_l = mem->pwm_fifo[0]; |
69 | unsigned short *fifo_r = mem->pwm_fifo[1]; |
70 | int sum = 0; |
71 | |
8ad1d2ad |
72 | if (pwm_cycles == 0 || pwm_doing_fifo) |
a7f82a77 |
73 | return; |
74 | |
75 | elprintf(EL_PWM, "pwm: %u: consume %d/%d, %d,%d ptr %d", |
df63f1a6 |
76 | m68k_cycles, sh2_cycles_diff, sh2_cycles_diff / pwm_cycles, |
a7f82a77 |
77 | Pico32x.pwm_p[0], Pico32x.pwm_p[1], pwm_ptr); |
78 | |
8ad1d2ad |
79 | // this is for recursion from dreq1 writes |
80 | pwm_doing_fifo = 1; |
07e5dbab |
81 | |
8ce9c3a7 |
82 | for (; sh2_cycles_diff >= pwm_cycles; sh2_cycles_diff -= pwm_cycles) |
83 | { |
a7f82a77 |
84 | if (Pico32x.pwm_p[0] > 0) { |
85 | fifo_l[0] = fifo_l[1]; |
86 | fifo_l[1] = fifo_l[2]; |
87 | fifo_l[2] = fifo_l[3]; |
88 | Pico32x.pwm_p[0]--; |
8ce9c3a7 |
89 | mem->pwm_current[0] = convert_sample(fifo_l[0]); |
90 | sum += mem->pwm_current[0]; |
bc3aea8e |
91 | } |
a7f82a77 |
92 | if (Pico32x.pwm_p[1] > 0) { |
93 | fifo_r[0] = fifo_r[1]; |
94 | fifo_r[1] = fifo_r[2]; |
95 | fifo_r[2] = fifo_r[3]; |
96 | Pico32x.pwm_p[1]--; |
8ce9c3a7 |
97 | mem->pwm_current[1] = convert_sample(fifo_r[0]); |
98 | sum += mem->pwm_current[1]; |
a7f82a77 |
99 | } |
100 | |
8ce9c3a7 |
101 | mem->pwm[pwm_ptr * 2 ] = mem->pwm_current[0]; |
102 | mem->pwm[pwm_ptr * 2 + 1] = mem->pwm_current[1]; |
a7f82a77 |
103 | pwm_ptr = (pwm_ptr + 1) & (PWM_BUFF_LEN - 1); |
df63f1a6 |
104 | |
df63f1a6 |
105 | if (--Pico32x.pwm_irq_cnt == 0) { |
106 | Pico32x.pwm_irq_cnt = pwm_irq_reload; |
8ad1d2ad |
107 | do_pwm_irq(sh2, m68k_cycles); |
df63f1a6 |
108 | } |
a8fd6e37 |
109 | } |
df63f1a6 |
110 | Pico32x.pwm_cycle_p = m68k_cycles * 3 - sh2_cycles_diff; |
8ad1d2ad |
111 | pwm_doing_fifo = 0; |
8ce9c3a7 |
112 | if (sum != 0) |
113 | pwm_silent = 0; |
a7f82a77 |
114 | } |
115 | |
c1931173 |
116 | static int p32x_pwm_schedule_(SH2 *sh2, unsigned int m68k_now) |
a8fd6e37 |
117 | { |
df63f1a6 |
118 | unsigned int sh2_now = m68k_now * 3; |
119 | int cycles_diff_sh2; |
120 | |
121 | if (pwm_cycles == 0) |
122 | return 0; |
123 | |
124 | cycles_diff_sh2 = sh2_now - Pico32x.pwm_cycle_p; |
125 | if (cycles_diff_sh2 >= pwm_cycles) |
c1931173 |
126 | consume_fifo_do(sh2, m68k_now, cycles_diff_sh2); |
a8fd6e37 |
127 | |
a8fd6e37 |
128 | if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 1)) |
19886062 |
129 | return 0; // masked by everyone |
a8fd6e37 |
130 | |
df63f1a6 |
131 | cycles_diff_sh2 = sh2_now - Pico32x.pwm_cycle_p; |
132 | return (Pico32x.pwm_irq_cnt * pwm_cycles |
133 | - cycles_diff_sh2) / 3 + 1; |
19886062 |
134 | } |
135 | |
df63f1a6 |
136 | void p32x_pwm_schedule(unsigned int m68k_now) |
19886062 |
137 | { |
c1931173 |
138 | int after = p32x_pwm_schedule_(NULL, m68k_now); |
19886062 |
139 | if (after != 0) |
df63f1a6 |
140 | p32x_event_schedule(m68k_now, P32X_EVENT_PWM, after); |
19886062 |
141 | } |
142 | |
143 | void p32x_pwm_schedule_sh2(SH2 *sh2) |
144 | { |
c1931173 |
145 | int after = p32x_pwm_schedule_(sh2, sh2_cycles_done_m68k(sh2)); |
19886062 |
146 | if (after != 0) |
147 | p32x_event_schedule_sh2(sh2, P32X_EVENT_PWM, after); |
a8fd6e37 |
148 | } |
149 | |
9e1fa0a6 |
150 | void p32x_pwm_sync_to_sh2(SH2 *sh2) |
151 | { |
152 | int m68k_cycles = sh2_cycles_done_m68k(sh2); |
153 | consume_fifo(sh2, m68k_cycles); |
154 | } |
155 | |
df63f1a6 |
156 | void p32x_pwm_irq_event(unsigned int m68k_now) |
157 | { |
158 | p32x_pwm_schedule(m68k_now); |
159 | } |
160 | |
c1931173 |
161 | unsigned int p32x_pwm_read16(unsigned int a, SH2 *sh2, |
162 | unsigned int m68k_cycles) |
db1d3564 |
163 | { |
164 | unsigned int d = 0; |
a7f82a77 |
165 | |
c1931173 |
166 | consume_fifo(sh2, m68k_cycles); |
db1d3564 |
167 | |
168 | a &= 0x0e; |
169 | switch (a) { |
170 | case 0: // control |
171 | case 2: // cycle |
a7f82a77 |
172 | d = Pico32x.regs[(0x30 + a) / 2]; |
173 | break; |
db1d3564 |
174 | |
175 | case 4: // L ch |
a7f82a77 |
176 | if (Pico32x.pwm_p[0] == 3) |
177 | d |= P32XP_FULL; |
178 | else if (Pico32x.pwm_p[0] == 0) |
179 | d |= P32XP_EMPTY; |
180 | break; |
181 | |
db1d3564 |
182 | case 6: // R ch |
183 | case 8: // MONO |
a7f82a77 |
184 | if (Pico32x.pwm_p[1] == 3) |
db1d3564 |
185 | d |= P32XP_FULL; |
a7f82a77 |
186 | else if (Pico32x.pwm_p[1] == 0) |
db1d3564 |
187 | d |= P32XP_EMPTY; |
188 | break; |
189 | } |
190 | |
df63f1a6 |
191 | elprintf(EL_PWM, "pwm: %u: r16 %02x %04x (p %d %d)", |
192 | m68k_cycles, a, d, Pico32x.pwm_p[0], Pico32x.pwm_p[1]); |
db1d3564 |
193 | return d; |
194 | } |
195 | |
df63f1a6 |
196 | void p32x_pwm_write16(unsigned int a, unsigned int d, |
c1931173 |
197 | SH2 *sh2, unsigned int m68k_cycles) |
db1d3564 |
198 | { |
df63f1a6 |
199 | elprintf(EL_PWM, "pwm: %u: w16 %02x %04x (p %d %d)", |
200 | m68k_cycles, a & 0x0e, d, Pico32x.pwm_p[0], Pico32x.pwm_p[1]); |
201 | |
c1931173 |
202 | consume_fifo(sh2, m68k_cycles); |
a7f82a77 |
203 | |
db1d3564 |
204 | a &= 0x0e; |
a7f82a77 |
205 | if (a == 0) { // control |
8ce9c3a7 |
206 | // avoiding pops.. |
207 | if ((Pico32x.regs[0x30 / 2] & 0x0f) == 0) |
208 | Pico32xMem->pwm_fifo[0][0] = Pico32xMem->pwm_fifo[1][0] = 0; |
db1d3564 |
209 | Pico32x.regs[0x30 / 2] = d; |
045a4c52 |
210 | p32x_pwm_ctl_changed(); |
df63f1a6 |
211 | Pico32x.pwm_irq_cnt = pwm_irq_reload; // ? |
a7f82a77 |
212 | } |
db1d3564 |
213 | else if (a == 2) { // cycle |
214 | Pico32x.regs[0x32 / 2] = d & 0x0fff; |
045a4c52 |
215 | p32x_pwm_ctl_changed(); |
db1d3564 |
216 | } |
217 | else if (a <= 8) { |
a7f82a77 |
218 | d = (d - 1) & 0x0fff; |
db1d3564 |
219 | |
a7f82a77 |
220 | if (a == 4 || a == 8) { // L ch or MONO |
8ce9c3a7 |
221 | unsigned short *fifo = Pico32xMem->pwm_fifo[0]; |
a7f82a77 |
222 | if (Pico32x.pwm_p[0] < 3) |
223 | Pico32x.pwm_p[0]++; |
224 | else { |
225 | fifo[1] = fifo[2]; |
226 | fifo[2] = fifo[3]; |
227 | } |
228 | fifo[Pico32x.pwm_p[0]] = d; |
229 | } |
230 | if (a == 6 || a == 8) { // R ch or MONO |
8ce9c3a7 |
231 | unsigned short *fifo = Pico32xMem->pwm_fifo[1]; |
a7f82a77 |
232 | if (Pico32x.pwm_p[1] < 3) |
233 | Pico32x.pwm_p[1]++; |
234 | else { |
235 | fifo[1] = fifo[2]; |
236 | fifo[2] = fifo[3]; |
237 | } |
238 | fifo[Pico32x.pwm_p[1]] = d; |
db1d3564 |
239 | } |
240 | } |
241 | } |
242 | |
243 | void p32x_pwm_update(int *buf32, int length, int stereo) |
244 | { |
db1d3564 |
245 | short *pwmb; |
246 | int step; |
247 | int p = 0; |
a7f82a77 |
248 | int xmd; |
db1d3564 |
249 | |
ae214f1c |
250 | consume_fifo(NULL, SekCyclesDone()); |
8ad1d2ad |
251 | |
a7f82a77 |
252 | xmd = Pico32x.regs[0x30 / 2] & 0x0f; |
8ad1d2ad |
253 | if (xmd == 0 || xmd == 0x06 || xmd == 0x09 || xmd == 0x0f) |
254 | goto out; // invalid? |
8ce9c3a7 |
255 | if (pwm_silent) |
256 | return; |
db1d3564 |
257 | |
8ad1d2ad |
258 | step = (pwm_ptr << 16) / length; |
db1d3564 |
259 | pwmb = Pico32xMem->pwm; |
260 | |
261 | if (stereo) |
262 | { |
8ad1d2ad |
263 | if (xmd == 0x05) { |
264 | // normal |
265 | while (length-- > 0) { |
266 | *buf32++ += pwmb[0]; |
267 | *buf32++ += pwmb[1]; |
268 | |
269 | p += step; |
270 | pwmb += (p >> 16) * 2; |
271 | p &= 0xffff; |
272 | } |
273 | } |
274 | else if (xmd == 0x0a) { |
a7f82a77 |
275 | // channel swap |
276 | while (length-- > 0) { |
277 | *buf32++ += pwmb[1]; |
278 | *buf32++ += pwmb[0]; |
db1d3564 |
279 | |
a7f82a77 |
280 | p += step; |
281 | pwmb += (p >> 16) * 2; |
282 | p &= 0xffff; |
283 | } |
284 | } |
285 | else { |
8ad1d2ad |
286 | // mono - LMD, RMD specify dst |
287 | if (xmd & 0x06) // src is R |
288 | pwmb++; |
289 | if (xmd & 0x0c) // dst is R |
290 | buf32++; |
a7f82a77 |
291 | while (length-- > 0) { |
8ad1d2ad |
292 | *buf32 += *pwmb; |
a7f82a77 |
293 | |
294 | p += step; |
295 | pwmb += (p >> 16) * 2; |
296 | p &= 0xffff; |
8ad1d2ad |
297 | buf32 += 2; |
a7f82a77 |
298 | } |
db1d3564 |
299 | } |
300 | } |
301 | else |
302 | { |
8ad1d2ad |
303 | // mostly unused |
db1d3564 |
304 | while (length-- > 0) { |
305 | *buf32++ += pwmb[0]; |
306 | |
307 | p += step; |
308 | pwmb += (p >> 16) * 2; |
309 | p &= 0xffff; |
310 | } |
311 | } |
312 | |
1b3f5844 |
313 | elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d", |
db1d3564 |
314 | pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2); |
315 | |
a7f82a77 |
316 | out: |
db1d3564 |
317 | pwm_ptr = 0; |
8ce9c3a7 |
318 | pwm_silent = Pico32xMem->pwm_current[0] == 0 |
319 | && Pico32xMem->pwm_current[1] == 0; |
db1d3564 |
320 | } |
321 | |
df63f1a6 |
322 | void p32x_pwm_state_loaded(void) |
323 | { |
324 | int cycles_diff_sh2; |
325 | |
045a4c52 |
326 | p32x_pwm_ctl_changed(); |
df63f1a6 |
327 | |
328 | // for old savestates |
88fd63ad |
329 | cycles_diff_sh2 = Pico.t.m68c_cnt * 3 - Pico32x.pwm_cycle_p; |
df63f1a6 |
330 | if (cycles_diff_sh2 >= pwm_cycles || cycles_diff_sh2 < 0) { |
331 | Pico32x.pwm_irq_cnt = pwm_irq_reload; |
88fd63ad |
332 | Pico32x.pwm_cycle_p = Pico.t.m68c_cnt * 3; |
333 | p32x_pwm_schedule(Pico.t.m68c_cnt); |
df63f1a6 |
334 | } |
335 | } |
336 | |
a8fd6e37 |
337 | // vim:shiftwidth=2:ts=2:expandtab |