]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/apu.c
games/nes: basic audio support, battery backup, bug fixes
[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 "dat.h"
6 #include "fns.h"
7
8 u8int apuseq, apuctr[10];
9 static int fd;
10
11 enum { RATE = 44100 };
12
13 int
14 targperiod(int i)
15 {
16         int m, p, t;
17         
18         m = mem[0x4001 + i * 4];
19         p = mem[0x4002 + i * 4];
20         p |= (mem[0x4003 + i * 4] & 7) << 8;
21         t = p >> (m & 7);
22         if((m & 8) != 0){
23                 if(i == 0 && t != 0)
24                         t--;
25                 t = p - t;
26         }else
27                 t += p;
28         return t;
29 }
30
31 static void
32 declen(void)
33 {
34         int i, m, p;
35         u8int *a;
36         
37         for(i = 0; i < 4; i++){
38                 m = mem[0x4000 + i * 4];
39                 if(i == 2)
40                         m >>= 2;
41                 if((m & 0x20) != 0)
42                         continue;
43                 if(apuctr[i] != 0)
44                         apuctr[i]--;
45         }
46         for(i = 0, a = apuctr + 8; i < 2; i++, a++){
47                 m = mem[0x4001 + i * 4];
48                 if((m & 0x80) != 0 && (m & 0x07) != 0 && (*a & 7) == 0){ 
49                         p = targperiod(i);
50                         if(p <= 0x7ff){
51                                 mem[0x4002 + i * 4] = p;
52                                 mem[0x4003 + i * 4] = p >> 8;
53                         }
54                 }
55                 if((*a & 0x80) != 0 || (*a & 7) == 0 && (m & 0x80) != 0)
56                         *a = (m & 0x70) >> 4;
57                 else if(*a != 0)
58                         (*a)--;
59         }
60 }
61
62 static void
63 doenv(void)
64 {
65         int i, m;
66         u8int *a;
67         
68         for(i = 0, a = apuctr + 4; i < 4; i++, a++){
69                 if(i == 2)
70                         continue;
71                 m = mem[0x4000 + 4 * i];
72                 if((*a & 0x80) != 0)
73                         *a = *a & 0x70 | 0x0f;
74                 else if(*a == 0){
75                         if((m & 0x20) != 0)
76                                 *a |= 0x0f;
77                 }else
78                         (*a)--;
79         }
80         a = apuctr + 6;
81         if((*a & 0x80) != 0)
82                 *a = mem[0x4008];
83         else if(*a != 0)
84                 (*a)--;
85 }
86
87 void
88 apustep(void)
89 {
90         int mode, len, env;
91         
92         mode = mem[APUFRAME];
93         if((mode & 0x80) != 0){
94                 if(apuseq >= 4){
95                         env = len = 0;
96                         apuseq = 0;
97                 }else{
98                         env = 1;
99                         len = (apuseq & 1) == 0;
100                         apuseq++;
101                 }
102         }else{
103                 env = 1;
104                 len = (apuseq & 1) != 0;
105                 if(apuseq >= 3){
106                         if((mode & 0x40) == 0)
107                                 irq |= IRQFRAME;
108                         apuseq = 0;
109                 }else
110                         apuseq++;
111         }
112         if(len)
113                 declen();
114         if(env)
115                 doenv();
116 }
117
118 static int
119 freq(int i)
120 {
121         int f;
122         
123         f = mem[0x4002 + 4 * i];
124         f |= (mem[0x4003 + 4 * i] & 0x7) << 8;
125         return f;
126 }
127
128 static int
129 pulse(int i)
130 {
131         static int c[2];
132         int m, s, f;
133
134         f = freq(i);
135         if(f < 8 || targperiod(i) > 0x7ff)
136                 f = -1;
137         else
138                 f = muldiv(16 * (f + 1), RATE, FREQ/12);
139         if(c[i] >= f)
140                 c[i] = 0;
141         else
142                 c[i]++;
143         m = mem[0x4000 + 4 * i];
144         if((m & 0x10) != 0)
145                 s = m;
146         else
147                 s = apuctr[i+4];
148         s &= 0x0f;
149         if(c[i] >= f/2 || apuctr[i] == 0)
150                 s = 0;
151         return s;
152 }
153
154 static int
155 tri(void)
156 {
157         static int c;
158         int f, i;
159         
160         f = freq(2);
161         if(f <= 2)
162                 return 7;
163         f = muldiv(32 * (f + 1), RATE, FREQ / 12);
164         if(c >= f)
165                 c = 0;
166         else
167                 c++;
168         i = 32 * c / f;
169         i ^= (i < 16) ? 0xf : 0x10;
170         if(apuctr[2] == 0 || (apuctr[6] & 0x7f) == 0)
171                 return 0;
172         return i;
173 }
174
175 static int
176 noise(void)
177 {
178         static int c, r=1;
179         int m, f;
180         static int per[] = {
181                 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0,
182                 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4,
183         };
184
185         m = mem[0x400E];
186         f = muldiv(per[m & 0x0f], RATE * 1000, FREQ/24);
187         c += 1000;
188         while(c >= f){
189                 r |= ((r ^ (r >> ((m & 0x80) != 0 ? 6 : 1))) & 1) << 15;
190                 r >>= 1;
191                 c -= f;
192         }
193         if(apuctr[3] == 0 || (r & 1) != 0)
194                 return 0;
195         m = mem[0x400C];
196         if((m & 0x10) != 0)
197                 return m & 0xf;
198         return apuctr[7] & 0xf;
199 }
200
201 static int
202 dmc(void)
203 {
204         return 0;
205 }
206
207 static void
208 sample(short *s)
209 {
210         double d;
211         
212         d = 95.88 / (8128.0 / (0.01 + pulse(0) + pulse(1)) + 100);
213         d += 159.79 / (1.0 / (0.01 + tri()/8227.0 + noise()/12241.0 + dmc()/22638.0) + 100.0);
214         *s++ = d * 20000;
215         *s = d * 20000;
216 }
217
218 static void
219 audioproc(void *)
220 {
221         static short samples[500 * 2];
222         int i;
223
224         for(;;){
225                 if(paused)
226                         memset(samples, 0, sizeof samples);
227                 else
228                         for(i = 0; i < sizeof samples/4; i++)
229                                 sample(samples + 2 * i);
230                 write(fd, samples, sizeof samples);
231         }
232 }
233
234 void
235 initaudio(void)
236 {
237         fd = open("/dev/audio", OWRITE);
238         if(fd < 0)
239                 return;
240         proccreate(audioproc, nil, 8192);
241 }
242
243 u8int apulen[32] = {
244         0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
245         0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
246         0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
247         0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E,
248 };