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