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


/** VGB: portable GameBoy emulator ***************************/ 
/**                                                         **/ 
/**                           Z80.c                         **/ 
/**                                                         **/ 
/** This file contains implementation for the GameBoy CPU.  **/ 
/** See Z80.h for the relevant definitions. Please, note    **/ 
/** that this code can not be used to emulate a generic Z80 **/ 
/** because the GameBoy version of it differs from Z80 in   **/ 
/** many ways.                                              **/ 
/**                                                         **/ 
/** Copyright (C) Marat Fayzullin 1994,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  
#include  
#include "GB.h" 
#include "Tables.h" 
 
/*** Registers ***********************************************/ 
/*** Z80 registers and running flag.                       ***/ 
/*************************************************************/ 
reg R; 
byte CPURunning; 
 
/*** Interrupts **********************************************/ 
/*** Interrupt-related variables.                          ***/ 
/*************************************************************/ 
int  IPeriod = 69905; /* Number of cycles between int. int-s */ 
int  ICount;          /* Variable used to count CPU cycles   */ 
 
/*** Trace and Trap ******************************************/ 
/*** Switches to turn tracing on and off in DEBUG mode.    ***/ 
/*************************************************************/ 
#ifdef DEBUG 
byte Trace=0;       /* Tracing is on if Trace==1  */ 
word Trap=0xFFFF;   /* When PC==Trap, set Trace=1 */ 
#endif 
 
/*** TrapBadOps **********************************************/ 
/*** When 1, print warnings of illegal Z80 instructions.   ***/ 
/*************************************************************/ 
byte TrapBadOps=0; 
 
/* Tables to convert Z80 flags to GB flags and vise versa */ 
byte Z80toGB[256]; 
byte GBtoZ80[256]; 
 
#define S(Fl)        R.AF.B.l|=Fl 
#define R(Fl)        R.AF.B.l&=~(Fl) 
#define FLAGS(Rg,Fl) R.AF.B.l=Fl|ZTable[Rg]; 
 
#define M_RLC(Rg)      \ 
  R.AF.B.l=Rg>>7;Rg=(Rg<<1)|R.AF.B.l;R.AF.B.l|=ZTable[Rg] 
#define M_RRC(Rg)      \ 
  R.AF.B.l=Rg&0x01;Rg=(Rg>>1)|(R.AF.B.l<<7);R.AF.B.l|=ZTable[Rg] 
 
#define M_RL(Rg)       \ 
  if(Rg&0x80)          \ 
  {                    \ 
    Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \ 
    R.AF.B.l=ZTable[Rg]|C_FLAG;  \ 
  }                    \ 
  else                 \ 
  {                    \ 
    Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \ 
    R.AF.B.l=ZTable[Rg];         \ 
  } 
#define M_RR(Rg)       \ 
  if(Rg&0x01)          \ 
  {                    \ 
    Rg=(Rg>>1)|(R.AF.B.l<<7);    \ 
    R.AF.B.l=ZTable[Rg]|C_FLAG; \ 
  }                    \ 
  else                 \ 
  {                    \ 
    Rg=(Rg>>1)|(R.AF.B.l<<7);    \ 
    R.AF.B.l=ZTable[Rg];        \ 
  } 
   
#define M_SLA(Rg)      \ 
  R.AF.B.l=Rg>>7;Rg<<=1;R.AF.B.l|=ZTable[Rg] 
#define M_SRA(Rg)      \ 
  R.AF.B.l=Rg&C_FLAG;Rg=(Rg>>1)|(Rg&0x80);R.AF.B.l|=ZTable[Rg] 
#define M_SLL(Rg)      \ 
  R.AF.B.l=Rg>>7;Rg=(Rg<<1)|0x01;R.AF.B.l|=ZTable[Rg] 
#define M_SRL(Rg)      \ 
  R.AF.B.l=Rg&0x01;Rg>>=1;R.AF.B.l|=ZTable[Rg] 
 
#define M_BIT(Bit,Rg)  \ 
  R.AF.B.l=(R.AF.B.l&~(N_FLAG|Z_FLAG))|H_FLAG|(Rg&(1<PC.W=0x0100;Regs->SP.W=0xF000; 
  Regs->AF.W=Regs->BC.W=Regs->DE.W=Regs->HL.W=0x0000; 
  Regs->I=0x00;Regs->IFF=0x00; 
} 
 
/*** Interpret Z80 code: **********************************/ 
/*** Registers have initial values from Regs. PC value  ***/ 
/*** at which emulation stopped is returned by this     ***/ 
/*** function.                                          ***/ 
/**********************************************************/ 
word Z80(reg Regs) 
{ 
  byte I; 
  pair J; 
  static int Z80_virgin=1; 
 
  if (Z80_virgin) 
  { 
   int i,j; 
   for (i=0;i<256;++i) 
   { 
    Cycles[i]<<=8; 
    CyclesCB[i]<<=8; 
    j=(i&0xA0)>>1;      /* Z and H flags */ 
    j|=(i&0x40)>>5;     /* N flag */ 
    j|=(i&0x10)>>4;     /* C flag */ 
    GBtoZ80[i]=j; 
    j=(i&0x50)<<1;      /* Z and H flags */ 
    j|=(i&0x02)<<5;     /* N flag */ 
    j|=(i&0x01)<<4;     /* C flag */ 
    Z80toGB[i]=j; 
   } 
   Z80_virgin=0; 
  } 
 
  R=Regs;ICount=IPeriod;CPURunning=1; 
 
  for(;;) 
  { 
#ifdef DEBUG 
    if(R.PC.W==Trap) Trace=1;  /*** Turn tracing on if trapped ***/ 
    if(Trace) Debug(&R);       /*** Call single-step debugger  ***/ 
#endif 
 
    I=M_RDMEM_OPCODE(); 
    ICount-=Cycles[I]; 
    switch(I) 
    { 
#include "Codes.h" 
      case PFX_CB: 
        CodesCB();break; 
      case 0x10: 
      case HALT: 
        R.PC.W--;R.IFF|=0x80;if (ICount>0) ICount=0; break; 
      default: 
        ICount-=(4<<8); 
        if(TrapBadOps) 
          printf 
          ( 
            "Unrecognized instruction: %X at PC=%hX\n", 
            M_RDMEM(R.PC.W-1),R.PC.W-1 
          ); 
    } 
  
    if(ICount<=0)  
    { 
      ICount+=IPeriod; 
      if(!CPURunning) break; 
      J.W=Interrupt(); 
      if((J.W!=0xFFFF)&&(R.IFF&0x81)) 
      { 
        if(R.IFF&0x80) R.PC.W++; 
        IFLAGS&=~IMask;IMask=0x00; 
        R.IFF&=0x7E; 
        M_PUSH(PC);R.PC.W=J.W; 
      } 
    }   
  } 
 
  return(R.PC.W); 
}