32x: watchdog for Star Wars, SCI IRQs for X-men (also needs idle loop hacks)
[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   tm = (Pico32x.regs[0x30 / 2] & 0x0f00) >> 8;
54   if (tm != 0) {
55     if (new_line)
56       Pico32x.pwm_irq_sample_cnt += pwm_line_samples;
57     if (Pico32x.pwm_irq_sample_cnt >= (tm << 16)) {
58       Pico32x.pwm_irq_sample_cnt -= tm << 16;
59       Pico32x.sh2irqs |= P32XI_PWM;
60       p32x_update_irls();
61     }
62   }
63
64   if (!new_line)
65     return;
66
67   for (i = 0; i < 2; i++) {
68     void *pregs = Pico32xMem->sh2_peri_regs[i];
69     if (PREG8(pregs, 0x80) & 0x20) { // TME
70       cnt = PREG8(pregs, 0x81);
71       cnt += timer_line_ticks[i];
72       if (cnt >= 0x100) {
73         int level = PREG8(pregs, 0xe3) >> 4;
74         int vector = PREG8(pregs, 0xe4) & 0x7f;
75         elprintf(EL_32X, "%csh2 WDT irq (%d, %d)", i ? 's' : 'm', level, vector);
76         sh2_internal_irq(&sh2s[i], level, vector);
77       }
78       cnt &= 0xff;
79       PREG8(pregs, 0x81) = cnt;
80     }
81   }
82 }
83
84 unsigned int p32x_pwm_read16(unsigned int a)
85 {
86   unsigned int d = 0;
87   int predict;
88
89   a &= 0x0e;
90   switch (a) {
91     case 0: // control
92     case 2: // cycle
93       return Pico32x.regs[(0x30 + a) / 2];
94
95     case 4: // L ch
96     case 6: // R ch
97     case 8: // MONO
98       predict = (pwm_line_samples * Pico.m.scanline) >> 16;
99       elprintf(EL_PWM, "pwm: read status: ptr %d/%d, predict %d",
100         pwm_frame_smp_cnt, (pwm_line_samples * scanlines_total) >> 16, predict);
101       if (pwm_frame_smp_cnt > predict + 3)
102         d |= P32XP_FULL;
103       else if (pwm_frame_smp_cnt == 0 || pwm_frame_smp_cnt < predict - 1)
104         d |= P32XP_EMPTY;
105       break;
106   }
107
108   return d;
109 }
110
111 void p32x_pwm_write16(unsigned int a, unsigned int d)
112 {
113   a &= 0x0e;
114   if (a == 0) // control
115     Pico32x.regs[0x30 / 2] = d;
116   else if (a == 2) { // cycle
117     Pico32x.regs[0x32 / 2] = d & 0x0fff;
118     p32x_timers_recalc();
119     Pico32x.pwm_irq_sample_cnt = 0; // resets?
120   }
121   else if (a <= 8) {
122     d &= 0x0fff;
123     if (d > pwm_cycles)
124       d = pwm_cycles;
125     d = (d - pwm_cycles / 2) * pwm_mult;
126
127     if       (a < 6) // L ch
128       Pico32xMem->pwm[pwm_ptr * 2] = d;
129     else if (a == 6) // R ch
130       Pico32xMem->pwm[pwm_ptr * 2 + 1] = d;
131     else             // MONO
132       Pico32xMem->pwm[pwm_ptr * 2] = Pico32xMem->pwm[pwm_ptr * 2 + 1] = d;
133
134     if (a >= 6) { // R or MONO
135       pwm_frame_smp_cnt++;
136       pwm_ptr = (pwm_ptr + 1) & (PWM_BUFF_LEN - 1);
137         elprintf(EL_PWM, "pwm: smp_cnt %d, ptr %d, smp %x", pwm_frame_smp_cnt, pwm_ptr, d);
138     }
139   }
140 }
141
142 void p32x_pwm_update(int *buf32, int length, int stereo)
143 {
144   extern int pwm_ptr;
145   short *pwmb;
146   int step;
147   int p = 0;
148
149   if (pwm_ptr <= 16) // at least some samples..
150     return;
151
152   step = (pwm_ptr << 16) / length; // FIXME: division..
153   pwmb = Pico32xMem->pwm;
154
155   if (stereo)
156   {
157     while (length-- > 0) {
158       *buf32++ += pwmb[0];
159       *buf32++ += pwmb[1];
160
161       p += step;
162       pwmb += (p >> 16) * 2;
163       p &= 0xffff;
164     }
165   }
166   else
167   {
168     while (length-- > 0) {
169       *buf32++ += pwmb[0];
170
171       p += step;
172       pwmb += (p >> 16) * 2;
173       p &= 0xffff;
174     }
175   }
176
177   elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d",
178     pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2);
179
180   pwm_ptr = 0;
181 }
182