489a540cf1a532a5e401ca2381c4648a6552d8c8
[fceu.git] / boards / 90.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2002 Xodnizel
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "mapinc.h"
22
23 static int is209;
24
25 static uint8 IRQMode;        // from $c001
26 static uint8 IRQPre;         // from $c004
27 static uint8 IRQPreSize;     // from $c007
28 static uint8 IRQCount;       // from $c005
29 static uint8 IRQXOR;         // Loaded from $C006
30 static uint8 IRQa;           // $c002, $c003, and $c000
31
32 static uint8 mul[2];
33 static uint8 regie;
34
35 static uint8 tkcom[4];
36 static uint8 prgb[4];
37 static uint8 chrlow[8];
38 static uint8 chrhigh[8];
39
40 static uint16 names[4];
41 static uint8 tekker;
42
43 static SFORMAT Tek_StateRegs[]={
44   {&IRQMode, 1, "IRQMODE"},
45   {&IRQPre, 1, "IRQPRE"},
46   {&IRQPreSize, 1, "IRQPRESIZE"},
47   {&IRQCount, 1, "IRQC"},
48   {&IRQXOR, 1, "IRQXOR"},
49   {&IRQa, 1, "IRQa"},
50   {mul, 2, "MUL"},
51   {&regie, 1, "REGI"},
52   {tkcom, 4, "TKCO"},
53   {prgb, 4, "PRGB"},
54   {chrlow, 4, "CHRL"},
55   {chrhigh, 8, "CHRH"},
56   {&names[0], 2|FCEUSTATE_RLSB, "NMS0"},
57   {&names[1], 2|FCEUSTATE_RLSB, "NMS1"},
58   {&names[2], 2|FCEUSTATE_RLSB, "NMS2"},
59   {&names[3], 2|FCEUSTATE_RLSB, "NMS3"},
60   {&tekker, 1, "TEKR"},
61   {0}
62 };
63
64 static DECLFR(M90Read)
65 {
66   switch(A)
67   {
68     case 0x5800: return (mul[0]*mul[1]);
69     case 0x5801: return((mul[0]*mul[1])>>8);
70     case 0x5803: return (regie);
71     default: return(tekker);
72   }
73 }
74
75 static void mira(void)
76 {
77   if(tkcom[0]&0x20 && is209)
78   {
79     int x;
80     if(tkcom[0]&0x40)        // Name tables are ROM-only
81     {
82       for(x=0;x<4;x++)
83          setntamem(CHRptr[0]+(((names[x])&CHRmask1[0])<<10),0,x);
84     }
85     else                        // Name tables can be RAM or ROM.
86     {
87       for(x=0;x<4;x++)
88       {
89         if((tkcom[2]&0x80)==(names[x]&0x80))        // RAM selected.
90           setntamem(NTARAM+((names[x]&0x1)<<10),1,x);
91         else
92           setntamem(CHRptr[0]+(((names[x])&CHRmask1[0])<<10),0,x);
93       }
94     }
95   }
96   else
97   {
98     switch(tkcom[1]&3)
99     {
100       case 0: setmirror(MI_V); break;
101       case 1: setmirror(MI_H); break;
102       case 2: setmirror(MI_0); break;
103       case 3: setmirror(MI_1); break;
104     }
105   }
106 }
107
108 static void tekprom(void)
109 {
110   uint32 bankmode=((tkcom[3]&6)<<5);
111   switch(tkcom[0]&3)
112   {
113     case 1:              // 16 KB
114          setprg16(0x8000,(prgb[0]&0x1F)|((tkcom[3]&6)<<4));
115          setprg16(0xC000,(prgb[2]&0x1F)|((tkcom[3]&6)<<4));
116          break;
117     case 2:              //2 = 8 KB ??
118          if(tkcom[0]&0x4)
119          {
120            setprg8(0x8000,(prgb[0]&0x3F)|bankmode);
121            setprg8(0xa000,(prgb[1]&0x3F)|bankmode);
122            setprg8(0xc000,(prgb[2]&0x3F)|bankmode);
123            setprg8(0xe000,(prgb[3]&0x3F)|bankmode);
124          }
125          else
126          {
127            if(tkcom[0]&0x80)
128              setprg8(0x6000,(prgb[3]&0x3F)|bankmode);
129            setprg8(0x8000,(prgb[0]&0x3F)|bankmode);
130            setprg8(0xa000,(prgb[1]&0x3F)|bankmode);
131            setprg8(0xc000,(prgb[2]&0x3F)|bankmode);
132            setprg8(0xe000,((~0)&0x3F)|bankmode);
133          }
134          break;
135     case 0:
136     case 3:
137          setprg8(0x8000,(prgb[0]&0x3F)|bankmode);
138          setprg8(0xa000,(prgb[1]&0x3F)|bankmode);
139          setprg8(0xc000,(prgb[2]&0x3F)|bankmode);
140          setprg8(0xe000,(prgb[3]&0x3F)|bankmode);
141          break;
142   }
143 }
144
145 static void tekvrom(void)
146 {
147   int x, bank=0, mask=0xFFFF;
148   if(!(tkcom[3]&0x20))
149   {
150     bank=(tkcom[3]&1)|((tkcom[3]&0x18)>>2);
151     switch (tkcom[0]&0x18)
152     {
153       case 0x00: bank<<=5; mask=0x1F; break;
154       case 0x08: bank<<=6; mask=0x3F; break;
155       case 0x10: bank<<=7; mask=0x7F; break;
156       case 0x18: bank<<=8; mask=0xFF; break;
157     }
158   }
159   switch(tkcom[0]&0x18)
160   {
161     case 0x00:      // 8KB
162          setchr8(((chrlow[0]|(chrhigh[0]<<8))&mask)|bank);
163          break;
164     case 0x08:      // 4KB
165          for(x=0;x<8;x+=4)
166             setchr4(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
167          break;
168     case 0x10:      // 2KB
169          for(x=0;x<8;x+=2)
170             setchr2(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
171          break;
172     case 0x18:      // 1KB
173          for(x=0;x<8;x++)
174             setchr1(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
175          break;
176   }
177 }
178
179 static DECLFW(M90Write)
180 {
181   if(A==0x5800) mul[0]=V;
182   else if(A==0x5801) mul[1]=V;
183   else if(A==0x5803) regie=V;
184
185   A&=0xF007;
186   if(A>=0x8000 && A<=0x8003)
187   {
188     prgb[A&3]=V;
189     tekprom();
190   }
191   else if(A>=0x9000 && A<=0x9007)
192   {
193     chrlow[A&7]=V;
194     tekvrom();
195   }
196   else if(A>=0xa000 && A<=0xa007)
197   {
198     chrhigh[A&7]=V;
199     tekvrom();
200   }
201   else if(A>=0xb000 && A<=0xb007)
202   {
203     if(A&4)
204     {
205       names[A&3]&=0x00FF;
206       names[A&3]|=V<<8;
207     }
208     else
209     {
210       names[A&3]&=0xFF00;
211       names[A&3]|=V;
212     }
213     mira();
214   }
215   else if(A>=0xd000 && A<=0xdfff)
216   {
217     tkcom[A&3]=V;
218     tekprom();
219     tekvrom();
220     mira();
221 /*
222   switch (A&3)
223   {
224    case 00: FCEU_printf("Main Control Register:\n");
225             FCEU_printf("  PGR Banking mode: %d\n",V&7);
226             FCEU_printf("  CHR Banking mode: %d\n",(V>>3)&3);
227             FCEU_printf("  6000-7FFF addresses mapping: %s\n",(V&0x80)?"Yes":"No");
228             FCEU_printf("  Nametable control: %s\n",(V&0x20)?"Enabled":"Disabled");
229             if(V&0x20)
230                FCEU_printf("  Nametable can be: %s\n",(V&0x40)?"ROM Only":"RAM or ROM");
231             break;
232    case 01: FCEU_printf("Mirroring mode: ");
233             switch (V&3)
234             {
235              case 00: FCEU_printf("Vertical\n");break;
236              case 01: FCEU_printf("Horizontal\n");break;
237              case 02: FCEU_printf("Nametable 0 only\n");break;
238              case 03: FCEU_printf("Nametable 1 only\n");break;
239             }
240             FCEU_printf("Mirroring flag: %s\n",(V&0x80)?"On":"Off");
241             break;
242    case 02: if((((tkcom[0])>>5)&3)==1)
243               FCEU_printf("Nametable ROM/RAM select mode: %d\n",V>>7);
244             break;
245    case 03:
246             FCEU_printf("CHR Banking mode: %s\n",(V&0x20)?"Entire CHR ROM":"256Kb Switching mode");
247             if(!(V&0x20)) FCEU_printf("256K CHR bank number: %02x\n",(V&1)|((V&0x18)>>2));
248             FCEU_printf("512K PRG bank number: %d\n",(V&6)>>1);
249             FCEU_printf("CHR Bank mirroring: %s\n",(V&0x80)?"Swapped":"Normal operate");
250   }
251 */
252   }
253   else switch(A)
254   {
255     case 0xc000: //FCEU_printf("%s IRQ (C000)\n",V&1?"Enable":"Disable");
256                  IRQa=V&1;if(!(V&1)) X6502_IRQEnd(FCEU_IQEXT);break;
257     case 0xc002: //FCEU_printf("Disable IRQ (C002) scanline=%d\n", scanline);
258                  IRQa=0;X6502_IRQEnd(FCEU_IQEXT);break;
259     case 0xc003: //FCEU_printf("Enable IRQ (C003) scanline=%d\n", scanline);
260                  IRQa=1;break;
261     case 0xc001: IRQMode=V;
262 /*               FCEU_printf("IRQ Count method: ");
263                switch (IRQMode&3)
264                {
265                  case 00: FCEU_printf("M2 cycles\n");break;
266                  case 01: FCEU_printf("PPU A12 toggles\n");break;
267                  case 02: FCEU_printf("PPU reads\n");break;
268                  case 03: FCEU_printf("Writes to CPU space\n");break;
269                }
270                FCEU_printf("Counter prescaler size: %s\n",(IRQMode&4)?"3 bits":"8 bits");
271                FCEU_printf("Counter prescaler size adjust: %s\n",(IRQMode&8)?"Used C007":"Normal Operation");
272                if((IRQMode>>6)==2) FCEU_printf("Counter Down\n");
273                 else if((IRQMode>>6)==1) FCEU_printf("Counter Up\n");
274                 else FCEU_printf("Counter Stopped\n");
275 */               break;
276     case 0xc004: //FCEU_printf("Pre Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR);
277                  IRQPre=V^IRQXOR;break;
278     case 0xc005: //FCEU_printf("Main Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR);
279                  IRQCount=V^IRQXOR;break;
280     case 0xc006: //FCEU_printf("Xor Value: %d\n",V);
281                  IRQXOR=V;break;
282     case 0xc007: //if(!(IRQMode&8)) FCEU_printf("C001 is clear, no effect applied\n");
283                  // else if(V==0xFF) FCEU_printf("Prescaler is changed for 12bits\n");
284                  // else FCEU_printf("Counter Stopped\n");
285                  IRQPreSize=V;break;
286   }
287 }
288
289 static void CCL(void)
290 {
291   if((IRQMode>>6) == 1) // Count Up
292   {
293     IRQCount++;
294     if((IRQCount == 0) && IRQa)
295       X6502_IRQBegin(FCEU_IQEXT);
296   }
297   else if((IRQMode>>6) == 2) // Count down
298   {
299     IRQCount--;
300     if((IRQCount == 0xFF) && IRQa)
301       X6502_IRQBegin(FCEU_IQEXT);
302   }
303 }
304
305 static void ClockCounter(void)
306 {
307   uint8 premask;
308
309   if(IRQMode & 0x4)
310     premask = 0x7;
311   else
312     premask = 0xFF;
313   if((IRQMode>>6) == 1) // Count up
314   {
315     IRQPre++;
316     if((IRQPre & premask) == 0) CCL();
317   }
318   else if((IRQMode>>6) == 2) // Count down
319   {
320     IRQPre--;
321     if((IRQPre & premask) == premask) CCL();
322   }
323 }
324
325 static void SLWrap(void)
326 {
327   int x;
328   if((IRQMode&3)!=2) for(x=0;x<8;x++) ClockCounter();
329 }
330
331 static uint32 lastread;
332 static void FP_FASTAPASS(1) M90PPU(uint32 A)
333 {
334   if(lastread!=A)
335   {
336     lastread=A;
337     if((IRQMode&3)==2)
338     {
339       ClockCounter();
340       ClockCounter();
341     }
342   }
343 }
344
345 static void togglie()
346 {
347   tekker>>=6;
348   if(tekker>3)
349     tekker=0;
350   else
351     tekker++;
352   tekker<<=6;
353   FCEU_printf("tekker=%04x\n",tekker);
354   memset(tkcom,0xff,sizeof(tkcom));
355   memset(prgb,0xff,sizeof(prgb));
356   tekprom();
357   tekvrom();
358 }
359
360 static void M90Restore(int version)
361 {
362   tekprom();
363   tekvrom();
364   mira();
365 }
366
367 static void M90Power(void)
368 {
369   SetWriteHandler(0x5000,0xffff,M90Write);
370   SetReadHandler(0x5000,0x5fff,M90Read);
371   SetReadHandler(0x6000,0xffff,CartBR);
372
373   mul[0]=mul[1]=regie=0xFF;
374
375   memset(tkcom,0xff,sizeof(tkcom));
376   memset(prgb,0xff,sizeof(prgb));
377   memset(chrlow,0xff,sizeof(chrlow));
378   memset(chrhigh,0xff,sizeof(chrhigh));
379   memset(names,0x00,sizeof(names));
380
381   tekker=0;
382
383   tekprom();
384   tekvrom();
385 }
386
387
388 void Mapper90_Init(CartInfo *info)
389 {
390   is209=0;
391   info->Reset=togglie;
392   info->Power=M90Power;
393   PPU_hook = M90PPU;
394   GameHBIRQHook2 = SLWrap;
395   GameStateRestore=M90Restore;
396   AddExState(Tek_StateRegs, ~0, 0, 0);
397 }
398
399 void Mapper209_Init(CartInfo *info)
400 {
401   is209=1;
402   info->Reset=togglie;
403   info->Power=M90Power;
404   PPU_hook = M90PPU;
405   GameHBIRQHook2 = SLWrap;
406   GameStateRestore=M90Restore;
407   AddExState(Tek_StateRegs, ~0, 0, 0);
408 }