]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/mem.c
games/nes: SUROM support, subtle NMI timing bug fixed
[plan9front.git] / sys / src / games / nes / mem.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 uchar mem[32768];
9 uchar ppuram[16384];
10 uchar oam[256];
11 uchar *prgb[16], *chrb[16];
12 u16int pput, ppuv;
13 u8int ppusx, vrambuf;
14 int vramlatch = 1, keylatch = 0xFF;
15 int prgsh, chrsh, mmc3hack;
16
17 static void
18 nope(int p)
19 {
20         print("unimplemented mapper function %d (mapper %d)\n", p, map);
21 }
22
23 static void
24 nrom(int p, u8int)
25 {
26         if(p >= 0)
27                 return;
28         switch(p){
29         case INIT:
30         case RSTR:
31                 prgb[0] = prg;
32                 if(nprg == 1)
33                         prgb[1] = prg;
34                 else
35                         prgb[1] = prg + 0x4000;
36                 prgsh = 14;
37                 chrb[0] = chr;
38                 chrsh = 13;
39                 break;
40         case SAVE:
41         case SCAN:
42                 break;
43         default:
44                 nope(p);
45         }
46 }
47
48 static void
49 mmc1(int v, u8int p)
50 {
51         static u8int n, s, mode, c0, c1, pr;
52         static int mirrs[] = {MSINGB, MSINGA, MVERT, MHORZ};
53         
54         if(v < 0){
55                 switch(v){
56                 case INIT:
57                         if(nprg > 32)
58                                 sysfatal("bad rom, too much prg rom for mmc1");
59                         mode = 0x0C;
60                         prgsh = 14;
61                         chrsh = 12;
62                         goto t;
63                 case RSTR:
64                         mode = get8();
65                         c0 = get8();
66                         c1 = get8();
67                         pr = get8();
68                         n = get8();
69                         s = get8();
70                         goto t;
71                 case SAVE:
72                         put8(mode);
73                         put8(c0);
74                         put8(c1);
75                         put8(pr);
76                         put8(n);
77                         put8(s);
78                         break;
79                 default:
80                         nope(v);
81                 case SCAN:
82                         break;
83                 }
84                 return;
85         }
86         if((p & 0x80) != 0){
87                 n = 0;
88                 s = 0;
89                 mode |= 0xC;
90                 return;
91         }
92         s |= (p & 1) << 4;
93         if(n < 4){
94                 n++;
95                 s >>= 1;
96                 return;
97         }
98         switch(v & 0xE000){
99         case 0x8000:
100                 mode = s;
101                 mirr = mirrs[mode & 3];
102                 break;
103         case 0xA000:
104                 if(nprg > 16){
105                         pr = s & 0x10 | pr & 0x0f;
106                         pr %= nprg;
107                 }
108                 c0 = s & 0x1f;
109                 c0 %= 2*nchr;
110                 break;
111         case 0xC000:
112                 c1 = s & 0x1f;
113                 c1 %= 2*nchr;
114                 break;
115         case 0xE000:
116                 pr = pr & 0x10 | s & 0x0f;
117                 pr %= nprg;
118                 break;
119         }
120         s = 0;
121         n = 0;
122 t:
123         switch(mode & 0x0c){
124         case 0x08:
125                 prgb[0] = prg;
126                 prgb[1] = prg + pr * 0x4000;
127                 break;
128         case 0x0C:
129                 prgb[0] = prg + pr * 0x4000;
130                 prgb[1] = prg + ((pr & 0x10 | 0x0f) % nprg) * 0x4000;
131                 break;
132         default:
133                 prgb[0] = prg + (pr & 0xfe) * 0x4000;
134                 prgb[1] = prg + (pr | 1) * 0x4000;
135                 break;
136         }
137         if((mode & 0x10) != 0){
138                 chrb[0] = chr + c0 * 0x1000;
139                 chrb[1] = chr + c1 * 0x1000;
140         }else{
141                 chrb[0] = chr + (c0 & 0xfe) * 0x1000;
142                 chrb[1] = chr + (c0 | 1) * 0x1000;
143         }
144 }
145
146 static void
147 uxrom(int p, u8int v)
148 {
149         static u8int b;
150         
151         if(p < 0)
152                 switch(p){
153                 case INIT:
154                         prgsh = 14;
155                         chrsh = 13;
156                         prgb[1] = prg + (nprg - 1) * 0x4000;
157                         chrb[0] = chr;
158                         break;
159                 case SAVE:
160                         put8(b);
161                         return;
162                 case RSTR:
163                         b = get8();
164                         break;
165                 case SCAN:
166                         return;
167                 default:
168                         nope(p);
169                         return;
170                 }
171         else
172                 b = v % nprg;
173         prgb[0] = prg + b * 0x4000;
174 }
175
176 static void
177 cnrom(int p, u8int v)
178 {
179         static u8int b;
180         
181         if(p < 0)
182                 switch(p){
183                 case INIT:
184                         prgsh = 14;
185                         chrsh = 13;
186                         prgb[0] = prg;
187                         if(nprg == 1)
188                                 prgb[1] = prg;
189                         else
190                                 prgb[1] = prg + 0x4000;
191                         break;
192                 case SAVE:
193                         put8(b);
194                         return;
195                 case RSTR:
196                         b = get8();
197                         break;
198                 case SCAN:
199                         return;
200                 default:
201                         nope(p);
202                         return;
203                 }
204         else
205                 b = v % nchr;
206         chrb[0] = chr + b * 0x2000;
207
208 }
209
210 static void
211 mmc3(int p, u8int v)
212 {
213         static u8int m, b[8], l, n, en;
214         int i, j, c;
215
216         if(p < 0){
217                 switch(p){
218                 case INIT:
219                         prgsh = 13;
220                         chrsh = 10;
221                         mmc3hack = 1;
222                         prgb[2] = prg + (2 * nprg - 2) * 0x2000;
223                         prgb[3] = prgb[2] + 0x2000;
224                         goto t;
225                 case SCAN:
226                         if(n == 0)
227                                 n = l;
228                         else
229                                 n--;
230                         if(n == 0 && en)
231                                 irq |= IRQMMC;
232                         return;
233                 case SAVE:
234                         put8(m);
235                         for(i = 0; i < 8; i++)
236                                 put8(b[i]);
237                         put8(l);
238                         put8(n);
239                         put8(en);
240                         return;
241                 case RSTR:
242                         m = get8();
243                         for(i = 0; i < 8; i++)
244                                 b[i] = get8();
245                         l = get8();
246                         n = get8();
247                         en = get8();
248                         goto t;
249                 }
250         }
251         switch(p & 0xE001){
252         case 0x8000:
253                 if(((m ^ v) & 0xc0) != 0){
254                         m = v;
255                         goto t;
256                 }
257                 m = v;
258                 break;
259         case 0x8001:
260                 i = m & 7;
261                 if(i < 6)
262                         v %= 8 * nchr;
263                 else
264                         v %= 2 * nprg;
265                 b[i] = v;
266                 goto t;
267         case 0xA000:
268                 if(mirr == MFOUR)
269                         break;
270                 if(v & 1)
271                         mirr = MHORZ;
272                 else
273                         mirr = MVERT;
274                 break;
275         case 0xC000: l = v; break;
276         case 0xC001: n = 0; break;
277         case 0xE000: en = 0; irq &= ~IRQMMC; break;
278         case 0xE001: en = 1; break;
279         }
280         return;
281 t:
282         if((m & 0x40) != 0){
283                 prgb[0] = prg + (2 * nprg - 2) * 0x2000;
284                 prgb[2] = prg + b[6] * 0x2000;
285         }else{
286                 prgb[0] = prg + b[6] * 0x2000;
287                 prgb[2] = prg + (2 * nprg - 2) * 0x2000;
288         }
289         prgb[1] = prg + b[7] * 0x2000;
290         c = (m & 0x80) >> 5;
291         for(i = 0; i < 2; i++){
292                 chrb[j = (i << 1) ^ c] = chr + (b[i] >> 1) * 0x800;
293                 chrb[j+1] = chrb[j] + 0x400;
294         }
295         for(i = 2; i < 6; i++)
296                 chrb[(i + 2) ^ c] = chr + b[i] * 0x400;
297 }
298
299 static void
300 axrom(int p, u8int v)
301 {
302         static int b;
303
304         if(p >= 0)
305                 b = v;
306         else
307                 switch(p){
308                 case INIT:
309                         nrom(INIT, 0);
310                         b = 0;
311                         break;
312                 case SAVE:
313                         put8(b);
314                         return;
315                 case RSTR:
316                         b = get8();
317                         break;
318                 case SCAN:
319                         return;
320                 default:
321                         nope(p);
322                         return;
323                 }
324         prgb[0] = prg + (b & 3) * 0x8000;
325         prgb[1] = prgb[0] + 0x4000;
326 }
327
328 void (*mapper[256])(int, u8int) = {
329         [0] nrom,
330         [1] mmc1,
331         [2] uxrom,
332         [3] cnrom,
333         [4] mmc3,
334         [7] axrom,
335 };
336
337 static void
338 incvram(void)
339 {
340         int old;
341         
342         old = ppuv;
343         if((mem[PPUCTRL] & VRAMINC) != 0)
344                 ppuv += 32;
345         else
346                 ppuv += 1;
347         ppuv &= 0x3FFF;
348         if(mmc3hack && (old & (1<<12)) == 0 && (ppuv & (1<<12)) != 0)
349                 mapper[map](SCAN, 0);
350 }
351
352 u8int
353 memread(u16int p)
354 {
355         u8int v;
356         int i;
357
358         if(p < 0x2000){
359                 p &= 0x7FF;
360         }else if(p < 0x6000){
361                 if(p < 0x4000)
362                         p &= 0x2007;
363                 switch(p){
364                 case 0x2002:
365                         v = mem[p];
366                         mem[p] &= ~PPUVBLANK;
367                         vramlatch = 1;
368                         return v;
369                 case 0x2004:
370                         return oam[mem[0x2003]];
371                 case 0x2007:
372                         if(ppuv < 0x4000){
373                                 v = vrambuf;
374                                 vrambuf = ppuread(ppuv);
375                                 incvram();
376                                 return v;
377                         }
378                         vrambuf = ppuread(ppuv);
379                         incvram();
380                         return vrambuf;
381                 case APUSTATUS:
382                         v = (irq & 3) << 6;
383                         for(i = 0; i < 4; i++){
384                                 if(apuctr[i] != 0)
385                                         v |= (1<<i);
386                         }
387                         if(mem[0x4013] != 0)
388                                 v |= (1<<4);
389                         irq &= ~IRQFRAME;
390                         return v;
391                 case 0x4016:
392                         if((mem[p] & 1) != 0)
393                                 return keys & 1;
394                         v = keylatch & 1;
395                         keylatch = (keylatch >> 1) | 0x80;
396                         return v | 0x40;
397                 case 0x4017:
398                         return 0x40;
399                 }
400         }
401         if(p >= 0x8000){
402                 p -= 0x8000;
403                 return prgb[p >> prgsh][p & ((1 << prgsh) - 1)];
404         }
405         return mem[p];
406 }
407
408 void
409 memwrite(u16int p, u8int v)
410 {
411         extern u8int apulen[32];
412         extern u16int dmclen[16];
413         int i;
414
415         if(p < 0x2000){
416                 p &= 0x7FF;
417         }else if(p < 0x6000){
418                 if(p < 0x4000)
419                         p &= 0x2007;
420                 switch(p){
421                 case PPUCTRL:
422                         if((mem[PPUCTRL] & PPUNMI) == 0 && (v & PPUNMI) != 0 &&
423                            (mem[PPUSTATUS] & PPUVBLANK) != 0)
424                                 nmi = 1;
425                         pput = (pput & 0xF3FF) | ((v & 3) << 10);
426                         break;
427                 case PPUSTATUS:
428                         return;
429                 case 0x2004:
430                         oam[mem[0x2003]++] = v;
431                         return;
432                 case 0x2005:
433                         if(vramlatch){
434                                 ppusx = v & 7;
435                                 pput = (pput & 0xFFE0) | (v >> 3);
436                         }else
437                                 pput = (pput & 0x0C1F) | ((v & 0xF8) << 2) | ((v & 7) << 12);
438                         vramlatch ^= 1;
439                         return;
440                 case 0x2006:
441                         if(vramlatch)
442                                 pput = (pput & 0xFF) | (v << 8) & 0x3F00;
443                         else{
444                                 pput = (pput & 0xFF00) | v;
445                                 if(mmc3hack && (ppuv & (1<<12)) == 0 && (pput & (1<<12)) != 0)
446                                         mapper[map](SCAN, 0);
447                                 ppuv = pput;
448                         }
449                         vramlatch ^= 1;
450                         return;
451                 case 0x2007:
452                         ppuwrite(ppuv, v);
453                         incvram();
454                         return;
455                 case 0x4001:
456                 case 0x4005:
457                         i = (p & 0xC) >> 2;
458                         apuctr[i+8] |= 0x80;
459                         break;
460                 case 0x4003:
461                 case 0x4007:
462                 case 0x400B:
463                 case 0x400F:
464                         i = (p & 0xC) >> 2;
465                         if((mem[APUSTATUS] & (1<<i)) != 0){
466                                 apuctr[i] = apulen[v >> 3];
467                                 apuctr[10] |= (1<<i);
468                         }
469                         break;
470                 case DMCCTRL:
471                         if((v & 0x80) == 0)
472                                 irq &= ~IRQDMC;
473                         dmcfreq = 12 * dmclen[v & 0xf];
474                         break;
475                 case DMCBUF:
476                         v &= ~0x80;
477                         break;
478                 case 0x4014:
479                         memcpy(oam, mem + (v<<8), sizeof(oam));
480                         return;
481                 case APUSTATUS:
482                         for(i = 0; i < 4; i++)
483                                 if((v & (1<<i)) == 0)
484                                         apuctr[i] = 0;
485                         if((v & 0x10) != 0 && dmccnt == 0){
486                                 dmcaddr = mem[DMCADDR] * 0x40 + 0xC000;
487                                 dmccnt = mem[DMCLEN] * 0x10 + 1;
488                         }
489                         irq &= ~IRQDMC;
490                         break;
491                 case 0x4016:
492                         if((mem[p] & 1) != 0 && (v & 1) == 0)
493                                 keylatch = keys;
494                         break;
495                 case APUFRAME:
496                         apuseq = 0;
497                         if((v & 0x80) != 0)
498                                 apuclock = APUDIV;
499                         else
500                                 apuclock = 0;
501                         if((v & 0x40) != 0)
502                                 irq &= ~IRQFRAME;
503                         break;
504                 }
505         }else if(p < 0x8000){
506                 if(saveclock == 0)
507                         saveclock = SAVEFREQ;
508         }else{
509                 if(mapper[map] != nil)
510                         mapper[map](p, v);
511                 return;
512         }
513         mem[p] = v;
514 }
515
516 static uchar *
517 ppumap(u16int p)
518 {
519         if(p >= 0x3F00){
520                 if((p & 3) == 0)
521                         p &= 0x3F0F;
522                 return ppuram + (p & 0x3F1F);
523         }
524         p &= 0x3FFF;
525         if(p >= 0x3000)
526                 p &= 0x2FFF;
527         if(p >= 0x2000)
528                 switch(mirr){
529                 case MHORZ: if((p & 0x800) != 0) p |= 0x400; else p &= ~0x400; break;
530                 case MVERT: if((p & 0x400) != 0) p |= 0x800; else p &= ~0x800; break;
531                 case MSINGA: p &= ~0xC00; break;
532                 case MSINGB: p |= 0xC00; break;
533                 }
534         if(p < 0x2000)
535                 return chrb[p >> chrsh] + (p & ((1 << chrsh) - 1));
536         else
537                 return ppuram + p;
538 }
539
540 u8int
541 ppuread(u16int p)
542 {
543         return *ppumap(p);
544 }
545
546 void
547 ppuwrite(u16int p, u8int v)
548 {
549         *ppumap(p) = v;
550 }
551