clarify PicoDrive's license
[picodrive.git] / pico / 32x / pwm.c
CommitLineData
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
10static int pwm_line_samples;
11static int pwm_cycles;
12static int pwm_mult;
13static int pwm_ptr;
14int pwm_frame_smp_cnt;
15
1d7a28a7 16static int timer_line_ticks[2];
db1d3564 17
1d7a28a7 18// timers. This includes PWM timer in 32x and internal SH2 timers
19void p32x_timers_recalc(void)
db1d3564 20{
21 int cycles = Pico32x.regs[0x32 / 2];
22 int frame_samples;
1d7a28a7 23 int tmp, i;
db1d3564 24
25 cycles = (cycles - 1) & 0x0fff;
26 if (cycles < 500) {
1b3f5844 27 elprintf(EL_32X|EL_PWM|EL_ANOMALY, "pwm: low cycle value: %d", cycles + 1);
db1d3564 28 cycles = 500;
29 }
30 pwm_cycles = cycles;
31 pwm_mult = 0x10000 / cycles;
32 if (Pico.m.pal)
33 frame_samples = OSC_PAL / 7 * 3 / 50 / cycles;
34 else
35 frame_samples = OSC_NTSC / 7 * 3 / 60 / cycles;
36
37 pwm_line_samples = (frame_samples << 16) / scanlines_total;
1d7a28a7 38
39 // SH2 timer step
40 for (i = 0; i < 2; i++) {
41 tmp = PREG8(Pico32xMem->sh2_peri_regs[i], 0x80) & 7;
42 // Sclk cycles per timer tick
43 if (tmp)
44 cycles = 0x20 << tmp;
45 else
46 cycles = 2;
47 if (Pico.m.pal)
48 tmp = OSC_PAL / 7 * 3 / 50 / scanlines_total;
49 else
50 tmp = OSC_NTSC / 7 * 3 / 60 / scanlines_total;
51 timer_line_ticks[i] = (tmp << 16) / cycles;
52 elprintf(EL_32X, "timer_line_ticks[%d] = %.3f", i, (double)timer_line_ticks[i] / 0x10000);
53 }
db1d3564 54}
55
1d7a28a7 56// PWM irq for every tm samples
1f1ff763 57void p32x_timers_do(int line_call)
db1d3564 58{
1d7a28a7 59 int tm, cnt, i;
07e5dbab 60
61 if (PicoOpt & POPT_EN_PWM)
62 {
63 tm = (Pico32x.regs[0x30 / 2] & 0x0f00) >> 8;
64 if (tm != 0) {
1f1ff763 65 if (line_call)
07e5dbab 66 Pico32x.pwm_irq_sample_cnt += pwm_line_samples;
67 if (Pico32x.pwm_irq_sample_cnt >= (tm << 16)) {
68 Pico32x.pwm_irq_sample_cnt -= tm << 16;
69 Pico32x.sh2irqs |= P32XI_PWM;
1f1ff763 70 p32x_update_irls(!line_call);
07e5dbab 71 }
1d7a28a7 72 }
73 }
74
1f1ff763 75 if (!line_call)
1d7a28a7 76 return;
db1d3564 77
1d7a28a7 78 for (i = 0; i < 2; i++) {
79 void *pregs = Pico32xMem->sh2_peri_regs[i];
80 if (PREG8(pregs, 0x80) & 0x20) { // TME
81 cnt = PREG8(pregs, 0x81);
82 cnt += timer_line_ticks[i];
83 if (cnt >= 0x100) {
84 int level = PREG8(pregs, 0xe3) >> 4;
85 int vector = PREG8(pregs, 0xe4) & 0x7f;
86 elprintf(EL_32X, "%csh2 WDT irq (%d, %d)", i ? 's' : 'm', level, vector);
87 sh2_internal_irq(&sh2s[i], level, vector);
88 }
89 cnt &= 0xff;
90 PREG8(pregs, 0x81) = cnt;
91 }
db1d3564 92 }
93}
94
95unsigned int p32x_pwm_read16(unsigned int a)
96{
97 unsigned int d = 0;
98 int predict;
99
100 a &= 0x0e;
101 switch (a) {
102 case 0: // control
103 case 2: // cycle
104 return Pico32x.regs[(0x30 + a) / 2];
105
106 case 4: // L ch
107 case 6: // R ch
108 case 8: // MONO
109 predict = (pwm_line_samples * Pico.m.scanline) >> 16;
1b3f5844 110 elprintf(EL_PWM, "pwm: read status: ptr %d/%d, predict %d",
db1d3564 111 pwm_frame_smp_cnt, (pwm_line_samples * scanlines_total) >> 16, predict);
112 if (pwm_frame_smp_cnt > predict + 3)
113 d |= P32XP_FULL;
114 else if (pwm_frame_smp_cnt == 0 || pwm_frame_smp_cnt < predict - 1)
115 d |= P32XP_EMPTY;
116 break;
117 }
118
119 return d;
120}
121
122void p32x_pwm_write16(unsigned int a, unsigned int d)
123{
124 a &= 0x0e;
125 if (a == 0) // control
126 Pico32x.regs[0x30 / 2] = d;
127 else if (a == 2) { // cycle
128 Pico32x.regs[0x32 / 2] = d & 0x0fff;
1d7a28a7 129 p32x_timers_recalc();
db1d3564 130 Pico32x.pwm_irq_sample_cnt = 0; // resets?
131 }
132 else if (a <= 8) {
133 d &= 0x0fff;
134 if (d > pwm_cycles)
135 d = pwm_cycles;
136 d = (d - pwm_cycles / 2) * pwm_mult;
137
138 if (a < 6) // L ch
139 Pico32xMem->pwm[pwm_ptr * 2] = d;
140 else if (a == 6) // R ch
141 Pico32xMem->pwm[pwm_ptr * 2 + 1] = d;
142 else // MONO
143 Pico32xMem->pwm[pwm_ptr * 2] = Pico32xMem->pwm[pwm_ptr * 2 + 1] = d;
144
145 if (a >= 6) { // R or MONO
146 pwm_frame_smp_cnt++;
147 pwm_ptr = (pwm_ptr + 1) & (PWM_BUFF_LEN - 1);
1b3f5844 148 elprintf(EL_PWM, "pwm: smp_cnt %d, ptr %d, smp %x", pwm_frame_smp_cnt, pwm_ptr, d);
db1d3564 149 }
150 }
151}
152
153void p32x_pwm_update(int *buf32, int length, int stereo)
154{
db1d3564 155 short *pwmb;
156 int step;
157 int p = 0;
158
159 if (pwm_ptr <= 16) // at least some samples..
160 return;
161
162 step = (pwm_ptr << 16) / length; // FIXME: division..
163 pwmb = Pico32xMem->pwm;
164
165 if (stereo)
166 {
167 while (length-- > 0) {
168 *buf32++ += pwmb[0];
169 *buf32++ += pwmb[1];
170
171 p += step;
172 pwmb += (p >> 16) * 2;
173 p &= 0xffff;
174 }
175 }
176 else
177 {
178 while (length-- > 0) {
179 *buf32++ += pwmb[0];
180
181 p += step;
182 pwmb += (p >> 16) * 2;
183 p &= 0xffff;
184 }
185 }
186
1b3f5844 187 elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d",
db1d3564 188 pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2);
189
190 pwm_ptr = 0;
191}
192