]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/mem.c
games/nes: mmc3 and bugfixes
[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 mmc3(int p, u8int v)
142 {
143         static u8int m, b[8], l, n, en;
144         int i, j, c;
145
146         if(p < 0){
147                 switch(p){
148                 case INIT:
149                         prgsh = 13;
150                         chrsh = 10;
151                         mmc3hack = 1;
152                         prgb[2] = prg + (2 * nprg - 2) * 0x2000;
153                         prgb[3] = prgb[2] + 0x2000;
154                         goto t;
155                 case SCAN:
156                         if(n == 0)
157                                 n = l;
158                         else
159                                 n--;
160                         if(n == 0 && en)
161                                 irq |= 2;
162                         return;
163                 case SAVE:
164                         put8(m);
165                         for(i = 0; i < 8; i++)
166                                 put8(b[i]);
167                         put8(l);
168                         put8(n);
169                         put8(en);
170                         return;
171                 case RSTR:
172                         m = get8();
173                         for(i = 0; i < 8; i++)
174                                 b[i] = get8();
175                         l = get8();
176                         n = get8();
177                         en = get8();
178                         goto t;
179                 }
180         }
181         switch(p & 0xE001){
182         case 0x8000:
183                 if(((m ^ v) & 0xc0) != 0){
184                         m = v;
185                         goto t;
186                 }
187                 m = v;
188                 break;
189         case 0x8001:
190                 i = m & 7;
191                 if(i < 6)
192                         v %= 8 * nchr;
193                 else
194                         v %= 2 * nprg;
195                 b[i] = v;
196                 goto t;
197         case 0xA000:
198                 if(mirr == MFOUR)
199                         break;
200                 if(v & 1)
201                         mirr = MHORZ;
202                 else
203                         mirr = MVERT;
204                 break;
205         case 0xC000: l = v; break;
206         case 0xC001: n = 0; break;
207         case 0xE000: en = 0; irq &= ~2; break;
208         case 0xE001: en = 1; break;
209         }
210         return;
211 t:
212         if((m & 0x40) != 0){
213                 prgb[0] = prg + (2 * nprg - 2) * 0x2000;
214                 prgb[2] = prg + b[6] * 0x2000;
215         }else{
216                 prgb[0] = prg + b[6] * 0x2000;
217                 prgb[2] = prg + (2 * nprg - 2) * 0x2000;
218         }
219         prgb[1] = prg + b[7] * 0x2000;
220         c = (m & 0x80) >> 6;
221         for(i = 0; i < 2; i++){
222                 chrb[j = (i << 1) ^ c] = chr + (b[i] >> 1) * 0x800;
223                 chrb[j+1] = chrb[j] + 0x400;
224         }
225         for(i = 2; i < 6; i++)
226                 chrb[(i + 2) ^ c] = chr + b[i] * 0x400;
227 }
228
229 static void
230 mmc7(int p, u8int v)
231 {
232         static int b;
233
234         if(p >= 0)
235                 b = v;
236         else
237                 switch(p){
238                 case INIT:
239                         nrom(INIT, 0);
240                         b = 0;
241                         break;
242                 case SAVE:
243                         put8(b);
244                         return;
245                 case RSTR:
246                         b = get8();
247                         break;
248                 default:
249                         nope(p);
250                         return;
251                 }
252         prgb[0] = prg + (b & 3) * 0x8000;
253         prgb[1] = prgb[0] + 0x4000;
254 }
255
256 void (*mapper[256])(int, u8int) = {
257         [0] nrom,
258         [1] mmc1,
259         [4] mmc3,
260         [7] mmc7,
261 };
262
263 static void
264 incvram(void)
265 {
266         int old;
267         
268         old = ppuv;
269         if((mem[PPUCTRL] & VRAMINC) != 0)
270                 ppuv += 32;
271         else
272                 ppuv += 1;
273         ppuv &= 0x3FFF;
274         if(mmc3hack && (old & (1<<12)) == 0 && (ppuv & (1<<12)) != 0)
275                 mapper[map](SCAN, 0);
276 }
277
278 u8int
279 memread(u16int p)
280 {
281         u8int v;
282
283         if(p < 0x2000){
284                 p &= 0x7FF;
285         }else if(p < 0x6000){
286                 if(p < 0x4000)
287                         p &= 0x2007;
288                 switch(p){
289                 case 0x2002:
290                         v = mem[p];
291                         mem[p] &= ~PPUVBLANK;
292                         vramlatch = 1;
293                         return v;
294                 case 0x2004:
295                         return oam[mem[0x2003]];
296                 case 0x2007:
297                         if(ppuv < 0x4000){
298                                 v = vrambuf;
299                                 vrambuf = ppuread(ppuv);
300                                 incvram();
301                                 return v;
302                         }
303                         vrambuf = ppuread(ppuv);
304                         incvram();
305                         return vrambuf;
306                 case 0x4016:
307                         if((mem[p] & 1) != 0)
308                                 return keys & 1;
309                         v = keylatch & 1;
310                         keylatch = (keylatch >> 1) | 0x80;
311                         return v | 0x40;
312                 case 0x4017:
313                         return 0x40;
314                 }
315         }
316         if(p >= 0x8000){
317                 p -= 0x8000;
318                 return prgb[p >> prgsh][p & ((1 << prgsh) - 1)];
319         }
320         return mem[p];
321 }
322
323 void
324 memwrite(u16int p, u8int v)
325 {
326         if(p < 0x2000){
327                 p &= 0x7FF;
328         }else if(p < 0x6000){
329                 if(p < 0x4000)
330                         p &= 0x2007;
331                 switch(p){
332                 case PPUCTRL:
333                         if((mem[PPUCTRL] & PPUNMI) == 0 && (v & PPUNMI) != 0 &&
334                            (mem[PPUSTATUS] & PPUVBLANK) != 0)
335                                 nmi = 1;
336                         pput = (pput & 0xF3FF) | ((v & 3) << 10);
337                         break;
338                 case PPUSTATUS:
339                         return;
340                 case 0x2004:
341                         oam[mem[0x2003]++] = v;
342                         return;
343                 case 0x2005:
344                         if(vramlatch){
345                                 ppusx = v & 7;
346                                 pput = (pput & 0xFFE0) | (v >> 3);
347                         }else
348                                 pput = (pput & 0x0C1F) | ((v & 0xF8) << 2) | ((v & 7) << 12);
349                         vramlatch ^= 1;
350                         return;
351                 case 0x2006:
352                         if(vramlatch)
353                                 pput = (pput & 0xFF) | (v << 8) & 0x3F00;
354                         else{
355                                 pput = (pput & 0xFF00) | v;
356                                 if(mmc3hack && (ppuv & (1<<12)) == 0 && (pput & (1<<12)) != 0)
357                                         mapper[map](SCAN, 0);
358                                 ppuv = pput;
359                         }
360                         vramlatch ^= 1;
361                         return;
362                 case 0x2007:
363                         ppuwrite(ppuv, v);
364                         incvram();
365                         return;
366                 case 0x4014:
367                         memcpy(oam, mem + (v<<8), sizeof(oam));
368                         return;
369                 case 0x4016:
370                         if((mem[p] & 1) != 0 && (v & 1) == 0)
371                                 keylatch = keys;
372                         break;
373                 }
374         }else if(p >= 0x8000){
375                 if(mapper[map] != nil)
376                         mapper[map](p, v);
377                 return;
378         }
379         mem[p] = v;
380 }
381
382 static uchar *
383 ppumap(u16int p)
384 {
385         if(p >= 0x3F00){
386                 if((p & 3) == 0)
387                         p &= 0x3F0F;
388                 return ppuram + (p & 0x3F1F);
389         }
390         p &= 0x3FFF;
391         if(p >= 0x3000)
392                 p &= 0x2FFF;
393         if(p >= 0x2000)
394                 switch(mirr){
395                 case MHORZ: if((p & 0x800) != 0) p |= 0x400; else p &= ~0x400; break;
396                 case MVERT: if((p & 0x400) != 0) p |= 0x800; else p &= ~0x800; break;
397                 case MSINGA: p &= ~0xC00; break;
398                 case MSINGB: p |= 0xC00; break;
399                 }
400         if(p < 0x2000)
401                 return chrb[p >> chrsh] + (p & ((1 << chrsh) - 1));
402         else
403                 return ppuram + p;
404 }
405
406 u8int
407 ppuread(u16int p)
408 {
409         return *ppumap(p);
410 }
411
412 void
413 ppuwrite(u16int p, u8int v)
414 {
415         *ppumap(p) = v;
416 }
417