merge mappers from FCEU-mm
[fceu.git] / boards / 90.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2002 Xodnizel
5  *  Copyright (C) 2005 CaH4e3
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "mapinc.h"
23 //#define DEBUG90
24
25 // Mapper 090 is simpliest mapper hardware and have not extended nametable control and latched chr banks in 4k mode
26 // Mapper 209 much compicated hardware with decribed above features disabled by default and switchable by command
27 // Mapper 211 the same mapper 209 but with forced nametable control
28
29 static int is209; 
30 static int is211;
31
32 static uint8 IRQMode;        // from $c001
33 static uint8 IRQPre;         // from $c004
34 static uint8 IRQPreSize;     // from $c007
35 static uint8 IRQCount;       // from $c005
36 static uint8 IRQXOR;         // Loaded from $C006
37 static uint8 IRQa;           // $c002, $c003, and $c000
38
39 static uint8 mul[2];
40 static uint8 regie;
41
42 static uint8 tkcom[4];
43 static uint8 prgb[4];
44 static uint8 chrlow[8];
45 static uint8 chrhigh[8];
46
47 static uint8 chr[2];
48
49 static uint16 names[4];
50 static uint8 tekker;
51
52 static SFORMAT Tek_StateRegs[]={
53   {&IRQMode, 1, "IRQM"},
54   {&IRQPre, 1, "IRQP"},
55   {&IRQPreSize, 1, "IRQR"},
56   {&IRQCount, 1, "IRQC"},
57   {&IRQXOR, 1, "IRQX"},
58   {&IRQa, 1, "IRQA"},
59   {mul, 2, "MUL"},
60   {&regie, 1, "REGI"},
61   {tkcom, 4, "TKCO"},
62   {prgb, 4, "PRGB"},
63   {chr, 2, "CLTC"},
64   {chrlow, 4, "CHRL"},
65   {chrhigh, 8, "CHRH"},
66   {&names[0], 2|FCEUSTATE_RLSB, "NMS0"},
67   {&names[1], 2|FCEUSTATE_RLSB, "NMS1"},
68   {&names[2], 2|FCEUSTATE_RLSB, "NMS2"},
69   {&names[3], 2|FCEUSTATE_RLSB, "NMS3"},
70   {&tekker, 1, "TEKR"},
71   {0}
72 };
73
74 static void mira(void)
75 {
76   if((tkcom[0]&0x20&&is209)||is211)
77   {
78     int x;
79     if(tkcom[0]&0x40)        // Name tables are ROM-only
80     {
81       for(x=0;x<4;x++)
82          setntamem(CHRptr[0]+(((names[x])&CHRmask1[0])<<10),0,x);
83     }
84     else                        // Name tables can be RAM or ROM.
85     {
86       for(x=0;x<4;x++)
87       {
88         if((tkcom[1]&0x80)==(names[x]&0x80))        // RAM selected.
89           setntamem(NTARAM+((names[x]&0x1)<<10),1,x);
90         else
91           setntamem(CHRptr[0]+(((names[x])&CHRmask1[0])<<10),0,x);
92       }
93     }
94   }
95   else
96   {
97     switch(tkcom[1]&3)
98     {
99       case 0: setmirror(MI_V); break;
100       case 1: setmirror(MI_H); break;
101       case 2: setmirror(MI_0); break;
102       case 3: setmirror(MI_1); break;
103     }
104   }
105 }
106
107 static void tekprom(void) // TODO: verify for single, small multi and large multi
108 {
109   uint32 bankmode=((tkcom[3]&6)<<5);
110   switch(tkcom[0]&7)
111   {
112     case 00: if(tkcom[0]&0x80)
113                setprg8(0x6000,(((prgb[3]<<2)+3)&0x3F)|bankmode);
114              setprg32(0x8000,(prgb[3]&7)|((tkcom[3]&7)<<3));
115              break;
116     case 01: if(tkcom[0]&0x80)
117                setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode);
118              setprg16(0x8000,(prgb[1]&0x0F)|((tkcom[3]&7)<<4));
119              setprg16(0xC000,0x0F|((tkcom[3]&7)<<4));
120              break;
121     case 03: // bit reversion
122     case 02: 
123              if(tkcom[0]&0x80)
124                setprg8(0x6000,(prgb[3]&0x1F)|((tkcom[3]&7)<<5)); // 45in1 multy has different bits, seems board was hacked to support big data banks
125              setprg8(0x8000,(prgb[0]&0x1F)|((tkcom[3]&7)<<5));
126              setprg8(0xa000,(prgb[1]&0x1F)|((tkcom[3]&7)<<5));
127              setprg8(0xc000,(prgb[2]&0x1F)|((tkcom[3]&7)<<5));
128              setprg8(0xe000,0x1F|((tkcom[3]&7)<<5));
129 //             setprg8(0xe000,(prgb[3]&0x0F)|((tkcom[3]&6)<<3));
130 //             setprg32(0x8000,((prgb[0]&0x0F)>>2)|((tkcom[3]&6)<<3));
131              break;
132     case 04: if(tkcom[0]&0x80)
133                setprg8(0x6000,(((prgb[3]<<2)+3)&0x3F)|bankmode);
134              setprg32(0x8000,(prgb[3]&0x0F)|((tkcom[3]&6)<<3));
135              break;
136     case 05: if(tkcom[0]&0x80)
137                setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode);
138              setprg16(0x8000,(prgb[1]&0x1F)|((tkcom[3]&6)<<4));
139              setprg16(0xC000,(prgb[3]&0x1F)|((tkcom[3]&6)<<4));
140              break;
141     case 07: // bit reversion
142     case 06: if(tkcom[0]&0x80)
143                setprg8(0x6000,(prgb[3]&0x3F)|bankmode);
144              setprg8(0x8000,(prgb[0]&0x3F)|bankmode);
145              setprg8(0xa000,(prgb[1]&0x3F)|bankmode);
146              setprg8(0xc000,(prgb[2]&0x3F)|bankmode);
147              setprg8(0xe000,(prgb[3]&0x3F)|bankmode);
148              break;
149   }
150 }
151
152 static void tekvrom(void)
153 {
154   int x, bank=0, mask=0xFFFF;
155   if(!(tkcom[3]&0x20))
156   {
157     bank=(tkcom[3]&1)|((tkcom[3]&0x18)>>2);
158     switch (tkcom[0]&0x18)
159     {
160       case 0x00: bank<<=5; mask=0x1F; break;
161       case 0x08: bank<<=6; mask=0x3F; break;
162       case 0x10: bank<<=7; mask=0x7F; break;
163       case 0x18: bank<<=8; mask=0xFF; break;
164     }
165   }
166   switch(tkcom[0]&0x18)
167   {
168     case 0x00:      // 8KB
169          setchr8(((chrlow[0]|(chrhigh[0]<<8))&mask)|bank);
170          break;
171     case 0x08:      // 4KB
172 //         for(x=0;x<8;x+=4)
173 //            setchr4(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
174          setchr4(0x0000,((chrlow[chr[0]]|(chrhigh[chr[0]]<<8))&mask)|bank);
175          setchr4(0x1000,((chrlow[chr[1]]|(chrhigh[chr[1]]<<8))&mask)|bank);
176          break;
177     case 0x10:      // 2KB
178          for(x=0;x<8;x+=2)
179             setchr2(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
180          break;
181     case 0x18:      // 1KB
182          for(x=0;x<8;x++)
183             setchr1(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
184          break;
185   }
186 }
187
188 static DECLFW(M90TekWrite)
189 {
190   switch(A&0x5C03)
191   {
192     case 0x5800: mul[0]=V; break;
193     case 0x5801: mul[1]=V; break;
194     case 0x5803: regie=V; break;
195   }
196 }
197
198 static DECLFR(M90TekRead)
199 {
200   switch(A&0x5C03)
201   {
202     case 0x5800: return (mul[0]*mul[1]);
203     case 0x5801: return((mul[0]*mul[1])>>8);
204     case 0x5803: return (regie);
205     default: return tekker;
206   }
207   return(0xff);
208 }
209
210 static DECLFW(M90PRGWrite)
211 {
212 //  FCEU_printf("bs %04x %02x\n",A,V);
213   prgb[A&3]=V;
214   tekprom();
215 }
216
217 static DECLFW(M90CHRlowWrite)
218 {
219 //  FCEU_printf("bs %04x %02x\n",A,V);
220   chrlow[A&7]=V;
221   tekvrom();
222 }
223
224 static DECLFW(M90CHRhiWrite)
225 {
226 //  FCEU_printf("bs %04x %02x\n",A,V);
227   chrhigh[A&7]=V;
228   tekvrom();
229 }
230
231 static DECLFW(M90NTWrite)
232 {
233 //  FCEU_printf("bs %04x %02x\n",A,V);
234   if(A&4)
235   {
236     names[A&3]&=0x00FF;
237     names[A&3]|=V<<8;
238   }
239   else
240   {
241     names[A&3]&=0xFF00;
242     names[A&3]|=V;
243   }
244   mira();
245 }
246
247 static DECLFW(M90IRQWrite)
248 {
249 //  FCEU_printf("bs %04x %02x\n",A,V);
250   switch(A&7)
251   {
252     case 00: //FCEU_printf("%s IRQ (C000)\n",V&1?"Enable":"Disable");
253              IRQa=V&1;if(!(V&1)) X6502_IRQEnd(FCEU_IQEXT);break;
254     case 02: //FCEU_printf("Disable IRQ (C002) scanline=%d\n", scanline);
255              IRQa=0;X6502_IRQEnd(FCEU_IQEXT);break;
256     case 03: //FCEU_printf("Enable IRQ (C003) scanline=%d\n", scanline);
257              IRQa=1;break;
258     case 01: IRQMode=V;
259                /*FCEU_printf("IRQ Count method: ");
260                switch (IRQMode&3)
261                {
262                  case 00: FCEU_printf("M2 cycles\n");break;
263                  case 01: FCEU_printf("PPU A12 toggles\n");break;
264                  case 02: FCEU_printf("PPU reads\n");break;
265                  case 03: FCEU_printf("Writes to CPU space\n");break;
266                }
267                FCEU_printf("Counter prescaler size: %s\n",(IRQMode&4)?"3 bits":"8 bits");
268                FCEU_printf("Counter prescaler size adjust: %s\n",(IRQMode&8)?"Used C007":"Normal Operation");
269                if((IRQMode>>6)==2) FCEU_printf("Counter Down\n");
270                 else if((IRQMode>>6)==1) FCEU_printf("Counter Up\n");
271                 else FCEU_printf("Counter Stopped\n");*/
272               break;
273     case 04: //FCEU_printf("Pre Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR);
274              IRQPre=V^IRQXOR;break;
275     case 05: //FCEU_printf("Main Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR);
276              IRQCount=V^IRQXOR;break;
277     case 06: //FCEU_printf("Xor Value: %d\n",V);
278              IRQXOR=V;break;
279     case 07: //if(!(IRQMode&8)) FCEU_printf("C001 is clear, no effect applied\n");
280              //     else if(V==0xFF) FCEU_printf("Prescaler is changed for 12bits\n");
281              //     else FCEU_printf("Counter Stopped\n");
282              IRQPreSize=V;break;
283   }
284 }
285
286 static DECLFW(M90ModeWrite)
287 {
288 //    FCEU_printf("bs %04x %02x\n",A,V);
289     tkcom[A&3]=V;
290     tekprom();
291     tekvrom();
292     mira();
293     
294 #ifdef DEBUG90
295   switch (A&3)
296   {
297    case 00: FCEU_printf("Main Control Register:\n");
298             FCEU_printf("  PGR Banking mode: %d\n",V&7);
299             FCEU_printf("  CHR Banking mode: %d\n",(V>>3)&3);
300             FCEU_printf("  6000-7FFF addresses mapping: %s\n",(V&0x80)?"Yes":"No");
301             FCEU_printf("  Nametable control: %s\n",(V&0x20)?"Enabled":"Disabled");
302             if(V&0x20)
303                FCEU_printf("  Nametable can be: %s\n",(V&0x40)?"ROM Only":"RAM or ROM");
304             break;
305    case 01: FCEU_printf("Mirroring mode: ");
306             switch (V&3)
307             {
308              case 0: FCEU_printf("Vertical\n");break;
309              case 1: FCEU_printf("Horizontal\n");break;
310              case 2: FCEU_printf("Nametable 0 only\n");break;
311              case 3: FCEU_printf("Nametable 1 only\n");break;
312             }
313             FCEU_printf("Mirroring flag: %s\n",(V&0x80)?"On":"Off");
314             break;
315    case 02: if((((tkcom[0])>>5)&3)==1)
316               FCEU_printf("Nametable ROM/RAM select mode: %d\n",V>>7);
317             break;
318    case 03:
319             FCEU_printf("CHR Banking mode: %s\n",(V&0x20)?"Entire CHR ROM":"256Kb Switching mode");
320             if(!(V&0x20)) FCEU_printf("256K CHR bank number: %02x\n",(V&1)|((V&0x18)>>2));
321             FCEU_printf("512K PRG bank number: %d\n",(V&6)>>1);
322             FCEU_printf("CHR Bank mirroring: %s\n",(V&0x80)?"Swapped":"Normal operate");
323   }
324 #endif
325 }
326
327 static DECLFW(M90DummyWrite)
328 {
329 //    FCEU_printf("bs %04x %02x\n",A,V);
330 }
331
332 static void CCL(void)
333 {
334   if((IRQMode>>6) == 1) // Count Up
335   {
336     IRQCount++;
337     if((IRQCount == 0) && IRQa)
338     {
339       X6502_IRQBegin(FCEU_IQEXT);
340     }
341   }
342   else if((IRQMode>>6) == 2) // Count down
343   {
344     IRQCount--;
345     if((IRQCount == 0xFF) && IRQa)
346     {
347       X6502_IRQBegin(FCEU_IQEXT);
348     }
349   }
350 }
351
352 static void ClockCounter(void)
353 {
354   uint8 premask;
355
356   if(IRQMode & 0x4)
357     premask = 0x7;
358   else
359     premask = 0xFF;
360   if((IRQMode>>6) == 1) // Count up
361   {
362     IRQPre++;
363     if((IRQPre & premask) == 0) CCL();
364   }
365   else if((IRQMode>>6) == 2) // Count down
366   {
367     IRQPre--;
368     if((IRQPre & premask) == premask) CCL();
369   }
370 }
371
372 void FP_FASTAPASS(1) CPUWrap(int a)
373 {
374   int x;
375   if((IRQMode&3)==0) for(x=0;x<a;x++) ClockCounter();
376 }
377
378 static void SLWrap(void)
379 {
380   int x;
381   if((IRQMode&3)==1) for(x=0;x<8;x++) ClockCounter();
382 }
383
384 static uint32 lastread;
385 static void FP_FASTAPASS(1) M90PPU(uint32 A)
386 {
387   if((IRQMode&3)==2)
388   {
389     if(lastread!=A)
390     {
391       ClockCounter();
392       ClockCounter();
393     }
394     lastread=A;
395   }
396   
397   if(is209)
398   {
399     uint8 l,h;
400     h=A>>8;
401     if(h<0x20&&((h&0x0F)==0xF))
402     {
403       l=A&0xF0;
404       if(l==0xD0)
405       {
406         chr[(h&0x10)>>4]=((h&0x10)>>2);
407         tekvrom();
408       }
409       else if(l==0xE0)
410       {
411         chr[(h&0x10)>>4]=((h&0x10)>>2)|2;
412         tekvrom();
413       }
414     }
415   }
416   else
417   {
418     chr[0]=0;
419     chr[1]=4;
420   }
421 }
422
423 static void togglie()
424 {
425   tekker+=0x40;
426   tekker&=0xC0;
427   FCEU_printf("tekker=%02x\n",tekker);
428   memset(tkcom,0x00,sizeof(tkcom));
429   memset(prgb,0xff,sizeof(prgb));
430   tekprom();
431   tekvrom();
432 }
433
434 static void M90Restore(int version)
435 {
436   tekprom();
437   tekvrom();
438   mira();
439 }
440
441 static void M90Power(void)
442 {
443   SetWriteHandler(0x5000,0x5fff,M90TekWrite);
444   SetWriteHandler(0x8000,0x8ff0,M90PRGWrite);
445   SetWriteHandler(0x9000,0x9fff,M90CHRlowWrite);
446   SetWriteHandler(0xA000,0xAfff,M90CHRhiWrite);
447   SetWriteHandler(0xB000,0xBfff,M90NTWrite);
448   SetWriteHandler(0xC000,0xCfff,M90IRQWrite);
449   SetWriteHandler(0xD000,0xD5ff,M90ModeWrite);
450   SetWriteHandler(0xE000,0xFfff,M90DummyWrite);
451
452   SetReadHandler(0x5000,0x5fff,M90TekRead);
453   SetReadHandler(0x6000,0xffff,CartBR);
454
455   mul[0]=mul[1]=regie=0xFF;
456
457   memset(tkcom,0x00,sizeof(tkcom));
458   memset(prgb,0xff,sizeof(prgb));
459   memset(chrlow,0xff,sizeof(chrlow));
460   memset(chrhigh,0xff,sizeof(chrhigh));
461   memset(names,0x00,sizeof(names));
462
463   if(is211)
464     tekker=0xC0;
465   else
466     tekker=0x00;
467
468   tekprom();
469   tekvrom();
470 }
471
472
473 void Mapper90_Init(CartInfo *info)
474 {
475   is211=0;
476   is209=0;
477   info->Reset=togglie;
478   info->Power=M90Power;
479   PPU_hook=M90PPU;
480   MapIRQHook=CPUWrap;
481   GameHBIRQHook2=SLWrap;
482   GameStateRestore=M90Restore;
483   AddExState(Tek_StateRegs, ~0, 0, 0);
484 }
485
486 void Mapper209_Init(CartInfo *info)
487 {
488   is211=0;
489   is209=1;
490   info->Reset=togglie;
491   info->Power=M90Power;
492   PPU_hook=M90PPU;
493   MapIRQHook=CPUWrap;
494   GameHBIRQHook2=SLWrap;
495   GameStateRestore=M90Restore;
496   AddExState(Tek_StateRegs, ~0, 0, 0);
497 }
498
499 void Mapper211_Init(CartInfo *info)
500 {
501   is211=1;
502   info->Reset=togglie;
503   info->Power=M90Power;
504   PPU_hook=M90PPU;
505   MapIRQHook=CPUWrap;
506   GameHBIRQHook2=SLWrap;
507   GameStateRestore=M90Restore;
508   AddExState(Tek_StateRegs, ~0, 0, 0);
509 }