/** 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 **/ /** You are not allowed to distribute this software **/ /** commercially. Please, notify me, if you make any **/ /** changes to this file. **/ /*************************************************************/ #include #include "GB.h" #include "Tables.h" /*** Registers ***********************************************/ /*** Z80 registers and running flag. ***/ /*************************************************************/ static reg R; byte CPURunning; /*** Interrupts **********************************************/ /*** Interrupt-related variables. ***/ /*************************************************************/ int IPeriod = 69905; /* Number of cycles between int. int-s */ byte IntSync = 1; /* 1 to generate internal interrupts */ byte IFlag = 0; /* If IFlag==1, gen. int. and set to 0 */ 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; #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|ZSTable[Rg]; #define M_RLC(Rg) \ R.AF.B.l=Rg>>7;Rg=(Rg<<1)|R.AF.B.l;R.AF.B.l|=ZSTable[Rg] #define M_RRC(Rg) \ R.AF.B.l=Rg&0x01;Rg=(Rg>>1)|(R.AF.B.l<<7);R.AF.B.l|=ZSTable[Rg] #define M_RL(Rg) \ if(Rg&0x80) \ { \ Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \ R.AF.B.l=ZSTable[Rg]|C_FLAG; \ } \ else \ { \ Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \ R.AF.B.l=ZSTable[Rg]; \ } #define M_RR(Rg) \ if(Rg&0x01) \ { \ Rg=(Rg>>1)|(R.AF.B.l<<7); \ R.AF.B.l=ZSTable[Rg]|C_FLAG; \ } \ else \ { \ Rg=(Rg>>1)|(R.AF.B.l<<7); \ R.AF.B.l=ZSTable[Rg]; \ } #define M_SLA(Rg) \ R.AF.B.l=Rg>>7;Rg<<=1;R.AF.B.l|=ZSTable[Rg] #define M_SRA(Rg) \ R.AF.B.l=Rg&C_FLAG;Rg=(Rg>>1)|(Rg&0x80);R.AF.B.l|=ZSTable[Rg] #define M_SLL(Rg) \ R.AF.B.l=Rg>>7;Rg=(Rg<<1)|0x01;R.AF.B.l|=ZSTable[Rg] #define M_SRL(Rg) \ R.AF.B.l=Rg&0x01;Rg>>=1;R.AF.B.l|=ZSTable[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) { static byte Cycles[256] = { 4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4, 4,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4, 8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4, 8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 8, 8, 8, 8, 8, 8, 4, 8, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4, 8,12,12,12,12,16, 8,32, 8, 8,12, 0,12,12, 8,32, 8,12,12, 0,12,16, 8,32, 8, 8,12, 0,12, 0, 8,32, 12,12, 8, 0, 0,16, 8,32,16, 4,16, 0, 0, 0, 8,32, 12,12, 8, 4, 0,16, 8,32,12, 8,16, 4, 0, 0, 8,32 }; register byte I; register pair J; 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(R.PC.W++); ICount-=Cycles[I]; switch(I) { #include "Codes.h" case PFX_CB: CodesCB();break; case 0x10: case HALT: R.PC.W--;R.IFF|=0x80;break; default: ICount-=4; 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; if(IntSync) IFlag=1; } if(IFlag) { IFlag=0;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); }