merge mappers from FCEU-mm
[fceu.git] / boards / 90.c
CommitLineData
d97315ac 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Xodnizel
e2d0dd92 5 * Copyright (C) 2005 CaH4e3
d97315ac 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
43725da7 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
d97315ac 20 */
21
22#include "mapinc.h"
e2d0dd92 23//#define DEBUG90
d97315ac 24
386f5371 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;
e2d0dd92 30static int is211;
d97315ac 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
386f5371 47static uint8 chr[2];
48
d97315ac 49static uint16 names[4];
50static uint8 tekker;
51
52static SFORMAT Tek_StateRegs[]={
386f5371 53 {&IRQMode, 1, "IRQM"},
54 {&IRQPre, 1, "IRQP"},
43725da7 55 {&IRQPreSize, 1, "IRQR"},
d97315ac 56 {&IRQCount, 1, "IRQC"},
386f5371 57 {&IRQXOR, 1, "IRQX"},
43725da7 58 {&IRQa, 1, "IRQA"},
d97315ac 59 {mul, 2, "MUL"},
60 {&regie, 1, "REGI"},
61 {tkcom, 4, "TKCO"},
62 {prgb, 4, "PRGB"},
43725da7 63 {chr, 2, "CLTC"},
d97315ac 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
d97315ac 74static void mira(void)
75{
e2d0dd92 76 if((tkcom[0]&0x20&&is209)||is211)
d97315ac 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 {
e2d0dd92 88 if((tkcom[1]&0x80)==(names[x]&0x80)) // RAM selected.
d97315ac 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
43725da7 107static void tekprom(void) // TODO: verify for single, small multi and large multi
d97315ac 108{
109 uint32 bankmode=((tkcom[3]&6)<<5);
e2d0dd92 110 switch(tkcom[0]&7)
d97315ac 111 {
e2d0dd92 112 case 00: if(tkcom[0]&0x80)
113 setprg8(0x6000,(((prgb[3]<<2)+3)&0x3F)|bankmode);
43725da7 114 setprg32(0x8000,(prgb[3]&7)|((tkcom[3]&7)<<3));
e2d0dd92 115 break;
116 case 01: if(tkcom[0]&0x80)
117 setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode);
43725da7 118 setprg16(0x8000,(prgb[1]&0x0F)|((tkcom[3]&7)<<4));
119 setprg16(0xC000,0x0F|((tkcom[3]&7)<<4));
e2d0dd92 120 break;
121 case 03: // bit reversion
43725da7 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));
e2d0dd92 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;
d97315ac 149 }
150}
151
152static 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
386f5371 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);
d97315ac 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
e2d0dd92 188static DECLFW(M90TekWrite)
d97315ac 189{
386f5371 190 switch(A&0x5C03)
d97315ac 191 {
e2d0dd92 192 case 0x5800: mul[0]=V; break;
193 case 0x5801: mul[1]=V; break;
194 case 0x5803: regie=V; break;
d97315ac 195 }
e2d0dd92 196}
197
198static DECLFR(M90TekRead)
199{
386f5371 200 switch(A&0x5C03)
d97315ac 201 {
e2d0dd92 202 case 0x5800: return (mul[0]*mul[1]);
203 case 0x5801: return((mul[0]*mul[1])>>8);
204 case 0x5803: return (regie);
386f5371 205 default: return tekker;
d97315ac 206 }
386f5371 207 return(0xff);
e2d0dd92 208}
209
210static DECLFW(M90PRGWrite)
211{
386f5371 212// FCEU_printf("bs %04x %02x\n",A,V);
e2d0dd92 213 prgb[A&3]=V;
214 tekprom();
215}
216
217static DECLFW(M90CHRlowWrite)
218{
386f5371 219// FCEU_printf("bs %04x %02x\n",A,V);
e2d0dd92 220 chrlow[A&7]=V;
221 tekvrom();
222}
223
224static DECLFW(M90CHRhiWrite)
225{
386f5371 226// FCEU_printf("bs %04x %02x\n",A,V);
e2d0dd92 227 chrhigh[A&7]=V;
228 tekvrom();
229}
230
231static DECLFW(M90NTWrite)
232{
386f5371 233// FCEU_printf("bs %04x %02x\n",A,V);
e2d0dd92 234 if(A&4)
d97315ac 235 {
e2d0dd92 236 names[A&3]&=0x00FF;
237 names[A&3]|=V<<8;
d97315ac 238 }
e2d0dd92 239 else
d97315ac 240 {
e2d0dd92 241 names[A&3]&=0xFF00;
242 names[A&3]|=V;
d97315ac 243 }
e2d0dd92 244 mira();
245}
246
247static DECLFW(M90IRQWrite)
248{
386f5371 249// FCEU_printf("bs %04x %02x\n",A,V);
e2d0dd92 250 switch(A&7)
d97315ac 251 {
e2d0dd92 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;
43725da7 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");*/
386f5371 272 break;
e2d0dd92 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");
386f5371 280 // else if(V==0xFF) FCEU_printf("Prescaler is changed for 12bits\n");
281 // else FCEU_printf("Counter Stopped\n");
e2d0dd92 282 IRQPreSize=V;break;
283 }
284}
285
286static DECLFW(M90ModeWrite)
287{
386f5371 288// FCEU_printf("bs %04x %02x\n",A,V);
d97315ac 289 tkcom[A&3]=V;
290 tekprom();
291 tekvrom();
292 mira();
e2d0dd92 293
294#ifdef DEBUG90
d97315ac 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 {
e2d0dd92 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;
d97315ac 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 }
e2d0dd92 324#endif
d97315ac 325}
326
386f5371 327static DECLFW(M90DummyWrite)
328{
329// FCEU_printf("bs %04x %02x\n",A,V);
330}
331
d97315ac 332static void CCL(void)
333{
334 if((IRQMode>>6) == 1) // Count Up
335 {
336 IRQCount++;
337 if((IRQCount == 0) && IRQa)
e2d0dd92 338 {
d97315ac 339 X6502_IRQBegin(FCEU_IQEXT);
e2d0dd92 340 }
d97315ac 341 }
342 else if((IRQMode>>6) == 2) // Count down
343 {
344 IRQCount--;
345 if((IRQCount == 0xFF) && IRQa)
e2d0dd92 346 {
d97315ac 347 X6502_IRQBegin(FCEU_IQEXT);
e2d0dd92 348 }
d97315ac 349 }
350}
351
352static 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
43725da7 372void FP_FASTAPASS(1) CPUWrap(int a)
e2d0dd92 373{
374 int x;
375 if((IRQMode&3)==0) for(x=0;x<a;x++) ClockCounter();
376}
377
d97315ac 378static void SLWrap(void)
379{
380 int x;
e2d0dd92 381 if((IRQMode&3)==1) for(x=0;x<8;x++) ClockCounter();
d97315ac 382}
383
384static uint32 lastread;
43725da7 385static void FP_FASTAPASS(1) M90PPU(uint32 A)
d97315ac 386{
e2d0dd92 387 if((IRQMode&3)==2)
d97315ac 388 {
e2d0dd92 389 if(lastread!=A)
d97315ac 390 {
391 ClockCounter();
392 ClockCounter();
393 }
e2d0dd92 394 lastread=A;
d97315ac 395 }
386f5371 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 }
d97315ac 421}
422
423static void togglie()
424{
386f5371 425 tekker+=0x40;
426 tekker&=0xC0;
427 FCEU_printf("tekker=%02x\n",tekker);
e2d0dd92 428 memset(tkcom,0x00,sizeof(tkcom));
d97315ac 429 memset(prgb,0xff,sizeof(prgb));
430 tekprom();
431 tekvrom();
432}
433
434static void M90Restore(int version)
435{
436 tekprom();
437 tekvrom();
438 mira();
439}
440
441static void M90Power(void)
442{
e2d0dd92 443 SetWriteHandler(0x5000,0x5fff,M90TekWrite);
386f5371 444 SetWriteHandler(0x8000,0x8ff0,M90PRGWrite);
e2d0dd92 445 SetWriteHandler(0x9000,0x9fff,M90CHRlowWrite);
446 SetWriteHandler(0xA000,0xAfff,M90CHRhiWrite);
447 SetWriteHandler(0xB000,0xBfff,M90NTWrite);
448 SetWriteHandler(0xC000,0xCfff,M90IRQWrite);
386f5371 449 SetWriteHandler(0xD000,0xD5ff,M90ModeWrite);
450 SetWriteHandler(0xE000,0xFfff,M90DummyWrite);
451
e2d0dd92 452 SetReadHandler(0x5000,0x5fff,M90TekRead);
d97315ac 453 SetReadHandler(0x6000,0xffff,CartBR);
454
455 mul[0]=mul[1]=regie=0xFF;
456
e2d0dd92 457 memset(tkcom,0x00,sizeof(tkcom));
d97315ac 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
e2d0dd92 463 if(is211)
464 tekker=0xC0;
465 else
466 tekker=0x00;
d97315ac 467
468 tekprom();
469 tekvrom();
470}
471
472
473void Mapper90_Init(CartInfo *info)
474{
e2d0dd92 475 is211=0;
d97315ac 476 is209=0;
477 info->Reset=togglie;
478 info->Power=M90Power;
e2d0dd92 479 PPU_hook=M90PPU;
e2d0dd92 480 MapIRQHook=CPUWrap;
386f5371 481 GameHBIRQHook2=SLWrap;
d97315ac 482 GameStateRestore=M90Restore;
483 AddExState(Tek_StateRegs, ~0, 0, 0);
484}
485
486void Mapper209_Init(CartInfo *info)
487{
e2d0dd92 488 is211=0;
d97315ac 489 is209=1;
490 info->Reset=togglie;
491 info->Power=M90Power;
386f5371 492 PPU_hook=M90PPU;
493 MapIRQHook=CPUWrap;
e2d0dd92 494 GameHBIRQHook2=SLWrap;
495 GameStateRestore=M90Restore;
496 AddExState(Tek_StateRegs, ~0, 0, 0);
497}
498
499void Mapper211_Init(CartInfo *info)
500{
501 is211=1;
502 info->Reset=togglie;
503 info->Power=M90Power;
386f5371 504 PPU_hook=M90PPU;
505 MapIRQHook=CPUWrap;
e2d0dd92 506 GameHBIRQHook2=SLWrap;
d97315ac 507 GameStateRestore=M90Restore;
508 AddExState(Tek_StateRegs, ~0, 0, 0);
509}