cff531af |
1 | /* |
2 | * PicoDrive |
3 | * (C) notaz, 2009,2010 |
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 | |
a8fd6e37 |
10 | static int pwm_cycle_counter; |
db1d3564 |
11 | static int pwm_cycles; |
12 | static int pwm_mult; |
13 | static int pwm_ptr; |
db1d3564 |
14 | |
a8fd6e37 |
15 | static int pwm_smp_cnt; |
16 | static int pwm_smp_expect; |
17 | |
18 | static int timer_cycles[2]; |
19 | static int timer_tick_cycles[2]; |
db1d3564 |
20 | |
1d7a28a7 |
21 | // timers. This includes PWM timer in 32x and internal SH2 timers |
22 | void p32x_timers_recalc(void) |
db1d3564 |
23 | { |
24 | int cycles = Pico32x.regs[0x32 / 2]; |
1d7a28a7 |
25 | int tmp, i; |
db1d3564 |
26 | |
27 | cycles = (cycles - 1) & 0x0fff; |
28 | if (cycles < 500) { |
1b3f5844 |
29 | elprintf(EL_32X|EL_PWM|EL_ANOMALY, "pwm: low cycle value: %d", cycles + 1); |
db1d3564 |
30 | cycles = 500; |
31 | } |
32 | pwm_cycles = cycles; |
33 | pwm_mult = 0x10000 / cycles; |
1d7a28a7 |
34 | |
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 | |
1d7a28a7 |
48 | // PWM irq for every tm samples |
a8fd6e37 |
49 | void p32x_timers_do(unsigned int cycles) |
db1d3564 |
50 | { |
a8fd6e37 |
51 | int cnt, i; |
07e5dbab |
52 | |
a8fd6e37 |
53 | cycles *= 3; |
1d7a28a7 |
54 | |
bc3aea8e |
55 | // since we run things in async fashion, allow pwm to lag behind |
56 | // but don't allow our "queue" to be infinite |
57 | cnt = pwm_smp_expect - pwm_smp_cnt; |
58 | if (cnt <= 0 || cnt * pwm_cycles < OSC_NTSC/7*3 / 60 / 2) { |
59 | pwm_cycle_counter += cycles; |
60 | while (pwm_cycle_counter > pwm_cycles) { |
61 | pwm_cycle_counter -= pwm_cycles; |
62 | pwm_smp_expect++; |
63 | } |
a8fd6e37 |
64 | } |
db1d3564 |
65 | |
a8fd6e37 |
66 | // WDT timers |
1d7a28a7 |
67 | for (i = 0; i < 2; i++) { |
68 | void *pregs = Pico32xMem->sh2_peri_regs[i]; |
69 | if (PREG8(pregs, 0x80) & 0x20) { // TME |
a8fd6e37 |
70 | timer_cycles[i] += cycles; |
1d7a28a7 |
71 | cnt = PREG8(pregs, 0x81); |
a8fd6e37 |
72 | while (timer_cycles[i] >= timer_tick_cycles[i]) { |
73 | timer_cycles[i] -= timer_tick_cycles[i]; |
74 | cnt++; |
75 | } |
1d7a28a7 |
76 | if (cnt >= 0x100) { |
77 | int level = PREG8(pregs, 0xe3) >> 4; |
78 | int vector = PREG8(pregs, 0xe4) & 0x7f; |
a8fd6e37 |
79 | elprintf(EL_32X, "%csh2 WDT irq (%d, %d)", |
80 | i ? 's' : 'm', level, vector); |
1d7a28a7 |
81 | sh2_internal_irq(&sh2s[i], level, vector); |
a8fd6e37 |
82 | cnt &= 0xff; |
1d7a28a7 |
83 | } |
1d7a28a7 |
84 | PREG8(pregs, 0x81) = cnt; |
85 | } |
db1d3564 |
86 | } |
87 | } |
88 | |
19886062 |
89 | static int p32x_pwm_schedule_(void) |
a8fd6e37 |
90 | { |
91 | int tm; |
92 | |
93 | if (Pico32x.emu_flags & P32XF_PWM_PEND) |
19886062 |
94 | return 0; // already scheduled |
a8fd6e37 |
95 | if (Pico32x.sh2irqs & P32XI_PWM) |
19886062 |
96 | return 0; // previous not acked |
a8fd6e37 |
97 | if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 1)) |
19886062 |
98 | return 0; // masked by everyone |
a8fd6e37 |
99 | |
19886062 |
100 | Pico32x.emu_flags |= P32XF_PWM_PEND; |
a8fd6e37 |
101 | tm = (Pico32x.regs[0x30 / 2] & 0x0f00) >> 8; |
102 | tm = ((tm - 1) & 0x0f) + 1; |
19886062 |
103 | return pwm_cycles * tm / 3; |
104 | } |
105 | |
106 | void p32x_pwm_schedule(unsigned int now) |
107 | { |
108 | int after = p32x_pwm_schedule_(); |
109 | if (after != 0) |
110 | p32x_event_schedule(now, P32X_EVENT_PWM, after); |
111 | } |
112 | |
113 | void p32x_pwm_schedule_sh2(SH2 *sh2) |
114 | { |
115 | int after = p32x_pwm_schedule_(); |
116 | if (after != 0) |
117 | p32x_event_schedule_sh2(sh2, P32X_EVENT_PWM, after); |
a8fd6e37 |
118 | } |
119 | |
db1d3564 |
120 | unsigned int p32x_pwm_read16(unsigned int a) |
121 | { |
122 | unsigned int d = 0; |
a8fd6e37 |
123 | int diff; |
db1d3564 |
124 | |
125 | a &= 0x0e; |
126 | switch (a) { |
127 | case 0: // control |
128 | case 2: // cycle |
129 | return Pico32x.regs[(0x30 + a) / 2]; |
130 | |
131 | case 4: // L ch |
132 | case 6: // R ch |
133 | case 8: // MONO |
a8fd6e37 |
134 | diff = pwm_smp_cnt - pwm_smp_expect; |
135 | elprintf(EL_PWM, "pwm: read status: ptr %d/%d %d", |
136 | pwm_smp_cnt, pwm_smp_expect, diff); |
137 | if (diff > 3) |
db1d3564 |
138 | d |= P32XP_FULL; |
a8fd6e37 |
139 | else if (diff < 0) |
db1d3564 |
140 | d |= P32XP_EMPTY; |
141 | break; |
142 | } |
143 | |
144 | return d; |
145 | } |
146 | |
147 | void p32x_pwm_write16(unsigned int a, unsigned int d) |
148 | { |
149 | a &= 0x0e; |
150 | if (a == 0) // control |
151 | Pico32x.regs[0x30 / 2] = d; |
152 | else if (a == 2) { // cycle |
153 | Pico32x.regs[0x32 / 2] = d & 0x0fff; |
1d7a28a7 |
154 | p32x_timers_recalc(); |
db1d3564 |
155 | Pico32x.pwm_irq_sample_cnt = 0; // resets? |
156 | } |
157 | else if (a <= 8) { |
158 | d &= 0x0fff; |
159 | if (d > pwm_cycles) |
160 | d = pwm_cycles; |
161 | d = (d - pwm_cycles / 2) * pwm_mult; |
162 | |
163 | if (a < 6) // L ch |
164 | Pico32xMem->pwm[pwm_ptr * 2] = d; |
165 | else if (a == 6) // R ch |
166 | Pico32xMem->pwm[pwm_ptr * 2 + 1] = d; |
167 | else // MONO |
168 | Pico32xMem->pwm[pwm_ptr * 2] = Pico32xMem->pwm[pwm_ptr * 2 + 1] = d; |
169 | |
170 | if (a >= 6) { // R or MONO |
a8fd6e37 |
171 | pwm_smp_cnt++; |
db1d3564 |
172 | pwm_ptr = (pwm_ptr + 1) & (PWM_BUFF_LEN - 1); |
a8fd6e37 |
173 | elprintf(EL_PWM, "pwm: smp_cnt %d, ptr %d, smp %x", |
174 | pwm_smp_cnt, pwm_ptr, d); |
db1d3564 |
175 | } |
176 | } |
177 | } |
178 | |
179 | void p32x_pwm_update(int *buf32, int length, int stereo) |
180 | { |
db1d3564 |
181 | short *pwmb; |
182 | int step; |
183 | int p = 0; |
184 | |
185 | if (pwm_ptr <= 16) // at least some samples.. |
186 | return; |
187 | |
188 | step = (pwm_ptr << 16) / length; // FIXME: division.. |
189 | pwmb = Pico32xMem->pwm; |
190 | |
191 | if (stereo) |
192 | { |
193 | while (length-- > 0) { |
194 | *buf32++ += pwmb[0]; |
195 | *buf32++ += pwmb[1]; |
196 | |
197 | p += step; |
198 | pwmb += (p >> 16) * 2; |
199 | p &= 0xffff; |
200 | } |
201 | } |
202 | else |
203 | { |
204 | while (length-- > 0) { |
205 | *buf32++ += pwmb[0]; |
206 | |
207 | p += step; |
208 | pwmb += (p >> 16) * 2; |
209 | p &= 0xffff; |
210 | } |
211 | } |
212 | |
1b3f5844 |
213 | elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d", |
db1d3564 |
214 | pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2); |
215 | |
216 | pwm_ptr = 0; |
217 | } |
218 | |
a8fd6e37 |
219 | // vim:shiftwidth=2:ts=2:expandtab |