/** 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                 **/
/**     You are not allowed to distribute this software     **/
/**     commercially. Please, notify me, if you make any    **/   
/**     changes to this file.                               **/
/*************************************************************/

#include <stdio.h>

#include "GB.h"
#include "Z80_Defs.h"

/*** Registers ***********************************************/
/*** Z80 registers, RAM address, and running flag.         ***/
/*************************************************************/
reg R;
byte *Addr;
byte CPURunning;

byte I;
pair J;

/*** operations **********************************************/
/*** external tables of pointers to opcode functions       ***/
/*************************************************************/
extern void (*(JumpTab[]))(void);
extern void (*(JumpTabCB[]))(void);
extern short (*(JumpTabED[]))(void);


/*** Interrupts **********************************************/
/*** Interrupt-related variables.                          ***/
/*************************************************************/
#ifdef INTERRUPTS
word IPeriod = 10000; /* Number of cmds between int. intrpts */
byte IntSync = 1;     /* 1 to generate internal interrupts   */
byte IFlag = 0;       /* If IFlag==1, gen. int. and set to 0 */
word Cnt;             /* Variable used to count CPU cycles   */
#endif


/*** 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 */
word OldTrap = 0xFFFF;
#endif


/*** TrapBadOps **********************************************/
/*** When 1, print warnings of illegal Z80 instructions.   ***/
/*************************************************************/
byte TrapBadOps=0;


#ifdef LAME
#include "Lame.h"
#else
word Z80(byte *Addr, reg Regs)
{
byte opcode;

  R=Regs;

#ifdef INTERRUPTS
  Cnt=IPeriod;
#endif

  for(CPURunning=1;CPURunning;)
  {
    LCDSTAT=(LCDSTAT&0xFC)|((Cnt>>5)&0x03);

		opcode = *(Addr+(R.PC.W++));
    switch(opcode)
    {
      case PFX_CB:
//			printf("call JumpTabCB[%x]() @%x\n",opcode,R.PC.W-1);
			(*JumpTabCB[*(Addr+(R.PC.W++))])();
        break;

      case PFX_ED:
//			printf("call JumpTabED[%x]() @%x\n",opcode,R.PC.W-1);
			if((*JumpTabED[*(Addr+(R.PC.W++))])())	break;
			else if(TrapBadOps) printf("Unrecognized ED instruction at PC=%hX\n",R.PC.W-3);
         break;

      case 0x10:
      case HALT:
#ifdef INTERRUPTS
        if(Cnt) R.PC.W--; else R.IFF|=0x01;
#else
        printf("CPU HALTed and stuck at PC=%hX\n",--R.PC.W); 
        return(R.PC.W);
#endif
        break;
		default:
//			printf("call JumpTab[%x]() @%x\n",opcode,R.PC.W-1);
			(*JumpTab[opcode])();
			break;
    }

#ifdef INTERRUPTS
    /*** Here we generate NMI if Cnt reaches 0: ***/ 
    if(!Cnt--) 
    { 
      Cnt=IPeriod;
      if(IntSync) IFlag=1;
    }
    if(IFlag)
    {
      IFlag=0;J.W=Interrupt();
      if((J.W!=0xFFFF)&&(R.IFF&0x01))
      { M_PUSH(PC);R.PC.W=R.IFF&0x04? ((word)R.I<<8)+0xFF:J.W; }
    }
#endif

#ifdef DEBUG
    if(R.PC.W==Trap) Trace=1;  /*** Turn tracing on if trapped ***/
    if(Trace) Z80_Debug(Addr,R);   /*** Call single-step debugger  ***/
#endif

  }
  return(R.PC.W);
}
#endif
