merge mapper code from FCEUX
[fceu.git] / boards / 90.c
... / ...
CommitLineData
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
29static int is209;
30static int is211;
31
32static uint8 IRQMode; // from $c001
33static uint8 IRQPre; // from $c004
34static uint8 IRQPreSize; // from $c007
35static uint8 IRQCount; // from $c005
36static uint8 IRQXOR; // Loaded from $C006
37static uint8 IRQa; // $c002, $c003, and $c000
38
39static uint8 mul[2];
40static uint8 regie;
41
42static uint8 tkcom[4];
43static uint8 prgb[4];
44static uint8 chrlow[8];
45static uint8 chrhigh[8];
46
47static uint8 chr[2];
48
49static uint16 names[4];
50static uint8 tekker;
51
52static SFORMAT Tek_StateRegs[]={
53 {&IRQMode, 1, "IRQM"},
54 {&IRQPre, 1, "IRQP"},
55 {&IRQPreSize, 1, "IRQS"},
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, "CHRA"},
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
74static 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
107static void tekprom(void)
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,0x0F|((tkcom[3]&6)<<3));
115 break;
116 case 01: if(tkcom[0]&0x80)
117 setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode);
118 setprg16(0x8000,(prgb[1]&0x1F)|((tkcom[3]&6)<<4));
119 setprg16(0xC000,0x1F|((tkcom[3]&6)<<4));
120 break;
121 case 03: // bit reversion
122 case 02: if(tkcom[0]&0x80)
123 setprg8(0x6000,(prgb[3]&0x3F)|bankmode);
124 setprg8(0x8000,(prgb[0]&0x3F)|bankmode);
125 setprg8(0xa000,(prgb[1]&0x3F)|bankmode);
126 setprg8(0xc000,(prgb[2]&0x3F)|bankmode);
127 setprg8(0xe000,0x3F|bankmode);
128 break;
129 case 04: if(tkcom[0]&0x80)
130 setprg8(0x6000,(((prgb[3]<<2)+3)&0x3F)|bankmode);
131 setprg32(0x8000,(prgb[3]&0x0F)|((tkcom[3]&6)<<3));
132 break;
133 case 05: if(tkcom[0]&0x80)
134 setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode);
135 setprg16(0x8000,(prgb[1]&0x1F)|((tkcom[3]&6)<<4));
136 setprg16(0xC000,(prgb[3]&0x1F)|((tkcom[3]&6)<<4));
137 break;
138 case 07: // bit reversion
139 case 06: if(tkcom[0]&0x80)
140 setprg8(0x6000,(prgb[3]&0x3F)|bankmode);
141 setprg8(0x8000,(prgb[0]&0x3F)|bankmode);
142 setprg8(0xa000,(prgb[1]&0x3F)|bankmode);
143 setprg8(0xc000,(prgb[2]&0x3F)|bankmode);
144 setprg8(0xe000,(prgb[3]&0x3F)|bankmode);
145 break;
146 }
147}
148
149static void tekvrom(void)
150{
151 int x, bank=0, mask=0xFFFF;
152 if(!(tkcom[3]&0x20))
153 {
154 bank=(tkcom[3]&1)|((tkcom[3]&0x18)>>2);
155 switch (tkcom[0]&0x18)
156 {
157 case 0x00: bank<<=5; mask=0x1F; break;
158 case 0x08: bank<<=6; mask=0x3F; break;
159 case 0x10: bank<<=7; mask=0x7F; break;
160 case 0x18: bank<<=8; mask=0xFF; break;
161 }
162 }
163 switch(tkcom[0]&0x18)
164 {
165 case 0x00: // 8KB
166 setchr8(((chrlow[0]|(chrhigh[0]<<8))&mask)|bank);
167 break;
168 case 0x08: // 4KB
169// for(x=0;x<8;x+=4)
170// setchr4(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
171 setchr4(0x0000,((chrlow[chr[0]]|(chrhigh[chr[0]]<<8))&mask)|bank);
172 setchr4(0x1000,((chrlow[chr[1]]|(chrhigh[chr[1]]<<8))&mask)|bank);
173 break;
174 case 0x10: // 2KB
175 for(x=0;x<8;x+=2)
176 setchr2(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
177 break;
178 case 0x18: // 1KB
179 for(x=0;x<8;x++)
180 setchr1(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank);
181 break;
182 }
183}
184
185static DECLFW(M90TekWrite)
186{
187 switch(A&0x5C03)
188 {
189 case 0x5800: mul[0]=V; break;
190 case 0x5801: mul[1]=V; break;
191 case 0x5803: regie=V; break;
192 }
193}
194
195static DECLFR(M90TekRead)
196{
197 switch(A&0x5C03)
198 {
199 case 0x5800: return (mul[0]*mul[1]);
200 case 0x5801: return((mul[0]*mul[1])>>8);
201 case 0x5803: return (regie);
202 default: return tekker;
203 }
204 return(0xff);
205}
206
207static DECLFW(M90PRGWrite)
208{
209// FCEU_printf("bs %04x %02x\n",A,V);
210 prgb[A&3]=V;
211 tekprom();
212}
213
214static DECLFW(M90CHRlowWrite)
215{
216// FCEU_printf("bs %04x %02x\n",A,V);
217 chrlow[A&7]=V;
218 tekvrom();
219}
220
221static DECLFW(M90CHRhiWrite)
222{
223// FCEU_printf("bs %04x %02x\n",A,V);
224 chrhigh[A&7]=V;
225 tekvrom();
226}
227
228static DECLFW(M90NTWrite)
229{
230// FCEU_printf("bs %04x %02x\n",A,V);
231 if(A&4)
232 {
233 names[A&3]&=0x00FF;
234 names[A&3]|=V<<8;
235 }
236 else
237 {
238 names[A&3]&=0xFF00;
239 names[A&3]|=V;
240 }
241 mira();
242}
243
244static DECLFW(M90IRQWrite)
245{
246// FCEU_printf("bs %04x %02x\n",A,V);
247 switch(A&7)
248 {
249 case 00: //FCEU_printf("%s IRQ (C000)\n",V&1?"Enable":"Disable");
250 IRQa=V&1;if(!(V&1)) X6502_IRQEnd(FCEU_IQEXT);break;
251 case 02: //FCEU_printf("Disable IRQ (C002) scanline=%d\n", scanline);
252 IRQa=0;X6502_IRQEnd(FCEU_IQEXT);break;
253 case 03: //FCEU_printf("Enable IRQ (C003) scanline=%d\n", scanline);
254 IRQa=1;break;
255 case 01: IRQMode=V;
256 // FCEU_printf("IRQ Count method: ");
257 // switch (IRQMode&3)
258 // {
259 // case 00: FCEU_printf("M2 cycles\n");break;
260 // case 01: FCEU_printf("PPU A12 toggles\n");break;
261 // case 02: FCEU_printf("PPU reads\n");break;
262 // case 03: FCEU_printf("Writes to CPU space\n");break;
263 // }
264 // FCEU_printf("Counter prescaler size: %s\n",(IRQMode&4)?"3 bits":"8 bits");
265 // FCEU_printf("Counter prescaler size adjust: %s\n",(IRQMode&8)?"Used C007":"Normal Operation");
266 // if((IRQMode>>6)==2) FCEU_printf("Counter Down\n");
267 // else if((IRQMode>>6)==1) FCEU_printf("Counter Up\n");
268 // else FCEU_printf("Counter Stopped\n");
269 break;
270 case 04: //FCEU_printf("Pre Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR);
271 IRQPre=V^IRQXOR;break;
272 case 05: //FCEU_printf("Main Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR);
273 IRQCount=V^IRQXOR;break;
274 case 06: //FCEU_printf("Xor Value: %d\n",V);
275 IRQXOR=V;break;
276 case 07: //if(!(IRQMode&8)) FCEU_printf("C001 is clear, no effect applied\n");
277 // else if(V==0xFF) FCEU_printf("Prescaler is changed for 12bits\n");
278 // else FCEU_printf("Counter Stopped\n");
279 IRQPreSize=V;break;
280 }
281}
282
283static DECLFW(M90ModeWrite)
284{
285// FCEU_printf("bs %04x %02x\n",A,V);
286 tkcom[A&3]=V;
287 tekprom();
288 tekvrom();
289 mira();
290
291#ifdef DEBUG90
292 switch (A&3)
293 {
294 case 00: FCEU_printf("Main Control Register:\n");
295 FCEU_printf(" PGR Banking mode: %d\n",V&7);
296 FCEU_printf(" CHR Banking mode: %d\n",(V>>3)&3);
297 FCEU_printf(" 6000-7FFF addresses mapping: %s\n",(V&0x80)?"Yes":"No");
298 FCEU_printf(" Nametable control: %s\n",(V&0x20)?"Enabled":"Disabled");
299 if(V&0x20)
300 FCEU_printf(" Nametable can be: %s\n",(V&0x40)?"ROM Only":"RAM or ROM");
301 break;
302 case 01: FCEU_printf("Mirroring mode: ");
303 switch (V&3)
304 {
305 case 0: FCEU_printf("Vertical\n");break;
306 case 1: FCEU_printf("Horizontal\n");break;
307 case 2: FCEU_printf("Nametable 0 only\n");break;
308 case 3: FCEU_printf("Nametable 1 only\n");break;
309 }
310 FCEU_printf("Mirroring flag: %s\n",(V&0x80)?"On":"Off");
311 break;
312 case 02: if((((tkcom[0])>>5)&3)==1)
313 FCEU_printf("Nametable ROM/RAM select mode: %d\n",V>>7);
314 break;
315 case 03:
316 FCEU_printf("CHR Banking mode: %s\n",(V&0x20)?"Entire CHR ROM":"256Kb Switching mode");
317 if(!(V&0x20)) FCEU_printf("256K CHR bank number: %02x\n",(V&1)|((V&0x18)>>2));
318 FCEU_printf("512K PRG bank number: %d\n",(V&6)>>1);
319 FCEU_printf("CHR Bank mirroring: %s\n",(V&0x80)?"Swapped":"Normal operate");
320 }
321#endif
322}
323
324static DECLFW(M90DummyWrite)
325{
326// FCEU_printf("bs %04x %02x\n",A,V);
327}
328
329static void CCL(void)
330{
331 if((IRQMode>>6) == 1) // Count Up
332 {
333 IRQCount++;
334 if((IRQCount == 0) && IRQa)
335 {
336 X6502_IRQBegin(FCEU_IQEXT);
337 }
338 }
339 else if((IRQMode>>6) == 2) // Count down
340 {
341 IRQCount--;
342 if((IRQCount == 0xFF) && IRQa)
343 {
344 X6502_IRQBegin(FCEU_IQEXT);
345 }
346 }
347}
348
349static void ClockCounter(void)
350{
351 uint8 premask;
352
353 if(IRQMode & 0x4)
354 premask = 0x7;
355 else
356 premask = 0xFF;
357 if((IRQMode>>6) == 1) // Count up
358 {
359 IRQPre++;
360 if((IRQPre & premask) == 0) CCL();
361 }
362 else if((IRQMode>>6) == 2) // Count down
363 {
364 IRQPre--;
365 if((IRQPre & premask) == premask) CCL();
366 }
367}
368
369void CPUWrap(int a)
370{
371 int x;
372 if((IRQMode&3)==0) for(x=0;x<a;x++) ClockCounter();
373}
374
375static void SLWrap(void)
376{
377 int x;
378 if((IRQMode&3)==1) for(x=0;x<8;x++) ClockCounter();
379}
380
381static uint32 lastread;
382static void M90PPU(uint32 A)
383{
384 if((IRQMode&3)==2)
385 {
386 if(lastread!=A)
387 {
388 ClockCounter();
389 ClockCounter();
390 }
391 lastread=A;
392 }
393
394 if(is209)
395 {
396 uint8 l,h;
397 h=A>>8;
398 if(h<0x20&&((h&0x0F)==0xF))
399 {
400 l=A&0xF0;
401 if(l==0xD0)
402 {
403 chr[(h&0x10)>>4]=((h&0x10)>>2);
404 tekvrom();
405 }
406 else if(l==0xE0)
407 {
408 chr[(h&0x10)>>4]=((h&0x10)>>2)|2;
409 tekvrom();
410 }
411 }
412 }
413 else
414 {
415 chr[0]=0;
416 chr[1]=4;
417 }
418}
419
420static void togglie()
421{
422 tekker+=0x40;
423 tekker&=0xC0;
424 FCEU_printf("tekker=%02x\n",tekker);
425 memset(tkcom,0x00,sizeof(tkcom));
426 memset(prgb,0xff,sizeof(prgb));
427 tekprom();
428 tekvrom();
429}
430
431static void M90Restore(int version)
432{
433 tekprom();
434 tekvrom();
435 mira();
436}
437
438static void M90Power(void)
439{
440 SetWriteHandler(0x5000,0x5fff,M90TekWrite);
441 SetWriteHandler(0x8000,0x8ff0,M90PRGWrite);
442 SetWriteHandler(0x9000,0x9fff,M90CHRlowWrite);
443 SetWriteHandler(0xA000,0xAfff,M90CHRhiWrite);
444 SetWriteHandler(0xB000,0xBfff,M90NTWrite);
445 SetWriteHandler(0xC000,0xCfff,M90IRQWrite);
446 SetWriteHandler(0xD000,0xD5ff,M90ModeWrite);
447 SetWriteHandler(0xE000,0xFfff,M90DummyWrite);
448
449
450 SetReadHandler(0x5000,0x5fff,M90TekRead);
451 SetReadHandler(0x6000,0xffff,CartBR);
452
453 mul[0]=mul[1]=regie=0xFF;
454
455 memset(tkcom,0x00,sizeof(tkcom));
456 memset(prgb,0xff,sizeof(prgb));
457 memset(chrlow,0xff,sizeof(chrlow));
458 memset(chrhigh,0xff,sizeof(chrhigh));
459 memset(names,0x00,sizeof(names));
460
461 if(is211)
462 tekker=0xC0;
463 else
464 tekker=0x00;
465
466 tekprom();
467 tekvrom();
468}
469
470
471void Mapper90_Init(CartInfo *info)
472{
473 is211=0;
474 is209=0;
475 info->Reset=togglie;
476 info->Power=M90Power;
477 PPU_hook=M90PPU;
478 MapIRQHook=CPUWrap;
479 GameHBIRQHook2=SLWrap;
480 GameStateRestore=M90Restore;
481 AddExState(Tek_StateRegs, ~0, 0, 0);
482}
483
484void Mapper209_Init(CartInfo *info)
485{
486 is211=0;
487 is209=1;
488 info->Reset=togglie;
489 info->Power=M90Power;
490 PPU_hook=M90PPU;
491 MapIRQHook=CPUWrap;
492 GameHBIRQHook2=SLWrap;
493 GameStateRestore=M90Restore;
494 AddExState(Tek_StateRegs, ~0, 0, 0);
495}
496
497void Mapper211_Init(CartInfo *info)
498{
499 is211=1;
500 info->Reset=togglie;
501 info->Power=M90Power;
502 PPU_hook=M90PPU;
503 MapIRQHook=CPUWrap;
504 GameHBIRQHook2=SLWrap;
505 GameStateRestore=M90Restore;
506 AddExState(Tek_StateRegs, ~0, 0, 0);
507}