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