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