]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/mem.c
added games/nes
[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[2], *chrb[2];
12 extern uchar *prg, *chr;
13 extern int nprg, nchr;
14 u16int pput, ppuv;
15 u8int ppusx;
16 static int vramlatch = 1, keylatch = 0xFF;
17 extern int keys, nmi, map, mirr;
18
19 static void
20 nrom(int p, u8int)
21 {
22         if(p < 0){
23                 prgb[0] = prg;
24                 if(nprg == 1)
25                         prgb[1] = prg;
26                 else
27                         prgb[1] = prg + 0x4000;
28                 chrb[0] = chr;
29                 chrb[1] = chr + 0x1000;
30         }
31         return;
32 }
33
34 static void
35 mmc1(int v, u8int p)
36 {
37         static u8int n, s, mode, c0, c1, pr;
38         int wchr, wprg;
39         static int mirrs[] = {MSINGB, MSINGA, MVERT, MHORZ};
40         
41         if(v < 0){
42                 wchr = 1;
43                 wprg = 1;
44                 mode = 0x0C;
45                 goto t;
46         }
47         if((p & 0x80) != 0){
48                 n = 0;
49                 s = 0;
50                 mode |= 0xC;
51                 return;
52         }
53         s |= (p & 1) << 4;
54         if(n < 4){
55                 n++;
56                 s >>= 1;
57                 return;
58         }
59         wchr = wprg = 1;
60         switch(v & 0xE000){
61         case 0x8000:
62                 mode = s;
63                 mirr = mirrs[mode & 3];
64                 wchr = wprg = 1;
65                 break;
66         case 0xA000:
67                 c0 = s & 0x1f;
68                 c0 %= 2*nchr;
69                 wchr = 1;
70                 break;
71         case 0xC000:
72                 c1 = s & 0x1f;
73                 c1 %= 2*nchr;
74                 if((mode & 0x10) != 0)
75                         wchr = 1;
76                 break;
77         case 0xE000:
78                 pr = s & 0x0f;
79                 pr %= nprg;
80                 wprg = 1;
81                 break;
82         }
83 t:
84         if(wprg)
85                 switch(mode & 0x0c){
86                 case 0x08:
87                         prgb[0] = prg;
88                         prgb[1] = prg + pr * 0x4000;
89                         break;
90                 case 0x0C:
91                         prgb[0] = prg + pr * 0x4000;
92                         prgb[1] = prg + (0x0f % nprg) * 0x4000;
93                         break;
94                 default:
95                         prgb[0] = prg + (pr & 0xfe) * 0x4000;
96                         prgb[1] = prg + (pr | 1) * 0x4000;
97                         break;
98                 }
99         if(wchr)
100                 if((mode & 0x10) != 0){
101                         chrb[0] = chr + c0 * 0x1000;
102                         chrb[1] = chr + c1 * 0x1000;
103                 }else{
104                         chrb[0] = chr + (c0 & 0xfe) * 0x1000;
105                         chrb[1] = chr + (c0 | 1) * 0x1000;
106                 }
107         s = 0;
108         n = 0;
109 }
110
111 void (*mapper[256])(int, u8int) = {
112         [0] nrom,
113         [1] mmc1,
114 };
115
116 static void
117 incvram(void)
118 {
119         if((mem[PPUCTRL] & VRAMINC) != 0)
120                 ppuv += 32;
121         else
122                 ppuv += 1;
123         ppuv &= 0x3FFF;
124 }
125
126 u8int
127 memread(u16int p)
128 {
129         static u8int vrambuf;
130         u8int v;
131
132         if(p < 0x2000){
133                 p &= 0x7FF;
134         }else if(p < 0x6000){
135                 if(p < 0x4000)
136                         p &= 0x2007;
137                 switch(p){
138                 case 0x2002:
139                         v = mem[p];
140                         mem[p] &= ~PPUVBLANK;
141                         vramlatch = 1;
142                         return v;
143                 case 0x2004:
144                         return oam[mem[0x2003]];
145                 case 0x2007:
146                         if(ppuv < 0x4000){
147                                 v = vrambuf;
148                                 vrambuf = ppuread(ppuv);
149                                 incvram();
150                                 return v;
151                         }
152                         vrambuf = ppuread(ppuv);
153                         incvram();
154                         return vrambuf;
155                 case 0x4016:
156                         if((mem[p] & 1) != 0)
157                                 return keys & 1;
158                         v = keylatch & 1;
159                         keylatch = (keylatch >> 1) | 0x80;
160                         return v | 0x40;
161                 case 0x4017:
162                         return 0x40;
163                 }
164         }
165         if(p >= 0x8000){
166                 if((p & 0x4000) != 0)
167                         return prgb[1][p - 0xC000];
168                 else
169                         return prgb[0][p - 0x8000];
170         }
171         return mem[p];
172 }
173
174 void
175 memwrite(u16int p, u8int v)
176 {
177         if(p < 0x2000){
178                 p &= 0x7FF;
179         }else if(p < 0x6000){
180                 if(p < 0x4000)
181                         p &= 0x2007;
182                 switch(p){
183                 case PPUCTRL:
184                         if((v & PPUNMI) != 0 && (mem[PPUSTATUS] & PPUVBLANK) != 0)
185                                 nmi = 1;
186                         pput = (pput & 0xF3FF) | ((v & 3) << 10);
187                         break;
188                 case PPUSTATUS:
189                         return;
190                 case 0x2004:
191                         oam[mem[0x2003]++] = v;
192                         return;
193                 case 0x2005:
194                         if(vramlatch){
195                                 ppusx = v & 7;
196                                 pput = (pput & 0xFFE0) | (v >> 3);
197                         }else
198                                 pput = (pput & 0x0C1F) | ((v & 0xF8) << 2) | ((v & 7) << 12);
199                         vramlatch ^= 1;
200                         return;
201                 case 0x2006:
202                         if(vramlatch)
203                                 pput = (pput & 0xFF) | (v << 8) & 0x3F00;
204                         else{
205                                 pput = (pput & 0xFF00) | v;
206                                 ppuv = pput;
207                         }
208                         vramlatch ^= 1;
209                         return;
210                 case 0x2007:
211                         ppuwrite(ppuv, v);
212                         incvram();
213                         return;
214                 case 0x4014:
215                         memcpy(oam, mem + (v<<8), sizeof(oam));
216                         return;
217                 case 0x4016:
218                         if((mem[p] & 1) != 0 && (v & 1) == 0)
219                                 keylatch = keys;
220                         break;
221                 }
222         }else if(p >= 0x8000){
223                 if(mapper[map] != nil)
224                         mapper[map](p, v);
225                 return;
226         }
227         mem[p] = v;
228 }
229
230 static uchar *
231 ppumap(u16int p)
232 {
233         if(p >= 0x3F00){
234                 if((p & 3) == 0)
235                         p &= 0x3F0F;
236                 return ppuram + (p & 0x3F1F);
237         }
238         p &= 0x3FFF;
239         if(p >= 0x3000)
240                 p &= 0x2FFF;
241         if(p >= 0x2000)
242                 switch(mirr){
243                 case MHORZ: if((p & 0x800) != 0) p |= 0x400; else p &= ~0x400; break;
244                 case MVERT: if((p & 0x400) != 0) p |= 0x800; else p &= ~0x800; break;
245                 case MSINGA: p &= ~0xC00; break;
246                 case MSINGB: p |= 0xC00; break;
247                 }
248         if(p < 0x1000)
249                 return chrb[0] + p;
250         else if(p < 0x2000)
251                 return chrb[1] + p - 0x1000;
252         else
253                 return ppuram + p;
254 }
255
256 u8int
257 ppuread(u16int p)
258 {
259         return *ppumap(p);
260 }
261
262 void
263 ppuwrite(u16int p, u8int v)
264 {
265         *ppumap(p) = v;
266 }
267