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