]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gb/apu.c
add libemu
[plan9front.git] / sys / src / games / gb / apu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <emu.h>
5 #include "dat.h"
6 #include "fns.h"
7
8 double TAU = 25000;
9
10 Event evsamp;
11 extern Event evenv;
12 s16int sbuf[2*4000], *sbufp;
13 enum {
14         Freq = 44100,
15         SRATEDIV = FREQ / Freq
16 };
17 static int fd;
18
19 u16int envmod;
20 u8int sweepen, sweepcalc, sweepctr;
21 u16int sweepfreq;
22 typedef struct chan chan;
23 struct chan {
24         u8int *env, *freq;
25         int per;
26         u16int len;
27         u8int n, ectr;
28         u8int vol, ctr, samp;
29 };
30 u8int wpos;
31 u16int lfsr;
32 u8int apustatus;
33 ulong waveclock;
34 u8int wavebuf;
35 double samp[2];
36
37 chan sndch[4] = {
38         {
39                 .n = 0,
40                 .env = reg + NR12,
41                 .freq = reg + NR14,
42                 .per = 8 * 2048,
43         },
44         {
45                 .n = 1,
46                 .env = reg + NR22,
47                 .freq = reg + NR24,
48                 .per = 8 * 2048,
49         },
50         {
51                 .n = 2,
52         },
53         {
54                 .n = 3,
55                 .env = reg + NR42,
56                 .freq = reg + NR44,
57                 .per = 32
58         }
59 };
60 Event chev[4] = {
61         {.aux = &sndch[0]},
62         {.aux = &sndch[1]},
63         {.aux = &sndch[2]},
64         {.aux = &sndch[3]}
65 };
66
67 Var apuvars[] = {
68         VAR(apustatus), VAR(envmod), VAR(sweepen), VAR(sweepcalc),
69         VAR(sweepctr), VAR(sweepfreq), VAR(wpos), VAR(lfsr), VAR(waveclock), VAR(wavebuf),
70         VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].per), VAR(sndch[0].ctr), VAR(sndch[0].vol), VAR(sndch[0].samp),
71         VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].per), VAR(sndch[1].ctr), VAR(sndch[1].vol), VAR(sndch[1].samp),
72         VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].per), VAR(sndch[2].vol), VAR(sndch[2].samp),
73         VAR(sndch[3].ectr), VAR(sndch[3].len), VAR(sndch[3].per), VAR(sndch[3].vol), VAR(sndch[3].samp),
74         {nil, 0, 0},
75 };
76
77 static void
78 rate(chan *c, u16int v)
79 {
80         switch(c->n){
81         case 0: case 1:
82                 c->per = 8 * (2048 - (v & 0x7ff));
83                 break;
84         case 2:
85                 c->per = 4 * (2048 - (v & 0x7ff));
86                 break;
87         case 3:
88                 c->per = 32;
89                 if((v & 7) != 0)
90                         c->per *= v & 7;
91                 else
92                         c->per >>= 1;
93                 c->per <<= (v >> 4 & 15);
94         }
95 }
96
97 static void
98 filter(int t)
99 {
100         static int ov0, ov1;
101         static u32int oclock;
102         double e;
103         u8int cntl, cnth;
104         int i, v;
105
106         e = exp((clock + t - oclock) * -(TAU / FREQ));
107         samp[0] = e * samp[0] + (1 - e) * ov0;
108         samp[1] = e * samp[1] + (1 - e) * ov1;
109         oclock = clock + t;
110         cntl = reg[NR50];
111         cnth = reg[NR51];
112         ov0 = 0;
113         ov1 = 0;
114         for(i = 0; i < 4; i++){
115                 if(i == 2 ? ((reg[NR30] & 0x80) == 0) : ((*sndch[i].env & 0xf8) == 0))
116                         continue;
117                 v = sndch[i].samp * 2 - 15;
118                 if((cnth & 1<<i) != 0)
119                         ov0 += v;
120                 if((cnth & 1<<4<<i) != 0)
121                         ov1 += v;
122         }
123         ov0 *= 1 + (cntl & 7);
124         ov1 *= 1 + (cntl >> 4 & 7);
125 }
126
127 static void
128 chansamp(chan *c, int t)
129 {
130         u8int ov;
131         
132         ov = c->samp;
133         switch(c->n){
134         case 0: case 1:
135                 c->samp = c->vol;
136                 switch(reg[NR21] >> 6){
137                 case 0: if(c->ctr < 7) c->samp = 0; break;
138                 case 1: if(c->ctr < 6) c->samp = 0; break;
139                 case 2: if(c->ctr < 4) c->samp = 0; break;
140                 case 3: if(c->ctr >= 6) c->samp = 0; break;
141                 }
142                 break;
143         case 2:
144                 if((apustatus & 1<<4) == 0){
145                         c->samp = 0;
146                         break;
147                 }
148                 c->samp = wavebuf;
149                 if((wpos & 1) == 0)
150                         c->samp >>= 4;
151                 else
152                         c->samp &= 0xf;
153                 if((reg[NR32] & 3<<5) == 0)
154                         c->samp = 0;
155                 else
156                         c->samp = c->samp >> (reg[NR32] >> 5 & 3) - 1;
157                 break;
158         case 3:
159                 c->samp = (lfsr & 1) != 0 ? 0 : c->vol;
160         }
161         if(ov != c->samp)
162                 filter(t);
163 }
164
165 void
166 chantick(void *vc)
167 {
168         chan *c;
169         u16int l;
170         
171         c = vc;
172         switch(c->n){
173         case 0: case 1:
174                 c->ctr = c->ctr - 1 & 7;
175                 break;
176         case 2:
177                 wpos = wpos + 1 & 31;
178                 wavebuf = reg[WAVE + (wpos >> 1)];
179                 waveclock = clock;
180                 break;
181         case 3:
182                 l = lfsr;
183                 lfsr >>= 1;
184                 if(((l ^ lfsr) & 1) != 0)
185                         if((reg[NR43] & 1<<3) != 0)
186                                 lfsr |= 0x40;
187                         else
188                                 lfsr |= 0x4000;
189                 break;
190         }
191         chansamp(c, chev[c->n].time);
192         addevent(&chev[c->n], c->per);
193 }
194
195 static void
196 env(chan *c, int t)
197 {
198         if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<6) != 0)
199                 if(--c->len == 0){
200                         apustatus &= ~(1<<c->n);
201                         c->vol = 0;
202                         chansamp(c, t);
203                         return;
204                 }
205         if((apustatus & 1<<c->n) == 0 || (envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0)
206                 return;
207         c->ectr = *c->env & 7;
208         if((*c->env & 1<<3) != 0){
209                 if(c->vol < 15){
210                         c->vol++;
211                         chansamp(c, t);
212                 }
213         }else
214                 if(c->vol > 0){
215                         c->vol--;
216                         chansamp(c, t);
217                 }
218 }
219
220 static void
221 sweep(int wb, int t)
222 {
223         u16int fr;
224         int d;
225         u16int cnt;
226         
227         cnt = reg[NR10];
228         d = sweepfreq >> (cnt & 7);
229         if((cnt & 1<<3) != 0)
230                 d = -d;
231         fr = sweepfreq + d;
232         sweepcalc |= cnt;
233         if(fr > 2047){
234                 sndch[0].len = 0;
235                 sndch[0].vol = 0;
236                 chansamp(&sndch[0], t);
237                 apustatus &= ~1;
238                 sweepen = 0;
239         }else if(wb && (cnt & 7) != 0){
240                 sweepfreq = fr;
241                 reg[NR13] = fr;
242                 reg[NR14] = reg[NR14] & 0xf8 | fr >> 8;
243                 rate(&sndch[0], fr);
244                 sweep(0, t);
245         }
246 }
247
248 void
249 sndstart(chan *c, u8int v)
250 {
251         u8int cnt;
252
253         filter(0);
254         c->vol = *c->env >> 4;
255         c->ectr = *c->env & 7;
256         if(c->len == 0)
257                 c->len = 64;
258         apustatus |= 1<<c->n;
259         if(c == sndch){
260                 cnt = reg[NR10];
261                 sweepen = (cnt & 0x07) != 0 || (cnt & 0x70) != 0;
262                 sweepctr = cnt >> 4 & 7;
263                 sweepctr += sweepctr - 1 & 8;
264                 sweepfreq = v << 8 & 0x700 | reg[NR13];
265                 sweepcalc = 0;
266                 if((cnt & 0x07) != 0)
267                         sweep(0, 0);
268         }
269         if((*c->freq & 0x40) == 0 && (v & 0x40) != 0 && (envmod & 1) != 0 && --c->len == 0 || (*c->env & 0xf8) == 0){
270                 apustatus &= ~(1<<c->n);
271                 c->vol = 0;
272         }
273         chansamp(c, 0);
274 }
275
276 void
277 envtick(void *)
278 {
279         env(&sndch[0], evenv.time);
280         env(&sndch[1], evenv.time);
281         if((envmod & 1) == 0 && sndch[2].len > 0 && (reg[NR34] & 0x40) != 0)
282                 if(--sndch[2].len == 0){
283                         apustatus &= ~4;
284                         delevent(&chev[2]);
285                 }
286         env(&sndch[3], evenv.time);
287         if((envmod & 3) == 2 && sweepen && --sweepctr == 0){
288                 sweepctr = reg[NR10] >> 4 & 7;
289                 sweepctr += sweepctr - 1 & 8;
290                 if((reg[NR10] & 0x70) != 0)
291                         sweep(1, evenv.time);
292         }
293         envmod++;
294         addevent(&evenv, FREQ / 512);
295 }
296
297 void
298 sampletick(void *)
299 {
300         filter(evsamp.time);
301         if(sbufp < sbuf + nelem(sbuf)){
302                 sbufp[0] = samp[0] * 30;
303                 sbufp[1] = samp[1] * 30;
304                 sbufp += 2;
305         }
306         addevent(&evsamp, SRATEDIV);
307 }
308
309 void
310 sndwrite(u8int a, u8int v)
311 {
312         static u16int thr[4] = {0x2000, 0x4000, 0x8000, 0xC000};
313         static u8int clrreg[] = {
314                 0x80, 0x3f, 0x00, 0xff, 0xbf,
315                 0xff, 0x3f, 0x00, 0xff, 0xbf,
316                 0x7f, 0xff, 0x9f, 0xff, 0xbf,
317                 0xff, 0xff, 0x00, 0x00, 0xbf,
318                 0x00, 0x00
319         };
320         
321         if((reg[NR52] & 0x80) == 0 && a != NR52 && ((mode & CGB) != 0 || a != NR11 && a != NR21 && a != NR31 && a != NR41))
322                 return;
323         switch(a){
324         case NR10:
325                 if((sweepcalc & 0x08) != 0 && (reg[NR10] & ~v & 0x08) != 0){
326                         sndch[0].vol = 0;
327                         apustatus &= ~1;
328                         sweepcalc = 0;
329                 }
330                 break;
331         case NR11:
332                 sndch[0].len = 64 - (v & 63);
333                 break;
334         case NR12:
335                 if((v & 0xf8) == 0){
336                         sndch[0].vol = 0;
337                         apustatus &= ~1;
338                 }
339                 break;
340         case NR13:
341                 rate(&sndch[0], reg[NR14] << 8 & 0x700 | v);
342                 break;
343         case NR14:
344                 rate(&sndch[0], v << 8 & 0x700 | reg[NR13]);
345                 if((v & 1<<7) != 0)
346                         sndstart(&sndch[0], v);
347                 break;
348         case NR21:
349                 sndch[1].len = 64 - (v & 63);
350                 break;
351         case NR22:
352                 if((v & 0xf8) == 0){
353                         sndch[1].vol = 0;
354                         apustatus &= ~2;
355                 }
356                 break;
357         case NR23:
358                 rate(&sndch[1], reg[NR24] << 8 & 0x700 | v);
359                 break;
360         case NR24:
361                 rate(&sndch[1], v << 8 & 0x700 | reg[NR23]);
362                 if((v & 1<<7) != 0)
363                         sndstart(&sndch[1], v);
364                 break;
365         case NR30:
366                 if((v & 0x80) == 0){
367                         apustatus &= ~4;
368                         delevent(&chev[2]);
369                 }
370                 break;
371         case NR31:
372                 sndch[2].len = 256 - (v & 0xff);
373                 break;
374         case NR33:
375                 rate(&sndch[2], reg[NR34] << 8 & 0x700 | v);
376                 break;
377         case NR34:
378                 rate(&sndch[2], v << 8 & 0x700 | reg[NR33]);
379                 if((v & 0x80) != 0){
380                         if(sndch[2].len == 0)
381                                 sndch[2].len = 256;
382                         wpos = 0;
383                         if((reg[NR30] & 0x80) != 0){
384                                 apustatus |= 4;
385                                 delevent(&chev[2]);
386                                 addevent(&chev[2], sndch[2].per);
387                         }
388                 }
389                 break;
390         case NR41:
391                 sndch[3].len = 64 - (v & 63);
392                 break;
393         case NR42:
394                 if((v & 0xf8) == 0){
395                         sndch[3].vol = 0;
396                         apustatus &= ~8;
397                 }
398                 break;
399         case NR43:
400                 rate(&sndch[3], v);
401                 break;
402         case NR44:
403                 if((v & 1<<7) != 0){
404                         if((reg[NR43] & 1<<3) != 0)
405                                 lfsr = 0x7f;
406                         else
407                                 lfsr = 0x7fff;
408                         sndstart(&sndch[3], v);
409                 }
410                 break;
411         case NR50: case NR51:
412                 filter(0);
413                 break;
414         case NR52:
415                 apustatus = v & 0xf0 | apustatus & 0x0f;
416                 if((v & 0x80) == 0){
417                         memcpy(reg + NR10, clrreg, NR52 - NR10);
418                         if((mode & CGB) != 0){
419                                 sndch[0].len = 0;
420                                 sndch[1].len = 0;
421                                 sndch[2].len = 0;
422                                 sndch[3].len = 0;
423                                 apustatus = 0;
424                                 delevent(&chev[2]);
425                         }
426                 }else if((reg[NR52] & 0x80) == 0){
427                         envmod = 0;
428                         delevent(&evenv);
429                         addevent(&evenv, FREQ / 512);
430                         sndch[0].ctr = 0;
431                         sndch[1].ctr = 0;
432                 }
433         }
434         reg[a] = v;
435 }
436
437 u8int
438 waveread(u8int a)
439 {
440         if((apustatus & 4) != 0)
441                 if((mode & CGB) != 0 || clock - waveclock == 0)
442                         return wavebuf;
443                 else
444                         return 0xff;
445         return reg[WAVE + a];
446 }
447
448 void
449 wavewrite(u8int a, u8int v)
450 {
451         reg[WAVE + a] = v;
452 }
453
454 void
455 audioinit(void)
456 {
457         fd = open("/dev/audio", OWRITE);
458         if(fd < 0)
459                 sysfatal("open: %r");
460         sbufp = sbuf;
461         evsamp.f = sampletick;
462         addevent(&evsamp, SRATEDIV);
463         addevent(&chev[0], 8 * 2048);
464         addevent(&chev[1], 8 * 2048);
465         addevent(&chev[3], 8 * 2048);
466 }
467
468 int
469 audioout(void)
470 {
471         int rc;
472         static int cl;
473
474         if(sbufp == nil)
475                 return -1;
476         if(sbufp == sbuf)
477                 return 0;
478         cl = clock;
479         rc = warp10 ? (sbufp - sbuf) * 2 : write(fd, sbuf, (sbufp - sbuf) * 2);
480         if(rc > 0)
481                 sbufp -= (rc+1)/2;
482         if(sbufp < sbuf)
483                 sbufp = sbuf;
484         return 0;
485 }