clarify PicoDrive's license
[picodrive.git] / pico / 32x / pwm.c
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  */
8 #include "../pico_int.h"
9
10 static int pwm_line_samples;
11 static int pwm_cycles;
12 static int pwm_mult;
13 static int pwm_ptr;
14 int pwm_frame_smp_cnt;
15
16 static int timer_line_ticks[2];
17
18 // timers. This includes PWM timer in 32x and internal SH2 timers
19 void p32x_timers_recalc(void)
20 {
21   int cycles = Pico32x.regs[0x32 / 2];
22   int frame_samples;
23   int tmp, i;
24
25   cycles = (cycles - 1) & 0x0fff;
26   if (cycles < 500) {
27     elprintf(EL_32X|EL_PWM|EL_ANOMALY, "pwm: low cycle value: %d", cycles + 1);
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;
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   }
54 }
55
56 // PWM irq for every tm samples
57 void p32x_timers_do(int line_call)
58 {
59   int tm, cnt, i;
60
61   if (PicoOpt & POPT_EN_PWM)
62   {
63     tm = (Pico32x.regs[0x30 / 2] & 0x0f00) >> 8;
64     if (tm != 0) {
65       if (line_call)
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;
70         p32x_update_irls(!line_call);
71       }
72     }
73   }
74
75   if (!line_call)
76     return;
77
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     }
92   }
93 }
94
95 unsigned 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;
110       elprintf(EL_PWM, "pwm: read status: ptr %d/%d, predict %d",
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
122 void 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;
129     p32x_timers_recalc();
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);
148         elprintf(EL_PWM, "pwm: smp_cnt %d, ptr %d, smp %x", pwm_frame_smp_cnt, pwm_ptr, d);
149     }
150   }
151 }
152
153 void p32x_pwm_update(int *buf32, int length, int stereo)
154 {
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
187   elprintf(EL_PWM, "pwm_update: pwm_ptr %d, len %d, step %04x, done %d",
188     pwm_ptr, length, step, (pwmb - Pico32xMem->pwm) / 2);
189
190   pwm_ptr = 0;
191 }
192