2 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
4 * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
5 * Jerremy Koot (jkoot@snes9x.com)
7 * Super FX C emulator code
8 * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
10 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
12 * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
13 * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
14 * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
16 * DOS port code contains the works of other authors. See headers in
19 * Snes9x homepage: http://www.snes9x.com
21 * Permission to use, copy, modify and distribute Snes9x in both binary and
22 * source form, for non-commercial purposes, is hereby granted without fee,
23 * providing that this license information and copyright notice appear with
24 * all copies and any derived work.
26 * This software is provided 'as-is', without any express or implied
27 * warranty. In no event shall the authors be held liable for any damages
28 * arising from the use of this software.
30 * Snes9x is freeware for PERSONAL USE only. Commercial users should
31 * seek permission of the copyright holders first. Commercial use includes
32 * charging money for Snes9x or software derived from Snes9x.
34 * The copyright holders request that bug fixes and improvements to the code
35 * should be forwarded to them so everyone can benefit from the modifications
38 * Super NES and Super Nintendo Entertainment System are trademarks of
39 * Nintendo Co., Limited and its subsidiary companies.
47 /* The FxChip Emulator's internal variables */
48 struct FxRegs_s GSU = {{0}, 0};
50 uint32 (**fx_ppfFunctionTable)(uint32) = 0;
51 void (**fx_ppfPlotTable)() = 0;
52 void (**fx_ppfOpcodeTable)() = 0;
58 GSU.bCacheActive = TRUE;
59 GSU.pvRegisters[0x3e] &= 0xf0;
60 c = (uint32)GSU.pvRegisters[0x3e];
61 c |= ((uint32)GSU.pvRegisters[0x3f])<<8;
62 if(c == GSU.vCacheBaseReg)
64 GSU.vCacheBaseReg = c;
68 uint8 const* t = &ROM(c);
69 memcpy(GSU.pvCache,t,512);
75 uint32 i = 0x10000 - c;
78 memcpy(GSU.pvCache,t1,i);
79 memcpy(&GSU.pvCache[i],t2,512-i);
84 void FxCacheWriteAccess(uint16 vAddress)
89 uint8 v = GSU.pvCache[GSU.pvCache[vAddress&0x1ff];
91 GSU.pvCache[GSU.pvCache[vAddress&0x1ff] = v;
94 if((vAddress & 0x00f) == 0x00f)
95 GSU.vCacheFlags |= 1 << ((vAddress&0x1f0) >> 4);
101 GSU.vCacheBaseReg = 0;
102 GSU.bCacheActive = FALSE;
106 static void fx_backupCache()
110 uint32 v = GSU.vCacheFlags;
111 uint32 c = USEX16(GSU.vCacheBaseReg);
119 uint8 * t = &GSU.pvPrgBank[c];
120 memcpy(&GSU.avCacheBackup[i<<4],t,16);
121 memcpy(t,&GSU.pvCache[i<<4],16);
127 uint32 a = 0x10000 - c;
128 t1 = &GSU.pvPrgBank[c];
129 t2 = &GSU.pvPrgBank[0];
130 memcpy(&GSU.avCacheBackup[i<<4],t1,a);
131 memcpy(t1,&GSU.pvCache[i<<4],a);
132 memcpy(&GSU.avCacheBackup[(i<<4)+a],t2,16-a);
133 memcpy(t2,&GSU.pvCache[(i<<4)+a],16-a);
142 static void fx_restoreCache()
146 uint32 v = GSU.vCacheFlags;
147 uint32 c = USEX16(GSU.vCacheBaseReg);
155 uint8 * t = &GSU.pvPrgBank[c];
156 memcpy(t,&GSU.avCacheBackup[i<<4],16);
157 memcpy(&GSU.pvCache[i<<4],t,16);
163 uint32 a = 0x10000 - c;
164 t1 = &GSU.pvPrgBank[c];
165 t2 = &GSU.pvPrgBank[0];
166 memcpy(t1,&GSU.avCacheBackup[i<<4],a);
167 memcpy(&GSU.pvCache[i<<4],t1,a);
168 memcpy(t2,&GSU.avCacheBackup[(i<<4)+a],16-a);
169 memcpy(&GSU.pvCache[(i<<4)+a],t2,16-a);
182 GSU.bCacheActive = FALSE;
185 static void fx_readRegisterSpace()
189 static uint32 avHeight[] = { 128, 160, 192, 256 };
190 static uint32 avMult[] = { 16, 32, 32, 64 };
199 GSU.avReg[i] += ((uint32)(*p++)) << 8;
202 /* Update other registers */
204 GSU.vStatusReg = (uint32)p[GSU_SFR];
205 GSU.vStatusReg |= ((uint32)p[GSU_SFR+1]) << 8;
206 GSU.vPrgBankReg = (uint32)p[GSU_PBR];
207 GSU.vRomBankReg = (uint32)p[GSU_ROMBR];
208 GSU.vRamBankReg = ((uint32)p[GSU_RAMBR]) & (FX_RAM_BANKS-1);
209 GSU.vCacheBaseReg = (uint32)p[GSU_CBR];
210 GSU.vCacheBaseReg |= ((uint32)p[GSU_CBR+1]) << 8;
212 /* Update status register variables */
213 GSU.vZero = !(GSU.vStatusReg & FLG_Z);
214 GSU.vSign = (GSU.vStatusReg & FLG_S) << 12;
215 GSU.vOverflow = (GSU.vStatusReg & FLG_OV) << 16;
216 GSU.vCarry = (GSU.vStatusReg & FLG_CY) >> 2;
218 /* Set bank pointers */
219 GSU.pvRamBank = GSU.apvRamBank[GSU.vRamBankReg & 0x3];
220 GSU.pvRomBank = GSU.apvRomBank[GSU.vRomBankReg];
221 GSU.pvPrgBank = GSU.apvRomBank[GSU.vPrgBankReg];
223 /* Set screen pointers */
224 GSU.pvScreenBase = &GSU.pvRam[ USEX8(p[GSU_SCBR]) << 10 ];
225 i = (int)(!!(p[GSU_SCMR] & 0x04));
226 i |= ((int)(!!(p[GSU_SCMR] & 0x20))) << 1;
227 GSU.vScreenHeight = GSU.vScreenRealHeight = avHeight[i];
228 GSU.vMode = p[GSU_SCMR] & 0x03;
231 error illegal color depth GSU.vMode;
234 GSU.vScreenSize = (256/8) * (256/8) * 32;
236 GSU.vScreenSize = (GSU.vScreenHeight/8) * (256/8) * avMult[GSU.vMode];
237 if (GSU.vPlotOptionReg & 0x10)
239 /* OBJ Mode (for drawing into sprites) */
240 GSU.vScreenHeight = 256;
243 if(GSU.pvScreenBase + GSU.vScreenSize > GSU.pvRam + (GSU.nRamBanks * 65536))
244 error illegal address for screen base register
246 if(GSU.pvScreenBase + GSU.vScreenSize > GSU.pvRam + (GSU.nRamBanks * 65536))
247 GSU.pvScreenBase = GSU.pvRam + (GSU.nRamBanks * 65536) - GSU.vScreenSize;
249 GSU.pfPlot = fx_apfPlotTable[GSU.vMode];
250 GSU.pfRpix = fx_apfPlotTable[GSU.vMode + 5];
252 fx_ppfOpcodeTable[0x04c] = GSU.pfPlot;
253 fx_ppfOpcodeTable[0x14c] = GSU.pfRpix;
254 fx_ppfOpcodeTable[0x24c] = GSU.pfPlot;
255 fx_ppfOpcodeTable[0x34c] = GSU.pfRpix;
257 fx_computeScreenPointers ();
262 void fx_computeScreenPointers ()
264 if (GSU.vMode != GSU.vPrevMode ||
265 GSU.vPrevScreenHeight != GSU.vScreenHeight)
269 /* Make a list of pointers to the start of each screen column */
270 switch (GSU.vScreenHeight)
276 for (i = 0; i < 32; i++)
278 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 4);
283 for (i = 0; i < 32; i++)
285 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 5);
291 for (i = 0; i < 32; i++)
293 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 6);
303 for (i = 0; i < 32; i++)
305 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 4);
306 GSU.x[i] = (i << 8) + (i << 6);
310 for (i = 0; i < 32; i++)
312 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 5);
313 GSU.x[i] = (i << 9) + (i << 7);
318 for (i = 0; i < 32; i++)
320 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 6);
321 GSU.x[i] = (i << 10) + (i << 8);
330 for (i = 0; i < 32; i++)
332 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 4);
333 GSU.x[i] = (i << 8) + (i << 7);
337 for (i = 0; i < 32; i++)
339 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 5);
340 GSU.x[i] = (i << 9) + (i << 8);
345 for (i = 0; i < 32; i++)
347 GSU.apvScreen[i] = GSU.pvScreenBase + (i << 6);
348 GSU.x[i] = (i << 10) + (i << 9);
357 for (i = 0; i < 32; i++)
359 GSU.apvScreen[i] = GSU.pvScreenBase +
360 ((i & 0x10) << 9) + ((i & 0xf) << 8);
361 GSU.x[i] = ((i & 0x10) << 8) + ((i & 0xf) << 4);
365 for (i = 0; i < 32; i++)
367 GSU.apvScreen[i] = GSU.pvScreenBase +
368 ((i & 0x10) << 10) + ((i & 0xf) << 9);
369 GSU.x[i] = ((i & 0x10) << 9) + ((i & 0xf) << 5);
374 for (i = 0; i < 32; i++)
376 GSU.apvScreen[i] = GSU.pvScreenBase +
377 ((i & 0x10) << 11) + ((i & 0xf) << 10);
378 GSU.x[i] = ((i & 0x10) << 10) + ((i & 0xf) << 6);
384 GSU.vPrevMode = GSU.vMode;
385 GSU.vPrevScreenHeight = GSU.vScreenHeight;
389 static void fx_writeRegisterSpace()
397 *p++ = (uint8)GSU.avReg[i];
398 *p++ = (uint8)(GSU.avReg[i] >> 8);
401 /* Update status register */
402 if( USEX16(GSU.vZero) == 0 ) SF(Z);
404 if( GSU.vSign & 0x8000 ) SF(S);
406 if(GSU.vOverflow >= 0x8000 || GSU.vOverflow < -0x8000) SF(OV);
408 if(GSU.vCarry) SF(CY);
412 p[GSU_SFR] = (uint8)GSU.vStatusReg;
413 p[GSU_SFR+1] = (uint8)(GSU.vStatusReg>>8);
414 p[GSU_PBR] = (uint8)GSU.vPrgBankReg;
415 p[GSU_ROMBR] = (uint8)GSU.vRomBankReg;
416 p[GSU_RAMBR] = (uint8)GSU.vRamBankReg;
417 p[GSU_CBR] = (uint8)GSU.vCacheBaseReg;
418 p[GSU_CBR+1] = (uint8)(GSU.vCacheBaseReg>>8);
423 /* Reset the FxChip */
424 void FxReset(struct FxInit_s *psFxInfo)
427 static uint32 (**appfFunction[])(uint32) = {
428 &fx_apfFunctionTable[0],
430 &fx_a_apfFunctionTable[0],
431 &fx_r_apfFunctionTable[0],
432 &fx_ar_apfFunctionTable[0],
435 static void (**appfPlot[])() = {
438 &fx_a_apfPlotTable[0],
439 &fx_r_apfPlotTable[0],
440 &fx_ar_apfPlotTable[0],
443 static void (**appfOpcode[])() = {
444 &fx_apfOpcodeTable[0],
446 &fx_a_apfOpcodeTable[0],
447 &fx_r_apfOpcodeTable[0],
448 &fx_ar_apfOpcodeTable[0],
452 /* Get function pointers for the current emulation mode */
453 fx_ppfFunctionTable = appfFunction[psFxInfo->vFlags & 0x3];
454 fx_ppfPlotTable = appfPlot[psFxInfo->vFlags & 0x3];
455 fx_ppfOpcodeTable = appfOpcode[psFxInfo->vFlags & 0x3];
457 /* Clear all internal variables */
458 memset((uint8*)&GSU,0,sizeof(struct FxRegs_s));
460 /* Set default registers */
461 GSU.pvSreg = GSU.pvDreg = &R0;
463 /* Set RAM and ROM pointers */
464 GSU.pvRegisters = psFxInfo->pvRegisters;
465 GSU.nRamBanks = psFxInfo->nRamBanks;
466 GSU.pvRam = psFxInfo->pvRam;
467 GSU.nRomBanks = psFxInfo->nRomBanks;
468 GSU.pvRom = psFxInfo->pvRom;
469 GSU.vPrevScreenHeight = ~0;
472 /* The GSU can't access more than 2mb (16mbits) */
473 if(GSU.nRomBanks > 0x20)
474 GSU.nRomBanks = 0x20;
476 /* Clear FxChip register space */
477 memset(GSU.pvRegisters,0,0x300);
479 /* Set FxChip version Number */
480 GSU.pvRegisters[0x3b] = 0;
482 /* Make ROM bank table */
488 if (GSU.nRomBanks > 1)
493 GSU.apvRomBank[i] = &GSU.pvRom[ b << 16 ];
497 b %= GSU.nRomBanks * 2;
498 GSU.apvRomBank[i] = &GSU.pvRom[ (b << 16) + 0x200000];
502 /* Make RAM bank table */
505 GSU.apvRamBank[i] = &GSU.pvRam[(i % GSU.nRamBanks) << 16];
506 GSU.apvRomBank[0x70 + i] = GSU.apvRamBank[i];
509 /* Start with a nop in the pipe */
512 /* Set pointer to GSU cache */
513 GSU.pvCache = &GSU.pvRegisters[0x100];
515 fx_readRegisterSpace();
518 static uint8 fx_checkStartAddress()
520 /* Check if we start inside the cache */
521 if(GSU.bCacheActive && R15 >= GSU.vCacheBaseReg && R15 < (GSU.vCacheBaseReg+512))
524 /* Check if we're in an unused area */
525 if(GSU.vPrgBankReg < 0x40 && R15 < 0x8000)
527 if(GSU.vPrgBankReg >= 0x60 && GSU.vPrgBankReg <= 0x6f)
529 if(GSU.vPrgBankReg >= 0x74)
532 /* Check if we're in RAM and the RAN flag is not set */
533 if(GSU.vPrgBankReg >= 0x70 && GSU.vPrgBankReg <= 0x73 && !(SCMR&(1<<3)) )
536 /* If not, we're in ROM, so check if the RON flag is set */
543 /* Execute until the next stop instruction */
544 int FxEmulate(uint32 nInstructions)
548 /* Read registers and initialize GSU session */
549 fx_readRegisterSpace();
551 /* Check if the start address is valid */
552 if(!fx_checkStartAddress())
555 fx_writeRegisterSpace();
557 GSU.vIllegalAddress = (GSU.vPrgBankReg << 24) | R15;
558 return FX_ERROR_ILLEGAL_ADDRESS;
564 /* Execute GSU session */
568 vCount = fx_ppfFunctionTable[FX_FUNCTION_RUN_TO_BREAKPOINT](nInstructions);
570 vCount = fx_ppfFunctionTable[FX_FUNCTION_RUN](nInstructions);
572 /* Store GSU registers */
573 fx_writeRegisterSpace();
575 /* Check for error code */
577 return GSU.vErrorCode;
583 void FxBreakPointSet(uint32 vAddress)
585 GSU.bBreakPoint = TRUE;
586 GSU.vBreakPoint = USEX16(vAddress);
588 void FxBreakPointClear()
590 GSU.bBreakPoint = FALSE;
593 /* Step by step execution */
594 int FxStepOver(uint32 nInstructions)
597 fx_readRegisterSpace();
599 /* Check if the start address is valid */
600 if(!fx_checkStartAddress())
604 GSU.vIllegalAddress = (GSU.vPrgBankReg << 24) | R15;
605 return FX_ERROR_ILLEGAL_ADDRESS;
612 GSU.vStepPoint = USEX16(R15+3);
613 else if( (PIPE >= 0x05 && PIPE <= 0x0f) || (PIPE >= 0xa0 && PIPE <= 0xaf) )
614 GSU.vStepPoint = USEX16(R15+2);
616 GSU.vStepPoint = USEX16(R15+1);
617 vCount = fx_ppfFunctionTable[FX_FUNCTION_STEP_OVER](nInstructions);
618 fx_writeRegisterSpace();
620 return GSU.vErrorCode;
628 return GSU.vErrorCode;
631 int FxGetIllegalAddress()
633 return GSU.vIllegalAddress;
636 /* Access to internal registers */
637 uint32 FxGetColorRegister()
639 return GSU.vColorReg & 0xff;
642 uint32 FxGetPlotOptionRegister()
644 return GSU.vPlotOptionReg & 0x1f;
647 uint32 FxGetSourceRegisterIndex()
649 return GSU.pvSreg - GSU.avReg;
652 uint32 FxGetDestinationRegisterIndex()
654 return GSU.pvDreg - GSU.avReg;