]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/apu.c
52e8e36b534ee768b4d31518d06fc48986b01b7f
[plan9front.git] / sys / src / games / nes / apu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <emu.h>
6 #include "dat.h"
7 #include "fns.h"
8
9 enum { MAXBUF = 2000 };
10
11 enum {
12         LEN = 0,
13         ENV = 4,
14         TRILIN = 6,
15         SWEEP = 8,
16         RELOAD = 10,
17         DMCCTR = 11,
18         DMCSHFT = 12,
19 };
20 u8int apuseq, apuctr[13];
21 u16int dmcaddr, dmccnt;
22 static int fd;
23 static short sbuf[2*MAXBUF], *sbufp;
24
25 int
26 targperiod(int i)
27 {
28         int m, p, t;
29         
30         m = mem[0x4001 + i * 4];
31         p = mem[0x4002 + i * 4];
32         p |= (mem[0x4003 + i * 4] & 7) << 8;
33         t = p >> (m & 7);
34         if((m & 8) != 0){
35                 if(i == 0 && t != 0)
36                         t--;
37                 t = p - t;
38         }else
39                 t += p;
40         return t;
41 }
42
43 static void
44 declen(void)
45 {
46         int i, m, p;
47         u8int *a;
48         
49         for(i = 0; i < 4; i++){
50                 m = mem[0x4000 + i * 4];
51                 if(i == 2)
52                         m >>= 2;
53                 if((m & 0x20) != 0)
54                         continue;
55                 if(apuctr[LEN + i] != 0)
56                         apuctr[LEN + i]--;
57         }
58         for(i = 0, a = apuctr + SWEEP; i < 2; i++, a++){
59                 m = mem[0x4001 + i * 4];
60                 if((m & 0x80) != 0 && (m & 0x07) != 0 && (*a & 7) == 0){ 
61                         p = targperiod(i);
62                         if(p <= 0x7ff){
63                                 mem[0x4002 + i * 4] = p;
64                                 mem[0x4003 + i * 4] = p >> 8;
65                         }
66                 }
67                 if((*a & 0x80) != 0 || (*a & 7) == 0 && (m & 0x80) != 0)
68                         *a = (m & 0x70) >> 4;
69                 else if(*a != 0)
70                         (*a)--;
71         }
72 }
73
74 static void
75 doenv(void)
76 {
77         int i, m;
78         u8int *a;
79         
80         for(i = 0, a = apuctr + ENV; i < 4; i++, a++){
81                 if(i == 2)
82                         continue;
83                 m = mem[0x4000 + 4 * i];
84                 if((apuctr[RELOAD] & (1<<i)) != 0){
85                         *a = 0xf0 | m & 0x0f;
86                         apuctr[RELOAD] &= ~(1<<i);
87                 }else if((*a & 0x0f) == 0){
88                         *a |= m & 0x0f;
89                         if((*a & 0xf0) == 0){
90                                 if((m & 0x20) != 0)
91                                         *a |= 0xf0;
92                         }else
93                                 *a -= 0x10;
94                 }else
95                         (*a)--;
96         }
97         a = apuctr + TRILIN;
98         if((apuctr[RELOAD] & (1<<2)) != 0)
99                 *a = mem[0x4008] & 0x7f;
100         else if(*a != 0)
101                 (*a)--;
102         if((mem[0x4008] & 0x80) == 0)
103                 apuctr[RELOAD] &= ~(1<<2);
104 }
105
106 void
107 apustep(void)
108 {
109         int mode, len, env;
110         
111         mode = mem[APUFRAME];
112         if((mode & 0x80) != 0){
113                 if(apuseq >= 4){
114                         env = len = 0;
115                         apuseq = 0;
116                 }else{
117                         env = 1;
118                         len = (apuseq & 1) == 0;
119                         apuseq++;
120                 }
121         }else{
122                 env = 1;
123                 len = (apuseq & 1) != 0;
124                 if(apuseq >= 3){
125                         if((mode & 0x40) == 0)
126                                 irq |= IRQFRAME;
127                         apuseq = 0;
128                 }else
129                         apuseq++;
130         }
131         if(len)
132                 declen();
133         if(env)
134                 doenv();
135 }
136
137 static int
138 freq(int i)
139 {
140         int f;
141         
142         f = mem[0x4002 + 4 * i];
143         f |= (mem[0x4003 + 4 * i] & 0x7) << 8;
144         return f;
145 }
146
147 static int
148 pulse(int i)
149 {
150         static int c[2];
151         int m, s, f;
152
153         f = freq(i);
154         if(f < 8 || targperiod(i) > 0x7ff)
155                 f = -1;
156         else
157                 f = muldiv(16 * (f + 1), RATE, FREQ/12);
158         if(c[i] >= f)
159                 c[i] = 0;
160         else
161                 c[i]++;
162         m = mem[0x4000 + 4 * i];
163         if((m & 0x10) != 0)
164                 s = m & 0x0f;
165         else
166                 s = apuctr[ENV + i] >> 4;
167         if(c[i] >= f/2 || apuctr[LEN + i] == 0)
168                 s = 0;
169         return s;
170 }
171
172 static int
173 tri(void)
174 {
175         static int c;
176         int f, i;
177         
178         f = freq(2);
179         if(f <= 2)
180                 return 7;
181         f = muldiv(32 * (f + 1), RATE, FREQ / 12);
182         if(c >= f)
183                 c = 0;
184         else
185                 c++;
186         i = 32 * c / f;
187         i ^= (i < 16) ? 0xf : 0x10;
188         if(apuctr[LEN + 2] == 0 || (apuctr[TRILIN] & 0x7f) == 0)
189                 return 0;
190         return i;
191 }
192
193 static int
194 noise(void)
195 {
196         static int c, r=1;
197         int m, f;
198         static int per[] = {
199                 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0,
200                 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4,
201         };
202
203         m = mem[0x400E];
204         f = muldiv(per[m & 0x0f], RATE * 1000, FREQ/24);
205         c += 1000;
206         while(c >= f){
207                 r |= ((r ^ (r >> ((m & 0x80) != 0 ? 6 : 1))) & 1) << 15;
208                 r >>= 1;
209                 c -= f;
210         }
211         if(apuctr[LEN + 3] == 0 || (r & 1) != 0)
212                 return 0;
213         m = mem[0x400C];
214         if((m & 0x10) != 0)
215                 return m & 0xf;
216         return apuctr[ENV + 3] >> 4;
217 }
218
219 static int
220 dmc(void)
221 {
222         return mem[DMCBUF];
223 }
224
225 void
226 audiosample(void)
227 {
228         double d;
229         
230         if(sbufp == nil)
231                 return;
232         d = 95.88 / (8128.0 / (0.01 + pulse(0) + pulse(1)) + 100);
233         d += 159.79 / (1.0 / (0.01 + tri()/8227.0 + noise()/12241.0 + dmc()/22638.0) + 100.0);
234         if(sbufp < sbuf + nelem(sbuf) - 1){
235                 *sbufp++ = d * 10000;
236                 *sbufp++ = d * 10000;
237         }
238 }
239
240 void
241 dmcstep(void)
242 {
243         if((apuctr[DMCCTR] & 7) == 0){
244                 apuctr[DMCCTR] = 8;
245                 if(dmccnt != 0){
246                         apuctr[DMCSHFT] = memread(dmcaddr);
247                         if(dmcaddr == 0xFFFF)
248                                 dmcaddr = 0x8000;
249                         else
250                                 dmcaddr++;
251                         if(--dmccnt == 0){
252                                 if((mem[DMCCTRL] & 0x40) != 0){
253                                         dmcaddr = mem[DMCADDR] * 0x40 + 0xC000;
254                                         dmccnt = mem[DMCLEN] * 0x10 + 1;
255                                 }else if((mem[DMCCTRL] & 0x80) != 0)
256                                         irq |= IRQDMC;
257                         }
258                 }else
259                         apuctr[DMCCTR] |= 0x80;
260         }
261         if((apuctr[DMCCTR] & 0x80) == 0){
262                 if((apuctr[DMCSHFT] & 1) != 0){
263                         if(mem[DMCBUF] < 126)
264                                 mem[DMCBUF] += 2;
265                 }else{
266                         if(mem[DMCBUF] > 1)
267                                 mem[DMCBUF] -= 2;
268                 }
269         }
270         apuctr[DMCSHFT] >>= 1;
271         apuctr[DMCCTR]--;
272 }
273
274 int
275 audioout(void)
276 {
277         int rc;
278
279         if(sbufp == nil)
280                 return -1;
281         if(sbufp == sbuf)
282                 return 0;
283         rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
284         if(rc > 0)
285                 sbufp -= (rc+1)/2;
286         if(sbufp < sbuf)
287                 sbufp = sbuf;
288         return 0;
289 }
290
291 void
292 initaudio(void)
293 {
294         fd = open("/dev/audio", OWRITE);
295         if(fd < 0)
296                 return;
297         sbufp = sbuf;
298 }
299
300 u8int apulen[32] = {
301         0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
302         0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
303         0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
304         0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E,
305 };
306
307 u16int dmclen[16] = {
308         0x1AC, 0x17C, 0x154, 0x140, 0x11E, 0x0FE, 0x0E2, 0x0D6,
309         0x0BE, 0x0A0, 0x08E, 0x080, 0x06A, 0x054, 0x048, 0x036,
310 };