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