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; |
db1d3564 |
14 | |
a8fd6e37 |
15 | static int timer_cycles[2]; |
16 | static int timer_tick_cycles[2]; |
db1d3564 |
17 | |
1d7a28a7 |
18 | // timers. This includes PWM timer in 32x and internal SH2 timers |
19 | void p32x_timers_recalc(void) |
db1d3564 |
20 | { |
df63f1a6 |
21 | int control = Pico32x.regs[0x30 / 2]; |
db1d3564 |
22 | int cycles = Pico32x.regs[0x32 / 2]; |
1d7a28a7 |
23 | int tmp, i; |
db1d3564 |
24 | |
25 | cycles = (cycles - 1) & 0x0fff; |
db1d3564 |
26 | pwm_cycles = cycles; |
27 | pwm_mult = 0x10000 / cycles; |
1d7a28a7 |
28 | |
df63f1a6 |
29 | pwm_irq_reload = (control & 0x0f00) >> 8; |
30 | pwm_irq_reload = ((pwm_irq_reload - 1) & 0x0f) + 1; |
31 | |
32 | if (Pico32x.pwm_irq_cnt == 0) |
33 | Pico32x.pwm_irq_cnt = pwm_irq_reload; |
34 | |
1d7a28a7 |
35 | // SH2 timer step |
36 | for (i = 0; i < 2; i++) { |
37 | tmp = PREG8(Pico32xMem->sh2_peri_regs[i], 0x80) & 7; |
38 | // Sclk cycles per timer tick |
39 | if (tmp) |
40 | cycles = 0x20 << tmp; |
41 | else |
42 | cycles = 2; |
a8fd6e37 |
43 | timer_tick_cycles[i] = cycles; |
44 | elprintf(EL_32X, "WDT cycles[%d] = %d", i, cycles); |
1d7a28a7 |
45 | } |
db1d3564 |
46 | } |
47 | |
df63f1a6 |
48 | static void do_pwm_irq(unsigned int m68k_cycles) |
49 | { |
50 | Pico32x.sh2irqs |= P32XI_PWM; |
51 | p32x_update_irls(NULL); |
52 | |
53 | if (Pico32x.regs[0x30 / 2] & P32XP_RTP) { |
54 | p32x_event_schedule(m68k_cycles, P32X_EVENT_PWM, pwm_cycles / 3 + 1); |
55 | // note: might recurse |
56 | p32x_dreq1_trigger(); |
57 | } |
58 | } |
59 | |
60 | #define consume_fifo(m68k_cycles) { \ |
61 | int cycles_diff = ((m68k_cycles) * 3) - Pico32x.pwm_cycle_p; \ |
a7f82a77 |
62 | if (cycles_diff >= pwm_cycles) \ |
df63f1a6 |
63 | consume_fifo_do(m68k_cycles, cycles_diff); \ |
a7f82a77 |
64 | } |
65 | |
df63f1a6 |
66 | static void consume_fifo_do(unsigned int m68k_cycles, int sh2_cycles_diff) |
db1d3564 |
67 | { |
df63f1a6 |
68 | int do_irq = 0; |
69 | |
a7f82a77 |
70 | if (pwm_cycles == 0) |
71 | return; |
72 | |
73 | elprintf(EL_PWM, "pwm: %u: consume %d/%d, %d,%d ptr %d", |
df63f1a6 |
74 | m68k_cycles, sh2_cycles_diff, sh2_cycles_diff / pwm_cycles, |
a7f82a77 |
75 | Pico32x.pwm_p[0], Pico32x.pwm_p[1], pwm_ptr); |
76 | |
df63f1a6 |
77 | if (sh2_cycles_diff >= pwm_cycles * 17) { |
a7f82a77 |
78 | // silence/skip |
df63f1a6 |
79 | Pico32x.pwm_cycle_p = m68k_cycles * 3; |
a7f82a77 |
80 | Pico32x.pwm_p[0] = Pico32x.pwm_p[1] = 0; |
81 | return; |
82 | } |
07e5dbab |
83 | |
df63f1a6 |
84 | while (sh2_cycles_diff >= pwm_cycles) { |
a7f82a77 |
85 | struct Pico32xMem *mem = Pico32xMem; |
86 | short *fifo_l = mem->pwm_fifo[0]; |
87 | short *fifo_r = mem->pwm_fifo[1]; |
1d7a28a7 |
88 | |
a7f82a77 |
89 | if (Pico32x.pwm_p[0] > 0) { |
90 | fifo_l[0] = fifo_l[1]; |
91 | fifo_l[1] = fifo_l[2]; |
92 | fifo_l[2] = fifo_l[3]; |
93 | Pico32x.pwm_p[0]--; |
bc3aea8e |
94 | } |
a7f82a77 |
95 | if (Pico32x.pwm_p[1] > 0) { |
96 | fifo_r[0] = fifo_r[1]; |
97 | fifo_r[1] = fifo_r[2]; |
98 | fifo_r[2] = fifo_r[3]; |
99 | Pico32x.pwm_p[1]--; |
100 | } |
101 | |
102 | mem->pwm[pwm_ptr * 2 ] = fifo_l[0]; |
103 | mem->pwm[pwm_ptr * 2 + 1] = fifo_r[0]; |
104 | pwm_ptr = (pwm_ptr + 1) & (PWM_BUFF_LEN - 1); |
df63f1a6 |
105 | |
106 | sh2_cycles_diff -= pwm_cycles; |
107 | |
108 | if (--Pico32x.pwm_irq_cnt == 0) { |
109 | Pico32x.pwm_irq_cnt = pwm_irq_reload; |
110 | // irq also does dreq1, so call it after cycle update |
111 | do_irq = 1; |
112 | break; |
113 | } |
a8fd6e37 |
114 | } |
df63f1a6 |
115 | Pico32x.pwm_cycle_p = m68k_cycles * 3 - sh2_cycles_diff; |
116 | |
117 | if (do_irq) |
118 | do_pwm_irq(m68k_cycles); |
a7f82a77 |
119 | } |
120 | |
121 | void p32x_timers_do(unsigned int m68k_now, unsigned int m68k_slice) |
122 | { |
123 | unsigned int cycles = m68k_slice * 3; |
124 | int cnt, i; |
125 | |
df63f1a6 |
126 | //consume_fifo(m68k_now); |
db1d3564 |
127 | |
a8fd6e37 |
128 | // WDT timers |
1d7a28a7 |
129 | for (i = 0; i < 2; i++) { |
130 | void *pregs = Pico32xMem->sh2_peri_regs[i]; |
131 | if (PREG8(pregs, 0x80) & 0x20) { // TME |
a8fd6e37 |
132 | timer_cycles[i] += cycles; |
1d7a28a7 |
133 | cnt = PREG8(pregs, 0x81); |
a8fd6e37 |
134 | while (timer_cycles[i] >= timer_tick_cycles[i]) { |
135 | timer_cycles[i] -= timer_tick_cycles[i]; |
136 | cnt++; |
137 | } |
1d7a28a7 |
138 | if (cnt >= 0x100) { |
139 | int level = PREG8(pregs, 0xe3) >> 4; |
140 | int vector = PREG8(pregs, 0xe4) & 0x7f; |
a8fd6e37 |
141 | elprintf(EL_32X, "%csh2 WDT irq (%d, %d)", |
142 | i ? 's' : 'm', level, vector); |
1d7a28a7 |
143 | sh2_internal_irq(&sh2s[i], level, vector); |
a8fd6e37 |
144 | cnt &= 0xff; |
1d7a28a7 |
145 | } |
1d7a28a7 |
146 | PREG8(pregs, 0x81) = cnt; |
147 | } |
db1d3564 |
148 | } |
149 | } |
150 | |
df63f1a6 |
151 | static int p32x_pwm_schedule_(unsigned int m68k_now) |
a8fd6e37 |
152 | { |
df63f1a6 |
153 | unsigned int sh2_now = m68k_now * 3; |
154 | int cycles_diff_sh2; |
155 | |
156 | if (pwm_cycles == 0) |
157 | return 0; |
158 | |
159 | cycles_diff_sh2 = sh2_now - Pico32x.pwm_cycle_p; |
160 | if (cycles_diff_sh2 >= pwm_cycles) |
161 | consume_fifo_do(m68k_now, cycles_diff_sh2); |
a8fd6e37 |
162 | |
a8fd6e37 |
163 | if (Pico32x.sh2irqs & P32XI_PWM) |
19886062 |
164 | return 0; // previous not acked |
a8fd6e37 |
165 | if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 1)) |
19886062 |
166 | return 0; // masked by everyone |
a8fd6e37 |
167 | |
df63f1a6 |
168 | cycles_diff_sh2 = sh2_now - Pico32x.pwm_cycle_p; |
169 | return (Pico32x.pwm_irq_cnt * pwm_cycles |
170 | - cycles_diff_sh2) / 3 + 1; |
19886062 |
171 | } |
172 | |
df63f1a6 |
173 | void p32x_pwm_schedule(unsigned int m68k_now) |
19886062 |
174 | { |
df63f1a6 |
175 | int after = p32x_pwm_schedule_(m68k_now); |
19886062 |
176 | if (after != 0) |
df63f1a6 |
177 | p32x_event_schedule(m68k_now, P32X_EVENT_PWM, after); |
19886062 |
178 | } |
179 | |
180 | void p32x_pwm_schedule_sh2(SH2 *sh2) |
181 | { |
df63f1a6 |
182 | int after = p32x_pwm_schedule_(sh2_cycles_done_m68k(sh2)); |
19886062 |
183 | if (after != 0) |
184 | p32x_event_schedule_sh2(sh2, P32X_EVENT_PWM, after); |
a8fd6e37 |
185 | } |
186 | |
df63f1a6 |
187 | void p32x_pwm_irq_event(unsigned int m68k_now) |
188 | { |
189 | p32x_pwm_schedule(m68k_now); |
190 | } |
191 | |
192 | unsigned int p32x_pwm_read16(unsigned int a, unsigned int m68k_cycles) |
db1d3564 |
193 | { |
194 | unsigned int d = 0; |
a7f82a77 |
195 | |
df63f1a6 |
196 | consume_fifo(m68k_cycles); |
db1d3564 |
197 | |
198 | a &= 0x0e; |
199 | switch (a) { |
200 | case 0: // control |
201 | case 2: // cycle |
a7f82a77 |
202 | d = Pico32x.regs[(0x30 + a) / 2]; |
203 | break; |
db1d3564 |
204 | |
205 | case 4: // L ch |
a7f82a77 |
206 | if (Pico32x.pwm_p[0] == 3) |
207 | d |= P32XP_FULL; |
208 | else if (Pico32x.pwm_p[0] == 0) |
209 | d |= P32XP_EMPTY; |
210 | break; |
211 | |
db1d3564 |
212 | case 6: // R ch |
213 | case 8: // MONO |
a7f82a77 |
214 | if (Pico32x.pwm_p[1] == 3) |
db1d3564 |
215 | d |= P32XP_FULL; |
a7f82a77 |
216 | else if (Pico32x.pwm_p[1] == 0) |
db1d3564 |
217 | d |= P32XP_EMPTY; |
218 | break; |
219 | } |
220 | |
df63f1a6 |
221 | elprintf(EL_PWM, "pwm: %u: r16 %02x %04x (p %d %d)", |
222 | m68k_cycles, a, d, Pico32x.pwm_p[0], Pico32x.pwm_p[1]); |
db1d3564 |
223 | return d; |
224 | } |
225 | |
df63f1a6 |
226 | void p32x_pwm_write16(unsigned int a, unsigned int d, |
227 | unsigned int m68k_cycles) |
db1d3564 |
228 | { |
df63f1a6 |
229 | elprintf(EL_PWM, "pwm: %u: w16 %02x %04x (p %d %d)", |
230 | m68k_cycles, a & 0x0e, d, Pico32x.pwm_p[0], Pico32x.pwm_p[1]); |
231 | |
232 | consume_fifo(m68k_cycles); |
a7f82a77 |
233 | |
db1d3564 |
234 | a &= 0x0e; |
a7f82a77 |
235 | if (a == 0) { // control |
236 | // supposedly we should stop FIFO when xMd is 0, |
237 | // but mars test disagrees |
db1d3564 |
238 | Pico32x.regs[0x30 / 2] = d; |
df63f1a6 |
239 | p32x_timers_recalc(); |
240 | Pico32x.pwm_irq_cnt = pwm_irq_reload; // ? |
a7f82a77 |
241 | } |
db1d3564 |
242 | else if (a == 2) { // cycle |
243 | Pico32x.regs[0x32 / 2] = d & 0x0fff; |
1d7a28a7 |
244 | p32x_timers_recalc(); |
db1d3564 |
245 | } |
246 | else if (a <= 8) { |
a7f82a77 |
247 | d = (d - 1) & 0x0fff; |
db1d3564 |
248 | if (d > pwm_cycles) |
249 | d = pwm_cycles; |
250 | d = (d - pwm_cycles / 2) * pwm_mult; |
251 | |
a7f82a77 |
252 | if (a == 4 || a == 8) { // L ch or MONO |
253 | short *fifo = Pico32xMem->pwm_fifo[0]; |
254 | if (Pico32x.pwm_p[0] < 3) |
255 | Pico32x.pwm_p[0]++; |
256 | else { |
257 | fifo[1] = fifo[2]; |
258 | fifo[2] = fifo[3]; |
259 | } |
260 | fifo[Pico32x.pwm_p[0]] = d; |
261 | } |
262 | if (a == 6 || a == 8) { // R ch or MONO |
263 | short *fifo = Pico32xMem->pwm_fifo[1]; |
264 | if (Pico32x.pwm_p[1] < 3) |
265 | Pico32x.pwm_p[1]++; |
266 | else { |
267 | fifo[1] = fifo[2]; |
268 | fifo[2] = fifo[3]; |
269 | } |
270 | fifo[Pico32x.pwm_p[1]] = d; |
db1d3564 |
271 | } |
272 | } |
273 | } |
274 | |
275 | void p32x_pwm_update(int *buf32, int length, int stereo) |
276 | { |
db1d3564 |
277 | short *pwmb; |
278 | int step; |
279 | int p = 0; |
a7f82a77 |
280 | int xmd; |
db1d3564 |
281 | |
a7f82a77 |
282 | xmd = Pico32x.regs[0x30 / 2] & 0x0f; |
283 | if ((xmd != 0x05 && xmd != 0x0a) || pwm_ptr <= 16) |
284 | goto out; |
db1d3564 |
285 | |
286 | step = (pwm_ptr << 16) / length; // FIXME: division.. |
287 | pwmb = Pico32xMem->pwm; |
288 | |
289 | if (stereo) |
290 | { |
a7f82a77 |
291 | if (xmd == 0x0a) { |
292 | // channel swap |
293 | while (length-- > 0) { |
294 | *buf32++ += pwmb[1]; |
295 | *buf32++ += pwmb[0]; |
db1d3564 |
296 | |
a7f82a77 |
297 | p += step; |
298 | pwmb += (p >> 16) * 2; |
299 | p &= 0xffff; |
300 | } |
301 | } |
302 | else { |
303 | while (length-- > 0) { |
304 | *buf32++ += pwmb[0]; |
305 | *buf32++ += pwmb[1]; |
306 | |
307 | p += step; |
308 | pwmb += (p >> 16) * 2; |
309 | p &= 0xffff; |
310 | } |
db1d3564 |
311 | } |
312 | } |
313 | else |
314 | { |
315 | while (length-- > 0) { |
316 | *buf32++ += pwmb[0]; |
317 | |
318 | p += step; |
319 | pwmb += (p >> 16) * 2; |
320 | p &= 0xffff; |
321 | } |
322 | } |
323 | |
1b3f5844 |
324 | elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d", |
db1d3564 |
325 | pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2); |
326 | |
a7f82a77 |
327 | out: |
db1d3564 |
328 | pwm_ptr = 0; |
329 | } |
330 | |
df63f1a6 |
331 | void p32x_pwm_state_loaded(void) |
332 | { |
333 | int cycles_diff_sh2; |
334 | |
335 | p32x_timers_recalc(); |
336 | |
337 | // for old savestates |
338 | cycles_diff_sh2 = SekCycleCntT * 3 - Pico32x.pwm_cycle_p; |
339 | if (cycles_diff_sh2 >= pwm_cycles || cycles_diff_sh2 < 0) { |
340 | Pico32x.pwm_irq_cnt = pwm_irq_reload; |
341 | Pico32x.pwm_cycle_p = SekCycleCntT * 3; |
342 | p32x_pwm_schedule(SekCycleCntT); |
343 | } |
344 | } |
345 | |
a8fd6e37 |
346 | // vim:shiftwidth=2:ts=2:expandtab |