www.pudn.com > virtual_gameboy-0.8.8.zip > GB.C


/** VGB: portable GameBoy emulator ***************************/ 
/**                                                         **/ 
/**                           GB.c                          **/ 
/**                                                         **/ 
/** This file contains the portable part of the GameBoy     **/ 
/** hardware emulation. See GB.h for #defines related to    **/ 
/** drivers and GameBoy hardware.                           **/ 
/**                                                         **/ 
/** Copyright (C) Marat Fayzullin 1995,1996                 **/ 
/**               Marcel de Kogel 1996                      **/ 
/**     You are not allowed to distribute this software     **/ 
/**     commercially. Please, notify me, if you make any    **/    
/**     changes to this file.                               **/ 
/*************************************************************/ 
 
#include "GB.h" 
#include  
#include  
#include  
#include  
 
byte Verbose    = 1;     /* Verboseness level                            */ 
 
byte *RAM,*Page[8];      /* RAM and pointers to Z80 address space 8x8kB) */ 
 
int  VPeriod    = 69905; /* Number of Z80 cycles between VBlanks         */ 
byte UPeriod    = 1;     /* Number of VBlanks per screen update          */ 
byte LineDelay  = 1;     /* When 1, screen refreshing is delayed         */ 
byte CheckCRC   = 1;     /* When 1, VGB checks cartridge CRC on loading  */ 
 
char *SaveName  = NULL;  /* .sav file name                               */ 
 
char *SndName   = NULL;  /* Soundtrack log file                          */ 
FILE *SndStream = NULL; 
 
struct                   /* Cheat list to be filled before StartGB()     */ 
{                        /* is called                                    */ 
  byte Value,Orig; 
  word Address; 
} Cheats[MAXCHEAT]; 
int CheatCount  = 0;     /* Number of cheats in the list                 */ 
 
byte SIOBuf;             /* Next byte to output via the serial line      */ 
byte SIOFlag;            /* Bit0 -> SIO interrupt should be generated    */ 
                         /* Bit1 -> Data should be sent out              */ 
 
byte IMask;              /* A mask to reset an event bit in IFLAGS       */ 
 
byte MBCType;            /* MBC type: 1 for MBC2, 0 for MBC1             */ 
byte *ROMMap[256];       /* Addresses of ROM banks                       */ 
byte ROMBank;            /* Number of ROM bank currently used            */ 
byte ROMMask;            /* Mask for the ROM bank number                 */ 
int  ROMBanks;           /* Total number of ROM banks                    */ 
byte *RAMMap[256];       /* Addresses of RAM banks                       */  
byte RAMBank;            /* Number of RAM bank currently used            */ 
byte RAMMask;            /* Mask for the RAM bank number                 */ 
int  RAMBanks;           /* Total number of RAM banks                    */ 
 
byte JoyState   = 0xFF;  /* Joystick state: START.SELECT.B.A.D.U.L.R     */ 
byte AutoA      = 0;     /* When 1, autofire emulation for button A      */ 
byte AutoB      = 0;     /* When 1, autofire emulation for button B      */ 
 
unsigned TCount,TStep;   /* Timer counter and increment                  */ 
unsigned SIOCount;       /* Serial I/O counter                           */  
 
byte BPal[4];            /* Background palette                           */ 
byte SPal0[4],SPal1[4];  /* Sprite palettes                              */ 
 
byte *ChrGen;            /* Character generator                          */ 
byte *BgdTab,*WndTab;    /* Background and window character tables       */ 
 
int IFreq=60;            /* Interrupt Frequency in Hertz                 */ 
 
static unsigned NumInts=0; 
static void WriteIntStamp (void) 
{ 
 if (NumInts>65535) 
 { 
  fputc (0xFF,SndStream); 
  fputc (NumInts,SndStream); 
  fputc (NumInts>>8,SndStream); 
  fputc (NumInts>>16,SndStream); 
  fputc (NumInts>>24,SndStream); 
 } 
 else 
 { 
  if (NumInts>255) 
  { 
   fputc (0xFE,SndStream); 
   fputc (NumInts,SndStream); 
   fputc (NumInts>>8,SndStream); 
  } 
  else 
  { 
   fputc (0xFD,SndStream); 
   fputc (NumInts,SndStream); 
  } 
 } 
 NumInts=0; 
} 
static void _Sound (byte Reg, byte Value) 
{ 
 if(SndStream) 
 { WriteIntStamp();fputc(Reg,SndStream);fputc(Value,SndStream); } 
 Sound (Reg,Value); 
} 
 
static void WriteROM (unsigned A,byte V)  __attribute__ ((regparm(3))); 
static void WriteCartRAM (unsigned A,byte V) __attribute__ ((regparm(3))); 
static void WriteRAM (unsigned A,byte V) __attribute__ ((regparm(3))); 
static void WROMBank (unsigned A,byte V) __attribute__ ((regparm(3))); 
static void WRAMBank (unsigned A,byte V) __attribute__ ((regparm(3))); 
static void WriteIO (unsigned A,byte V) __attribute__ ((regparm(3))); 
 
static void WriteCartRAM (unsigned A,byte V) 
{ 
 Page[A>>13][A&0x1FFF]=V; 
} 
 
static void WriteRAM (unsigned A,byte V) 
{ 
 RAM[A]=V; 
} 
 
static void WROMBank (unsigned A,byte V) 
{ 
 if(MBCType&&((A&0xFF00)!=0x2100)) return; 
 V&=ROMMask; 
 if(!V) V=1; 
 if(ROMMask&&(V!=ROMBank)) 
 { 
  ROMBank=V; 
  Page[2]=ROMMap[V]? ROMMap[V]:RAM+0x4000; 
  Page[3]=Page[2]+0x2000; 
  if(Verbose&0x08) printf("ROM: Bank %d selected\n",V); 
 } 
} 
 
static void WriteROM (unsigned A,byte V) 
{ 
 if(Verbose&0x02) printf("Wrote %Xh to ROM at %Xh\n",V,A); 
} 
 
static void WRAMBank (unsigned A,byte V) 
{ 
 V&=RAMMask; 
 if(RAMMask&&!MBCType&&(RAMBank!=V)) 
 { 
  RAMBank=V; 
  Page[5]=RAMMap[V]? RAMMap[V]:RAM+0xA000; 
  if(Verbose&0x08) printf("RAM: Bank %d selected\n",V); 
 } 
} 
 
static void WriteIO(unsigned A,byte V) 
{ 
  static unsigned long TPFreqs[] = { 4096,262144,65536,16384 }; 
  byte *P; 
 
  switch(A) 
  { 
    case 0xFF00: JOYPAD=0xCF|V; 
                 if(!(V&0x20)) JOYPAD&=(JoyState>>4)|0xF0; 
                 if(!(V&0x10)) JOYPAD&=JoyState|0xF0;  
                 return; 
    case 0xFF01: SIOBuf=V;SIOFlag|=0x02;return; 
    case 0xFF02: SIOCount=0; 
                 if(V&0x80) 
                 { 
                   if ((V&1)==0)        /* external clock */ 
                   { 
                    if(SIOFlag&0x02? SIOSend(SIOBuf):SIOReceive(&SIODATA)) 
                     SIOCount=8; 
                   } 
                   else                 /* internal clock */ 
                    SIOCount=8; 
                   SIOFlag=(SIOFlag&0xFD); 
                 } 
                 V|=0x7E;  
                 break; 
    case 0xFF07: TStep=(TPFreqs[V&0x03]<<16)/(154*IFreq); 
                 V|=0xF8;break; 
    case 0xFF0F: V&=0x1F;break; 
    case 0xFFFF: V&=0x1F;break; 
    case 0xFF46: P=RAM+0xFE00;A=(word)V<<8; 
                 for(V=0;V<0xA0;V++) *P++=M_RDMEM(A++); 
                 return; 
    case 0xFF41: V=(V&0xF8)|(LCDSTAT&0x07); 
                 break; 
    case 0xFF40: ChrGen=RAM+(V&0x10? 0x8000:0x8800); 
                 BgdTab=RAM+(V&0x08? 0x9C00:0x9800); 
                 WndTab=RAM+(V&0x40? 0x9C00:0x9800); 
                 break; 
    case 0xFF44: V=0;break; 
    case 0xFF47: BPal[0]=V&0x03; 
                 BPal[1]=(V&0x0C)>>2; 
                 BPal[2]=(V&0x30)>>4; 
                 BPal[3]=(V&0xC0)>>6; 
                 break; 
    case 0xFF48: SPal0[0]=V&0x03; 
                 SPal0[1]=(V&0x0C)>>2; 
                 SPal0[2]=(V&0x30)>>4; 
                 SPal0[3]=(V&0xC0)>>6; 
                 break; 
    case 0xFF49: SPal1[0]=V&0x03; 
                 SPal1[1]=(V&0x0C)>>2; 
                 SPal1[2]=(V&0x30)>>4; 
                 SPal1[3]=(V&0xC0)>>6; 
                 break; 
    default:     if((A>=0xFF10)&&(A<=0xFF26)) 
                  _Sound(A-0xFF10,V); 
  } 
  RAM[A]=V; 
} 
 
WriteMemFn WriteMemFnTable[256]= { 
 /* 0000-1FFF ROM */ 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 /* 2000-3FFF ROM Bank Select */ 
 WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank, 
 WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank, 
 WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank, 
 WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank,WROMBank, 
 /* 4000-5FFF RAM Bank Select */ 
 WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank, 
 WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank, 
 WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank, 
 WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank,WRAMBank, 
 /* 6000-7FFF ROM */ 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM,WriteROM, 
 /* 8000-9FFF RAM */ 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 /* A000-BFFF Cartridge RAM */ 
 WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM, 
 WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM, 
 WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM, 
 WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM,WriteCartRAM, 
 /* C000-FEFF RAM */ 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM,WriteRAM, 
 /* FF00-FFFF I/O */ 
 WriteIO 
}; 
 
void M_WRMEM (unsigned A,byte V) 
{ 
 (*WriteMemFnTable[A>>8])(A,V); 
} 
byte M_RDMEM(unsigned A) 
{ 
 return (Page[A>>13][A&0x1FFF]); 
} 
byte M_RDMEM_OPCODE (void) 
{ 
 byte V; 
 V=M_RDMEM (R.PC.D); 
 R.PC.W++; 
 return V; 
} 
 
void ResetGB (void) 
{ 
  int I; 
  if(RAMMap[0]) Page[5]=RAMMap[0]; 
  if(ROMMap[1]) Page[2]=ROMMap[1]; 
  Page[3]=Page[2]+0x2000; 
 
  TStep=32768;TCount=0; 
 
  ChrGen=RAM+0x8800;BgdTab=WndTab=RAM+0x9800; 
  LCDCONT=0x81;LCDSTAT=0x00; 
  CURLINE=0x00;CMPLINE=0xFF; 
  IFLAGS=ISWITCH=0x00; 
  TIMECNT=TIMEMOD=0x01; 
  TIMEFRQ=0xF8; 
  SIODATA=0x00; 
  SIOCONT=0x7E; 
  SIOBuf=SIOFlag=0x00; 
  JoyState=0xFF; 
 
  for(I=0;I<4;I++) SPal0[I]=SPal1[I]=BPal[I]=I; 
  BGRDPAL=SPR0PAL=SPR1PAL=0xE4; 
 
  /* Initialise the timer */ 
  M_WRMEM (0xFF07,RAM[0xFF07]); 
  ResetZ80(&R); 
} 
 
static int InitMachineDone=0; 
static word Exit_PC; 
int StartGB(char *CartName) 
{ 
  static char *CartTypes[] = 
  { 
    "ROM ONLY","ROM+MBC1","ROM+MBC1+RAM", 
    "ROM+MBC1+RAM+BATTERY","UNKNOWN", 
    "ROM+MBC2","ROM+MBC2+BATTERY" 
  }; 
 
  /*** Following are some known manufacturer codes *************************/ 
  static struct { word Code;char *Name; } Companies[] = 
  { 
  { 0x3301,"Nintendo"  },{ 0x7901,"Accolade"  },{ 0xA400,"Konami"    }, 
  { 0x6701,"Ocean"     },{ 0x5601,"LJN"       },{ 0x9900,"ARC?"      }, 
  { 0x0101,"Nintendo"  },{ 0x0801,"Capcom"    },{ 0x0100,"Nintendo"  }, 
  { 0xBB01,"SunSoft"   },{ 0xA401,"Konami"    },{ 0xAF01,"Namcot?"   }, 
  { 0x4901,"Irem"      },{ 0x9C01,"Imagineer" },{ 0xA600,"Kawada?"   }, 
  { 0xB101,"Nexoft"    },{ 0x5101,"Acclaim"   },{ 0x6001,"Titus"     }, 
  { 0xB601,"HAL"       },{ 0x3300,"Nintendo"  },{ 0x0B00,"Coconuts?" }, 
  { 0x5401,"Gametek"   },{ 0x7F01,"Kemco?"    },{ 0xC001,"Taito"     }, 
  { 0xEB01,"Atlus"     },{ 0xE800,"Asmik?"    },{ 0xDA00,"Tomy?"     }, 
  { 0xB100,"ASCII?"    },{ 0xEB00,"Atlus"     },{ 0xC000,"Taito"     }, 
  { 0x9C00,"Imagineer" },{ 0xC201,"Kemco?"    },{ 0xD101,"Sofel?"    }, 
  { 0x6101,"Virgin"    },{ 0xBB00,"SunSoft"   },{ 0xCE01,"FCI?"      }, 
  { 0xB400,"Enix?"     },{ 0xBD01,"Imagesoft" },{ 0x0A01,"Jaleco?"   }, 
  { 0xDF00,"Altron?"   },{ 0xA700,"Takara?"   },{ 0xEE00,"IGS?"      }, 
  { 0x8300,"Lozc?"     },{ 0x5001,"Absolute?" },{ 0xDD00,"NCS?"      }, 
  { 0xE500,"Epoch?"    },{ 0xCB00,"VAP?"      },{ 0x8C00,"Vic Tokai" }, 
  { 0xC200,"Kemco?"    },{ 0xBF00,"Sammy?"    }, 
  { 0x1800,"Hudson Soft"    },{ 0xCA01,"Palcom/Ultra" }, 
  { 0xCA00,"Palcom/Ultra"   },{ 0xC500,"Data East?" }, 
  { 0xA900,"Technos Japan?" },{ 0xD900,"Banpresto?" }, 
  { 0x7201,"Broderbund?"    },{ 0x7A01,"Triffix Entertainment?" }, 
  { 0xE100,"Towachiki?"     },{ 0x9300,"Tsuburava?" }, 
  { 0xC600,"Tonkin House?"  },{ 0xCE00,"Pony Canyon" }, 
  { 0x7001,"Infogrames?"    },{ 0x8B01,"Bullet-Proof Software?" }, 
  { 0x5501,"Park Place?"    },{ 0xEA00,"King Records?" }, 
  { 0x5D01,"Tradewest?"     },{ 0x6F01,"ElectroBrain?" }, 
  { 0xAA01,"Broderbund?"    },{ 0xC301,"SquareSoft" }, 
  { 0x5201,"Activision?"    },{ 0x5A01,"Bitmap Brothers/Mindscape" }, 
  { 0x5301,"American Sammy" },{ 0x4701,"Spectrum Holobyte" }, 
  { 0x1801,"Hudson Soft"},{ 0x0000,NULL } 
  }; 
 
  int Checksum,I,J,*T; 
  FILE *F; 
  char *P,S[50]; 
  word A; 
  int rambanks[4]= { 0,1,1,4 }; 
 
  /*** STARTUP CODE starts here: ***/ 
  T=(int *)"\01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 
#ifdef LSB_FIRST 
  if(*T!=1) 
  { 
    puts("********** This machine is high-endian. *********"); 
    puts("Take #define LSB_FIRST out and compile VGB again."); 
    return(0); 
  } 
#else 
  if(*T==1) 
  { 
    puts("********* This machine is low-endian. *********"); 
    puts("Insert #define LSB_FIRST and compile VGB again."); 
    return(0); 
  } 
#endif 
 
  for(I=0;I<256;I++) RAMMap[I]=ROMMap[I]=NULL; 
 
  if(Verbose) printf("Allocating 64kB for address space..."); 
  if(!(RAM=malloc(0x10000))) 
  { if(Verbose) puts("FAILED");return(0); } 
 
  memset(RAM,NORAM,0x10000); 
  for(I=0;I<8;I++) Page[I]=RAM+I*0x2000; 
 
  if(Verbose) printf("OK\nOpening %s...",CartName); 
  if(!(F=fopen(CartName,"rb"))) 
  { if(Verbose) puts("FAILED");return(0); } 
 
  if(Verbose) printf("reading..."); 
  if(fread(RAM,1,0x4000,F)!=0x4000) 
  { if(Verbose) puts("FAILED");return(0); } 
 
  ROMMap[0]=RAM; 
  ROMBanks=2<3; 
 
  P=NULL; 
  if((RAM[0x0147]==4)||(RAM[0x0147]>6)) P="Unknown ROM type"; 
  if(P) 
  { 
    printf("\nError loading cartridge: %s\n",P); 
    fclose(F);return(0); 
  } 
 
  if(Verbose) 
  { 
    strncpy(S,RAM+0x0134,16);S[16]='\0'; 
    printf("OK\n  Name: %s\n",S); 
    printf("  Type: %s\n",CartTypes[RAM[0x0147]]); 
    printf("  ROM Size: %dx16kB\n",ROMBanks); 
    J=(RAM[0x0149]&0x03)*2;J=J? (1<<(J-1)):0; 
    printf("  RAM Size: %dkB\n",J); 
 
    J=((word)RAM[0x014B]<<8)+RAM[0x014A]; 
    for(I=0,P=NULL;!P&&Companies[I].Name;I++) 
      if(J==Companies[I].Code) P=Companies[I].Name; 
    printf("  Manufacturer ID: %Xh",J); 
    printf(" [%s]\n",P? P:"?"); 
 
    printf("  Version Number: %Xh\n",RAM[0x014C]); 
    printf("  Complement Check: %Xh\n",RAM[0x014D]); 
    printf("  Checksum: %Xh\n",Checksum); 
    J=((word)RAM[0x0103]<<8)+RAM[0x0102]; 
    printf("  Start Address: %Xh\n",J); 
  } 
 
  Checksum+=RAM[0x014E]+RAM[0x014F]; 
  for(I=0;I<0x4000;I++) Checksum-=RAM[I]; 
 
  if(Verbose) printf("Loading %dx16kB ROM banks:\n.",ROMBanks); 
  for(I=1;I0) 
  { 
    if(Verbose) puts("Patching cheats into the ROM code:"); 
    for(J=0;J %Xh\n",A,Cheats[J].Orig,Cheats[J].Value); 
      if(A<0x4000) 
      { if(ROMMap[0][A]==Cheats[J].Orig) ROMMap[0][A]=Cheats[J].Value; } 
      else 
        for(I=0,A-=0x4000;I>8,SndStream); 
     fputc (I>>16,SndStream); 
     fputc (I>>24,SndStream); 
     for (I=4;I<64;++I) 
      fputc (0,SndStream); 
    } 
  } 
 
  if(ROMBanks<3) ROMMask=0; 
  else { for(I=1;I>16); 
        TCount&=0x0000FFFF; 
        if(L&0xFFFFFF00) 
        { 
          TIMECNT=TIMEMOD; 
          if(ISWITCH&TIM_IFLAG) 
          { IFLAGS=IMask=TIM_IFLAG;return 0x0050; } 
        } 
        else TIMECNT=L; 
       } 
       return 0xFFFF; 
      } 
    default: 
      /* Only LCD state had to be changed */ 
      return(0xFFFF); 
  } 
 
  /* Checking for line coincidence and refreshing screen */ 
  J=0; 
  if(LineDelay) 
  { 
    if(dorefresh&&(CURLINE<144)) RefreshLine(CURLINE); 
    CURLINE=(CURLINE+1)%154; 
    if(CURLINE!=CMPLINE) LCDSTAT&=0xFB; 
    else { LCDSTAT|=0x04;J|=0x40; } 
  } 
  else 
  { 
    if(CURLINE!=CMPLINE) LCDSTAT&=0xFB; 
    else { LCDSTAT|=0x04;J|=0x40; } 
    CURLINE=(CURLINE+1)%154; 
    if(dorefresh&&(CURLINE<144)) RefreshLine(CURLINE); 
  } 
 
  /* If end of frame reached... */ 
  if(CURLINE==144) 
  { 
    /* Set VBlank state */ 
    LCDSTAT=(LCDSTAT&0xFC)|0x01;J|=0x10; 
 
    /* Update joystick */ 
    JoyState=Joystick(); 
 
    /* Autofire emulation */ 
    ACount=(ACount+1)&0x07; 
    if(ACount>3) 
    { 
      if(AutoA) JoyState|=0x10; 
      if(AutoB) JoyState|=0x20; 
    } 
 
    /* Assign value to JOYPAD */ 
    I=JOYPAD|0xCF; 
    if(!(I&0x10)) JOYPAD=I&(JoyState|0xF0); 
    if(!(I&0x20)) JOYPAD=I&((JoyState>>4)|0xF0); 
 
    /* Refresh screen if needed */ 
    if (dorefresh) 
      RefreshScreen(); 
 
    /* Sync the GB emulation */ 
    switch (MachineInterrupt ()) 
    { 
     case 0:  if (UPeriod==0) 
              { 
               dorefresh=0; 
               break; 
              } 
     case 1:  if (UPeriod==0) 
              { 
               dorefresh=1; 
               break; 
              } 
     default: if (!--UCount) 
              { 
               UCount=UPeriod; 
               dorefresh=1; 
              } 
              else 
               dorefresh=0; 
              break; 
    } 
  } 
 
  /* Generating LCD controller interrupt */ 
  if((J&LCDSTAT)&&(ISWITCH&LCD_IFLAG)&&(LCDCONT&0x80)) IFLAGS|=LCD_IFLAG; 
 
  /* Determining interrupt address */ 
  if(IFLAGS&EXT_IFLAG) { IMask=EXT_IFLAG;return(0x0060); } 
  if(IFLAGS&SIO_IFLAG) { IMask=SIO_IFLAG;return(0x0058); } 
  if(IFLAGS&TIM_IFLAG) { IMask=TIM_IFLAG;return(0x0050); } 
  if(IFLAGS&LCD_IFLAG) { IMask=LCD_IFLAG;return(0x0048); } 
  if(IFLAGS&VBL_IFLAG) { IMask=VBL_IFLAG;return(0x0040); } 
 
  /* No interrupt */ 
  return(0xFFFF); 
}  
 
int AddCheat(char *Cheat) 
{ 
  static char Digits[]="0123456789ABCDEF"; 
  int X1,X2; 
 
  if(CheatCount>=MAXCHEAT) return(0); 
  else 
  { 
    if((Cheat[3]!='-')||(Cheat[7]!='-')) return(0); 
    X1=strchr(Digits,toupper(Cheat[0]))-Digits; 
    X2=strchr(Digits,toupper(Cheat[1]))-Digits; 
    if((X1<0)||(X2<0)) return(0); 
    else Cheats[CheatCount].Value=X1*16+X2; 
    X1=strchr(Digits,toupper(Cheat[4]))-Digits; 
    X2=strchr(Digits,toupper(Cheat[5]))-Digits; 
    if((X1<0)||(X2<0)) return(0); 
    else Cheats[CheatCount].Address=X1*16+X2; 
    X1=strchr(Digits,toupper(Cheat[6]))-Digits; 
    X2=strchr(Digits,toupper(Cheat[2]))-Digits; 
    if((X1<0)||(X2<0)) return(0); 
    else Cheats[CheatCount].Address+=256*((15-X1)*16+X2); 
    X1=strchr(Digits,toupper(Cheat[10]))-Digits; 
    X2=strchr(Digits,toupper(Cheat[8]))-Digits; 
    if((X1<0)||(X2<0)) return(0); 
    X1=~(16*X2+X1); 
    X1=((X1>>2)&0x3F)|((X1<<6)&0xC0); 
    Cheats[CheatCount].Orig=X1^0x45; 
 
    if(Cheats[CheatCount].Address>=0x8000) return(0); 
    CheatCount++;return(1); 
  } 
}