/* z80 - Z80 Emulator Copyright (C) 2006 Ian Cowburn Some of the opcode routines are based on the Z80 emulation from YAZE, Copyright (c) 1995 Frank D. Cringle. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------- $Id$ Z80 */ #include #include "z80.h" #include "z80_private.h" static const char ident[]="$Id$"; /* ---------------------------------------- TABLES AND INIT */ static const unsigned char partab[256] = { 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, }; static Z80Byte PSZtable[512]; static Z80Byte SZtable[512]; static Z80Byte Ptable[512]; static Z80Byte Stable[512]; static Z80Byte Ztable[512]; static int HI; static int LO; /* ---------------------------------------- PRIVATE FUNCTIONS */ void Z80_InitialiseInternals(void) { Z80Word f; Z80Reg r; r.w=0x1234; if (r.b[0] == 0x12) { HI=0; LO=1; } else if (r.b[1] == 0x12) { HI=1; LO=0; } else { exit(1); } /* Initialise flag tables */ for(f=0;f<256;f++) { Z80Byte p,z,s; int b; p=0; for(b=0;b<8;b++) if (f&(1<AF.b[HI]+(Z80Word)VAL; cpu->AF.b[LO]=SZtable[w]; if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; if ((VAL^cpu->AF.b[HI]^0x80)&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; SETHIDDEN(w); cpu->AF.b[HI]=w; } static void ADC8(Z80 *cpu, Z80Byte VAL) { Z80Word w; w=(cpu->AF.b[HI]+(Z80Word)VAL+CARRY)&0x1ff; cpu->AF.b[LO]=SZtable[w]; if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; if ((VAL^cpu->AF.b[HI]^0x80)&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; SETHIDDEN(w); cpu->AF.b[HI]=w; } static void SUB8(Z80 *cpu, Z80Byte VAL) { Z80Word w; w=(cpu->AF.b[HI]-(Z80Word)VAL)&0x1ff; cpu->AF.b[LO]=SZtable[w]|N_Z80; if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; if ((VAL^cpu->AF.b[HI]^0x80)&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; SETHIDDEN(w); cpu->AF.b[HI]=w; } static void CMP8(Z80 *cpu, Z80Byte VAL) { Z80Word w; w=(cpu->AF.b[HI]-(Z80Word)VAL)&0x1ff; cpu->AF.b[LO]=SZtable[w]|N_Z80; if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; if ((VAL^cpu->AF.b[HI]^0x80)&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; SETHIDDEN(VAL); } static void SBC8(Z80 *cpu, Z80Byte VAL) { Z80Word w; w=(cpu->AF.b[HI]-(Z80Word)VAL-CARRY)&0x1ff; cpu->AF.b[LO]=SZtable[w]|N_Z80; if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; if ((VAL^cpu->AF.b[HI]^0x80)&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; SETHIDDEN(w); cpu->AF.b[HI]=w; } static Z80Word ADD16(Z80 *cpu, Z80Word REG, Z80Word VAL) { Z80Val w; w=REG+(Z80Val)VAL; cpu->AF.b[LO]&=(S_Z80|Z_Z80|V_Z80); if (w>0xffff) cpu->AF.b[LO]|=C_Z80; if ((REG^w^VAL)&0x1000) cpu->AF.b[LO]|=H_Z80; SETHIDDEN(w>>8); return w; } static Z80Word ADC16(Z80 *cpu, Z80Word REG, Z80Word VAL) { Z80Val w; w=REG+(Z80Val)VAL+CARRY; cpu->AF.b[LO]&=(S_Z80|Z_Z80|V_Z80); if (w>0xffff) cpu->AF.b[LO]|=C_Z80; if ((REG^w^VAL)&0x1000) cpu->AF.b[LO]|=H_Z80; SETHIDDEN(w>>8); return w; } static Z80Word SBC16(Z80 *cpu, Z80Word REG, Z80Word VAL) { Z80Val w; w=REG-(Z80Val)VAL-CARRY; cpu->AF.b[LO]=N_Z80; if (w&0x8000) cpu->AF.b[LO]|=S_Z80; if ((w&0xffff)==0) cpu->AF.b[LO]|=Z_Z80; if (w>0xffff) cpu->AF.b[LO]|=C_Z80; if ((REG^w^VAL)&0x1000) cpu->AF.b[LO]|=H_Z80; if ((VAL^REG)&(VAL^w)&0x8000) cpu->AF.b[LO]|=P_Z80; SETHIDDEN(w>>8); return w; } static Z80Byte INC8(Z80 *cpu, Z80Byte REG) { REG++; cpu->AF.b[LO]=CARRY|SZtable[REG]; if (REG&0x80) cpu->AF.b[LO]|=P_Z80; if (REG&0x0f) cpu->AF.b[LO]|=H_Z80; return REG; } static Z80Byte DEC8(Z80 *cpu, Z80Byte REG) { cpu->AF.b[LO]=N_Z80|CARRY; if (REG&0x80) cpu->AF.b[LO]|=P_Z80; if (REG&0x0f) cpu->AF.b[LO]|=H_Z80; REG--; cpu->AF.b[LO]|=SZtable[REG]; return REG; } /* ---------------------------------------- ROTATE AND SHIFT OPS */ static void RRCA(Z80 *cpu) { cpu->AF.b[LO]=(cpu->AF.b[LO]&0xec)|(cpu->AF.b[HI]&C_Z80); cpu->AF.b[HI]=(cpu->AF.b[HI]>>1)|(cpu->AF.b[HI]<<7); SETHIDDEN(cpu->AF.b[HI]); } static void RRA(Z80 *cpu) { Z80Byte c; c=CARRY; cpu->AF.b[LO]=(cpu->AF.b[LO]&0xec)|(cpu->AF.b[HI]&C_Z80); cpu->AF.b[HI]=(cpu->AF.b[HI]>>1)|(c<<7); SETHIDDEN(cpu->AF.b[HI]); } static Z80Byte RRC(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG&C_Z80; REG=(REG>>1)|(REG<<7); cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static Z80Byte RR(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG&C_Z80; REG=(REG>>1)|(CARRY<<7); cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static void RLCA(Z80 *cpu) { cpu->AF.b[LO]=(cpu->AF.b[LO]&0xec)|(cpu->AF.b[HI]>>7); cpu->AF.b[HI]=(cpu->AF.b[HI]<<1)|(cpu->AF.b[HI]>>7); SETHIDDEN(cpu->AF.b[HI]); } static void RLA(Z80 *cpu) { Z80Byte c; c=CARRY; cpu->AF.b[LO]=(cpu->AF.b[LO]&0xec)|(cpu->AF.b[HI]>>7); cpu->AF.b[HI]=(cpu->AF.b[HI]<<1)|c; SETHIDDEN(cpu->AF.b[HI]); } static Z80Byte RLC(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG>>7; REG=(REG<<1)|c; cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static Z80Byte RL(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG>>7; REG=(REG<<1)|CARRY; cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static Z80Byte SRL(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG&C_Z80; REG>>=1; cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static Z80Byte SRA(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG&C_Z80; REG=(REG>>1)|(REG&0x80); cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static Z80Byte SLL(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG>>7; REG=(REG<<1)|1; cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } static Z80Byte SLA(Z80 *cpu, Z80Byte REG) { Z80Byte c; c=REG>>7; REG=REG<<1; cpu->AF.b[LO]=PSZtable[REG]|c; SETHIDDEN(REG); return REG; } /* ---------------------------------------- BOOLEAN OPS */ static void AND(Z80 *cpu, Z80Byte VAL) { cpu->AF.b[HI]&=VAL; cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]|H_Z80; SETHIDDEN(cpu->AF.b[HI]); } static void OR(Z80 *cpu, Z80Byte VAL) { cpu->AF.b[HI]|=VAL; cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]; SETHIDDEN(cpu->AF.b[HI]); } static void XOR(Z80* cpu, Z80Byte VAL) { cpu->AF.b[HI]^=VAL; cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]; SETHIDDEN(cpu->AF.b[HI]); } static void BIT(Z80 *cpu, Z80Byte REG, Z80Byte B) { cpu->AF.b[LO]=CARRY|H_Z80; if (REG&&(1<AF.b[LO]|=S_Z80; if (B==5) cpu->AF.b[LO]|=B5_Z80; if (B==3) cpu->AF.b[LO]|=B3_Z80; } else { cpu->AF.b[LO]|=Z_Z80; } } /* ---------------------------------------- BLOCK OPERATIONS */ static void LDI(Z80 *cpu) { Z80Byte b; b=PEEK(cpu->HL.w); POKE(cpu->DE.w,b); cpu->DE.w++; cpu->HL.w++; cpu->BC.w--; CLRFLAG(H_Z80); CLRFLAG(N_Z80); if (cpu->BC.w) SETFLAG(P_Z80); else CLRFLAG(P_Z80); SETHIDDEN(cpu->AF.b[HI]+b); } static void LDD(Z80 *cpu) { Z80Byte b; b=PEEK(cpu->HL.w); POKE(cpu->DE.w,b); cpu->DE.w--; cpu->HL.w--; cpu->BC.w--; CLRFLAG(H_Z80); CLRFLAG(N_Z80); if (cpu->BC.w) SETFLAG(P_Z80); else CLRFLAG(P_Z80); SETHIDDEN(cpu->AF.b[HI]+b); } static void CPI(Z80 *cpu) { Z80Byte c,b; c=CARRY; b=PEEK(cpu->HL.w); CMP8(cpu,b); if (c) SETFLAG(C_Z80); else CLRFLAG(C_Z80); cpu->HL.w++; cpu->BC.w--; if (cpu->BC.w) SETFLAG(P_Z80); else CLRFLAG(P_Z80); } static void CPD(Z80 *cpu) { Z80Byte c,b; c=CARRY; b=PEEK(cpu->HL.w); CMP8(cpu,b); if (c) SETFLAG(C_Z80); else CLRFLAG(C_Z80); cpu->HL.w--; cpu->BC.w--; if (cpu->BC.w) SETFLAG(P_Z80); else CLRFLAG(P_Z80); } static void INI(Z80 *cpu) { Z80Word w; Z80Byte b; b=IN(cpu->BC.w); POKE(cpu->HL.w,b); cpu->BC.b[HI]--; cpu->HL.w++; cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; SETHIDDEN(cpu->BC.b[HI]); w=(((Z80Word)cpu->BC.b[LO])&0xff)+b; if (b&0x80) SETFLAG(N_Z80); if (w&0x100) { SETFLAG(C_Z80); SETFLAG(H_Z80); } else { CLRFLAG(C_Z80); CLRFLAG(H_Z80); } } static void IND(Z80 *cpu) { Z80Word w; Z80Byte b; b=IN(cpu->BC.w); POKE(cpu->HL.w,b); cpu->BC.b[HI]--; cpu->HL.w--; cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; SETHIDDEN(cpu->BC.b[HI]); w=(((Z80Word)cpu->BC.b[LO])&0xff)+b; if (b&0x80) SETFLAG(N_Z80); if (w&0x100) { SETFLAG(C_Z80); SETFLAG(H_Z80); } else { CLRFLAG(C_Z80); CLRFLAG(H_Z80); } } static void OUTI(Z80 *cpu) { OUT(cpu->BC.w,PEEK(cpu->HL.w)); cpu->HL.w++; cpu->BC.b[HI]--; cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; SETHIDDEN(cpu->BC.b[HI]); } static void OUTD(Z80 *cpu) { OUT(cpu->BC.w,PEEK(cpu->HL.w)); cpu->HL.w--; cpu->BC.b[HI]--; cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; SETFLAG(N_Z80); SETHIDDEN(cpu->BC.b[HI]); } /* ---------------------------------------- GENERAL MACROS */ /* Decides whether the offset for IX/IY should be fetched, or used from the store for a CB instruction */ #define GETREL (cpu->use_cb_off ? cpu->cb_off : (Z80Relative)FETCH_BYTE) /* ---------------------------------------- DAA */ /* This code based on the DAA opcode from YAZE, (c) Frank D. Cringle */ static void DAA (Z80 *cpu) { Z80Word AF,acu,temp,cbits; AF=cpu->AF.w; acu=cpu->AF.b[HI]; temp=acu&0xf; cbits=CARRY; if (IS_N) /* last operation was a subtract */ { int hd = cbits || acu > 0x99; if (IS_H || (temp > 9)) /* adjust low digit */ { if (temp > 5) { CLR(AF,H_Z80); } acu -= 6; acu &= 0xff; } if (hd) /* adjust high digit */ acu -= 0x160; } else /* last operation was an add */ { if (IS_H || (temp > 9)) /* adjust low digit */ { if (temp>9) { SET(AF,H_Z80); } else { CLR(AF,H_Z80); } acu += 6; } if (cbits || ((acu & 0x1f0) > 0x90)) /* adjust high digit */ acu += 0x60; } cbits |= (acu >> 8) & 1; acu &= 0xff; AF = (acu << 8) | (acu & 0xa8) | ((acu == 0) << 6) | (AF & 0x12) | partab[acu] | cbits; cpu->AF.w=AF; SETHIDDEN(cpu->AF.b[HI]); } /* ---------------------------------------- DECODE AND REGISTER SELECTION */ /* Get the 16 bit register to use according to the bitmask */ static Z80Word *Get16(Z80 *cpu, Z80Byte bm) { if (bm==0x00) return &cpu->BC.w; if (bm==0x01) return &cpu->DE.w; if (bm==0x02) { if (cpu->shift==0xdd) return &cpu->IX.w; if (cpu->shift==0xfd) return &cpu->IY.w; return &cpu->HL.w; } return &cpu->SP; } /* Get the 16 bit register to use according to the bitmask, ignoring the shift */ static Z80Word *Get16_HL(Z80 *cpu, Z80Byte bm) { if (bm==0x00) return &cpu->BC.w; if (bm==0x01) return &cpu->DE.w; if (bm==0x02) return &cpu->HL.w; return &cpu->SP; } /* Get the 16 bit register to use according to the bitmask, with AF instead of SP */ static Z80Word *Get16_AF(Z80 *cpu, Z80Byte bm) { if (bm==0x00) return &cpu->BC.w; if (bm==0x01) return &cpu->DE.w; if (bm==0x02) { if (cpu->shift==0xdd) return &cpu->IX.w; if (cpu->shift==0xfd) return &cpu->IY.w; return &cpu->HL.w; } return &cpu->AF.w; } /* Get the 8 bit register to use according to the bitmask . Returns NULL for (HL), (I[XY]+d) */ static Z80Byte *Get8(Z80 *cpu, Z80Byte bm) { if (bm==0x07) return &cpu->AF.b[HI]; if (bm==0x00) return &cpu->BC.b[HI]; if (bm==0x01) return &cpu->BC.b[LO]; if (bm==0x02) return &cpu->DE.b[HI]; if (bm==0x03) return &cpu->DE.b[LO]; if (bm==0x04) { if (cpu->shift==0xdd) return &cpu->IX.b[HI]; if (cpu->shift==0xfd) return &cpu->IY.b[HI]; return &cpu->HL.b[HI]; } if (bm==0x05) { if (cpu->shift==0xdd) return &cpu->IX.b[LO]; if (cpu->shift==0xfd) return &cpu->IY.b[LO]; return &cpu->HL.b[LO]; } return NULL; } /* Get the 8 bit register to use according to the bitmask, ignoring the shift. Returns NULL for (HL) */ static Z80Byte *Get8_HL(Z80 *cpu, Z80Byte bm) { if (bm==0x07) return &cpu->AF.b[HI]; if (bm==0x00) return &cpu->BC.b[HI]; if (bm==0x01) return &cpu->BC.b[LO]; if (bm==0x02) return &cpu->DE.b[HI]; if (bm==0x03) return &cpu->DE.b[LO]; if (bm==0x04) return &cpu->HL.b[HI]; if (bm==0x05) return &cpu->HL.b[LO]; return NULL; } /* Get the address pointed to by HL, or the relative one depending on the shift */ static Z80Word GetHL(Z80 *cpu) { if (cpu->shift==0) return cpu->HL.w; if (cpu->shift==0xdd) { TSTATE(8); return cpu->IX.w+GETREL; } if (cpu->shift==0xfd) { TSTATE(8); return cpu->IY.w+GETREL; } return 0; } /* Get the value of HL, IX, IY depending on the shift */ static Z80Word GetHLVal(Z80 *cpu) { if (cpu->shift==0) return cpu->HL.w; if (cpu->shift==0xdd) return cpu->IX.w; if (cpu->shift==0xfd) return cpu->IY.w; return 0; } /* Get the register (HL, IX, IY) depending on the shift */ static Z80Word* GetHLReg(Z80 *cpu) { if (cpu->shift==0) return &cpu->HL.w; if (cpu->shift==0xdd) return &cpu->IX.w; if (cpu->shift==0xfd) return &cpu->IY.w; return 0; } /* Get a condition depending on the bitmask */ static Z80Byte GetCond(Z80 *cpu, Z80Byte bm) { if (bm==0x00) /* NZ */ return !IS_Z; if (bm==0x01) /* Z */ return IS_Z; if (bm==0x02) /* NC */ return !IS_C; if (bm==0x03) /* C */ return IS_C; if (bm==0x04) /* PO */ return !IS_P; if (bm==0x05) /* PE */ return IS_P; if (bm==0x06) /* P */ return !IS_S; if (bm==0x07) /* M */ return IS_S; return 0; } /* ---------------------------------------- HANDLERS FOR ED OPCODES */ static void EDOp_01(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { if (bml==0x00) /* IN r,(C) */ { Z80Byte *reg; Z80Byte v; TSTATE(12); reg=Get8_HL(cpu,bmm); v=IN(cpu->BC.w); cpu->AF.b[LO]=(cpu->AF.b[LO]&C_Z80)|PSZtable[v]; SETHIDDEN(v); if (reg) *reg=v; return; } if (bml==0x01) /* OUT r,(C) */ { Z80Byte *reg; TSTATE(12); reg=Get8_HL(cpu,bmm); OUT(cpu->BC.w,reg ? *reg:0); return; } if (bml==0x02) { Z80Word *reg; TSTATE(15); reg=Get16_HL(cpu,bmm>>1); if (bmm&1) /* ADC HL,rr */ { cpu->HL.w=ADC16(cpu,cpu->HL.w,*reg); } else /* SBC HL,rr */ { cpu->HL.w=SBC16(cpu,cpu->HL.w,*reg); } return; } if (bml==0x03) { Z80Word a; Z80Word *reg; TSTATE(20); a=FETCH_WORD; reg=Get16_HL(cpu,bmm>>1); if (bmm&1) /* LD rr,(nn) */ { *reg=PEEKW(a); } else /* LD (nn),rr */ { POKEW(a,*reg); } return; } if (bml==0x04) /* NEG */ { Z80Byte v; TSTATE(8); v=cpu->AF.b[HI]; cpu->AF.b[HI]=0; SUB8(cpu,v); return; } if (bml==0x05) { TSTATE(14); if (bmm&1) /* RETI */ { POP(cpu->PC); CALLBACK(eZ80_RETI,0); } else /* RETN */ { cpu->IFF1=cpu->IFF2; POP(cpu->PC); } return; } if (bml==0x06) /* IM n */ { TSTATE(8); switch(bmm&0x3) { case 0x00: case 0x01: cpu->IM=0; break; case 0x02: cpu->IM=1; break; case 0x03: cpu->IM=2; break; } return; } if (bml==0x07) { Z80Byte b; switch(bmm) { case 0x00: /* LD I,A */ TSTATE(9); cpu->I=cpu->AF.b[HI]; break; case 0x01: /* LD R,A */ TSTATE(9); cpu->R=cpu->AF.b[HI]; break; case 0x02: /* LD A,I */ TSTATE(9); cpu->AF.b[HI]=cpu->I; break; case 0x03: /* LD A,R */ TSTATE(9); cpu->AF.b[HI]=cpu->R; break; case 0x04: /* RRD */ TSTATE(18); b=PEEK(cpu->HL.w); POKE(cpu->HL.w,(b>>4)|(cpu->AF.b[HI]<<4)); cpu->AF.b[HI]=(cpu->AF.b[HI]&0xF0)|(b&0x0f); cpu->AF.b[LO]=(cpu->AF.b[LO]&C_Z80)|PSZtable[cpu->AF.b[HI]]; SETHIDDEN(cpu->AF.b[HI]); break; case 0x05: /* RLD */ TSTATE(18); b=PEEK(cpu->HL.w); POKE(cpu->HL.w,(b<<4)|(cpu->AF.b[HI]&0x0f)); cpu->AF.b[HI]=(cpu->AF.b[HI]&0xF0)|(b>>4); cpu->AF.b[LO]=(cpu->AF.b[LO]&C_Z80)|PSZtable[cpu->AF.b[HI]]; SETHIDDEN(cpu->AF.b[HI]); break; case 0x06: /* NOP */ case 0x07: TSTATE(8); break; } return; } } static void EDOp_10(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { if (bml==0x00) { switch(bmm) { case 0x00: /* NOP */ case 0x01: case 0x02: case 0x03: TSTATE(8); CALLBACK(eZ80_EDHook,opcode); break; case 0x04: /* LDI */ TSTATE(16); LDI(cpu); break; case 0x05: /* LDD */ TSTATE(16); LDD(cpu); break; case 0x06: /* LDIR */ TSTATE(16); LDI(cpu); if(cpu->BC.w) { TSTATE(5); cpu->PC-=2; } break; case 0x07: /* LDDR */ TSTATE(16); LDD(cpu); if(cpu->BC.w) { TSTATE(5); cpu->PC-=2; } break; } } if (bml==0x01) { switch(bmm) { case 0x00: /* NOP */ case 0x01: case 0x02: case 0x03: TSTATE(8); CALLBACK(eZ80_EDHook,opcode); break; case 0x04: /* CPI */ TSTATE(16); CPI(cpu); break; case 0x05: /* CPD */ TSTATE(16); CPD(cpu); break; case 0x06: /* CPIR */ TSTATE(16); CPI(cpu); if(cpu->BC.w && !IS_Z) { TSTATE(5); cpu->PC-=2; } break; case 0x07: /* CPDR */ TSTATE(16); CPD(cpu); if(cpu->BC.w && !IS_Z) { TSTATE(5); cpu->PC-=2; } break; } } if (bml==0x02) { switch(bmm) { case 0x00: /* NOP */ case 0x01: case 0x02: case 0x03: TSTATE(8); CALLBACK(eZ80_EDHook,opcode); break; case 0x04: /* INI */ TSTATE(16); INI(cpu); break; case 0x05: /* IND */ TSTATE(16); IND(cpu); break; case 0x06: /* INIR */ TSTATE(16); INI(cpu); if(cpu->BC.b[HI]) { TSTATE(5); cpu->PC-=2; } break; case 0x07: /* INDR */ TSTATE(16); IND(cpu); if(cpu->BC.b[HI]) { TSTATE(5); cpu->PC-=2; } break; } } if (bml==0x03) { switch(bmm) { case 0x00: /* NOP */ case 0x01: case 0x02: case 0x03: TSTATE(8); CALLBACK(eZ80_EDHook,opcode); break; case 0x04: /* OUTI */ TSTATE(16); OUTI(cpu); break; case 0x05: /* OUTD */ TSTATE(16); OUTD(cpu); break; case 0x06: /* OTIR */ TSTATE(16); OUTI(cpu); if(cpu->BC.b[HI]) { TSTATE(5); cpu->PC-=2; } break; case 0x07: /* OTDR */ TSTATE(16); OUTD(cpu); if(cpu->BC.b[HI]) { TSTATE(5); cpu->PC-=2; } break; } } /* Everything else is a NOP */ CALLBACK(eZ80_EDHook,opcode); } static void DecodeED(Z80 *cpu, Z80Byte opcode) { Z80Byte bmh,bmm,bml; /* Split up the opcode into it's bitmasks */ bmh=(opcode>>6)&3; bmm=(opcode>>3)&7; bml=opcode&7; /* Instruction set 01 */ if (bmh==0x01) { EDOp_01(cpu,opcode,bmh,bmm,bml); return; } /* Instruction set 10 */ if (bmh==0x02) { EDOp_10(cpu,opcode,bmh,bmm,bml); return; } /* All the rest are NOP/invalid */ CALLBACK(eZ80_EDHook,opcode); } /* ---------------------------------------- HANDLERS FOR CB OPCODES */ static void CBOp_00(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { static Z80Byte (*op[8])(Z80 *cpu, Z80Byte val)= { RLC, RRC, RL, RR, SLA, SRA, SLL, SRL }; Z80Byte *reg; reg=Get8(cpu,bml); /* If the reg is NULL, this is an (HL) type arg. Also, if this is a shifted op then the argument is taken from (HL) and also copied to the register. */ if (cpu->shift || !reg) { Z80Word a; TSTATE(15); a=GetHL(cpu); if (cpu->shift && reg) { *reg=op[bmm](cpu,PEEK(a)); POKE(a,*reg); } else POKE(a,op[bmm](cpu,PEEK(a))); } else { TSTATE(8); *reg=op[bmm](cpu,*reg); } } static void CBOp_01(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { Z80Byte *reg; reg=Get8(cpu,bml); /* If the reg is NULL, this is an (HL) type arg. Also, if this is a shifted op. */ if (cpu->shift || !reg) { Z80Word a; TSTATE(12); a=GetHL(cpu); BIT(cpu,PEEK(a),bmm); } else { TSTATE(8); BIT(cpu,*reg,bmm); } } static void CBOp_ResSet(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { Z80Byte *reg; int reset; reg=Get8(cpu,bml); reset=(bmh==0x02); /* If the reg is NULL, this is an (HL) type arg. Also, if this is a shifted op then the argument is taken from (HL) and also copied to the register. */ if (cpu->shift || !reg) { Z80Word a; TSTATE(15); a=GetHL(cpu); if (reset) { CLR(cpu->memory[a],bmm); } else { SET(cpu->memory[a],bmm); } if (cpu->shift && reg) { *reg=cpu->memory[a]; } } else { TSTATE(8); if (reset) { CLR(*reg,bmm); } else { SET(*reg,bmm); } } } static void DecodeCB(Z80 *cpu, Z80Byte opcode) { Z80Byte bmh,bmm,bml; /* Split up the opcode into it's bitmasks */ bmh=(opcode>>6)&3; bmm=(opcode>>3)&7; bml=opcode&7; /* Instruction set 00 */ if (bmh==0x00) { CBOp_00(cpu,opcode,bmh,bmm,bml); return; } /* Instruction set 01 (BIT) */ if (bmh==0x01) { CBOp_01(cpu,opcode,bmh,bmm,bml); return; } /* Instruction set 10 (RES) and 11 (SET) */ CBOp_ResSet(cpu,opcode,bmh,bmm,bml); } /* ---------------------------------------- HANDLERS FOR NORMAL OPCODES */ static void Normal_00(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { Z80Reg tmp; Z80Byte *bp=NULL; Z80Word w; if (bml==0x00) { switch(bmm) { case 0x00: /* NOP */ TSTATE(4); break; case 0x01: /* EX AF,AF' */ TSTATE(4); tmp=cpu->AF; cpu->AF=cpu->AF_; cpu->AF_=tmp; break; case 0x02: /* DJNZ */ if (--cpu->BC.b[HI]) { TSTATE(13); JR; } else { TSTATE(8); NOJR; } break; case 0x03: /* JR */ TSTATE(12); JR; break; case 0x04: /* JR cc */ case 0x05: case 0x06: case 0x07: if (GetCond(cpu,bmm&3)) { TSTATE(12); JR; } else { TSTATE(7); NOJR; } break; } return; } if (bml==0x01) { if(bmm&1) /* ADD HL,rr */ { TSTATE(11); *GetHLReg(cpu)=ADD16(cpu,*GetHLReg(cpu),*Get16(cpu,bmm>>1)); } else /* LD rr,nn */ { TSTATE(10); *Get16(cpu,bmm>>1)=FETCH_WORD; } return; } if (bml==0x02) { switch(bmm) { case 0x00: /* LD (rr),A */ case 0x02: TSTATE(7); POKE(*Get16(cpu,(bmm&2)>>1),cpu->AF.b[HI]); break; case 0x01: /* LD A,(rr) */ case 0x03: TSTATE(7); cpu->AF.b[HI]=PEEK(*Get16(cpu,(bmm&2)>>1)); break; case 0x04: /* LD (nn),HL */ TSTATE(16); w=FETCH_WORD; POKEW(w,*GetHLReg(cpu)); break; case 0x05: /* LD (nn),HL */ TSTATE(16); w=FETCH_WORD; *GetHLReg(cpu)=PEEKW(w); break; case 0x06: /* LD (nn),A */ TSTATE(13); w=FETCH_WORD; POKE(w,cpu->AF.b[HI]); break; case 0x07: /* LD A,(nn) */ TSTATE(13); w=FETCH_WORD; cpu->AF.b[HI]=PEEK(w); break; } return; } if (bml==0x03) { if(bmm&1) /* DEC rr */ { TSTATE(6); (*Get16(cpu,bmm>>1))--; } else /* INC rr */ { TSTATE(6); (*Get16(cpu,bmm>>1))++; } return; } if (bml==0x04) { bp=Get8(cpu,bmm); if(!bp) /* INC (HL) */ { Z80Word a; TSTATE(11); a=GetHL(cpu); POKE(a,INC8(cpu,PEEK(a))); } else /* INC r */ { TSTATE(4); *bp=INC8(cpu,*bp); } return; } if (bml==0x05) { bp=Get8(cpu,bmm); if(!bp) /* INC (HL) */ { Z80Word a; TSTATE(11); a=GetHL(cpu); POKE(a,DEC8(cpu,PEEK(a))); } else /* INC r */ { TSTATE(4); *bp=DEC8(cpu,*bp); } return; } if (bml==0x06) { bp=Get8(cpu,bmm); if(!bp) /* LD (HL),n */ { Z80Word a; TSTATE(10); a=GetHL(cpu); POKE(a,FETCH_BYTE); } else /* LD r,n */ { TSTATE(7); *bp=FETCH_BYTE; } return; } if (bml==0x07) { switch(bmm) { case 0x00: /* RLCA */ TSTATE(4); RLCA(cpu); break; case 0x01: /* RRCA */ TSTATE(4); RRCA(cpu); break; case 0x02: /* RLA */ TSTATE(4); RLA(cpu); break; case 0x03: /* RRA */ TSTATE(4); RRA(cpu); break; case 0x04: /* DAA */ TSTATE(4); DAA(cpu); break; case 0x05: /* CPL */ TSTATE(4); cpu->AF.b[HI]=cpu->AF.b[HI]^0xff; SETFLAG(H_Z80); SETFLAG(N_Z80); break; case 0x06: /* SCF */ TSTATE(4); SETFLAG(C_Z80); break; case 0x07: /* CCF */ TSTATE(4); if (IS_C) SETFLAG(H_Z80); else CLRFLAG(H_Z80); cpu->AF.b[LO]^=C_Z80; break; } return; } } static void Normal_01(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { Z80Byte *dest; Z80Byte *src; dest=Get8(cpu,bmm); src=Get8(cpu,bml); /* Handle HALT */ if (!dest && !src) { TSTATE(4); cpu->PC--; /* Inform user of HALT going high */ if (!cpu->halt) CALLBACK(eZ80_Halt,1); cpu->halt=TRUE; return; } /* If the src is NULL, then it is an (HL), (IX...) type instruction */ if (src) { /* If the dest is NULL then it is an (HL) type store */ if (!dest) { Z80Word a; TSTATE(7); /* Reget the source in case we're shifted. HL is the source when doing an (HL) style store. */ src=Get8_HL(cpu,bml); a=GetHL(cpu); POKE(a,*src); } else { TSTATE(4); *dest=*src; } } else { Z80Word a; TSTATE(7); /* Reget the destination in case we're shifted. HL is the dest when doing an (HL) style read. */ dest=Get8_HL(cpu,bmm); a=GetHL(cpu); *dest=PEEK(a); } } static void Normal_10(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { static void (*op[8])(Z80 *cpu, Z80Byte val)= { ADD8, ADC8, SUB8, SBC8, AND, XOR, OR, CMP8 }; Z80Byte *reg; reg=Get8(cpu,bml); if (!reg) /* op A,(HL) */ { Z80Word a; TSTATE(7); a=GetHL(cpu); op[bmm](cpu,PEEK(a)); } else /* op A,r */ { TSTATE(4); op[bmm](cpu,*reg); } } static void Normal_11(Z80 *cpu, Z80Byte opcode, Z80Byte bmh, Z80Byte bmm, Z80Byte bml) { Z80Reg tmp; if (bml==0x00) /* RET cc */ { if (GetCond(cpu,bmm)) { TSTATE(11); POP(cpu->PC); } else { TSTATE(5); } return; } if (bml==0x01) { switch(bmm) { case 0x00: /* POP rr */ case 0x02: case 0x04: case 0x06: TSTATE(10); POP(*Get16_AF(cpu,bmm>>1)); break; case 0x01: /* RET */ TSTATE(10); POP(cpu->PC); break; case 0x03: /* EXX */ TSTATE(4); tmp=cpu->BC; cpu->BC=cpu->BC_; cpu->BC_=tmp; tmp=cpu->DE; cpu->DE=cpu->DE_; cpu->DE_=tmp; tmp=cpu->HL; cpu->HL=cpu->HL_; cpu->HL_=tmp; break; case 0x05: /* JP (HL) */ TSTATE(4); cpu->PC=GetHLVal(cpu); break; case 0x07: /* LD SP,HL */ TSTATE(6); cpu->SP=GetHLVal(cpu); break; } return; } if (bml==0x02) /* JP cc */ { TSTATE(10); if (GetCond(cpu,bmm)) { JP; } else { NOJP; } return; } if (bml==0x03) { Z80Word tmp; Z80Word *reg; switch(bmm) { case 0x00: /* JP nn */ TSTATE(10); cpu->PC=FETCH_WORD; break; case 0x01: /* shift */ break; case 0x02: /* OUT (n),a */ TSTATE(11); OUT(FETCH_BYTE,cpu->AF.b[HI]); break; case 0x03: /* IN A,(n) */ TSTATE(11); cpu->AF.b[HI]=IN(FETCH_BYTE); break; case 0x04: /* EX (SP),HL */ TSTATE(19); reg=GetHLReg(cpu); tmp=*reg; *reg=PEEKW(cpu->SP); POKEW(cpu->SP,tmp); break; case 0x05: /* EX DE,HL */ TSTATE(4); tmp=cpu->HL.w; cpu->HL.w=cpu->DE.w; cpu->DE.w=tmp; break; case 0x06: /* DI */ TSTATE(4); cpu->IFF1=0; cpu->IFF2=0; break; case 0x07: /* EI */ TSTATE(4); cpu->IFF1=1; cpu->IFF2=1; break; } return; } if (bml==0x04) /* CALL cc */ { if (GetCond(cpu,bmm)) { TSTATE(12); CALL; } else { TSTATE(7); NOCALL; } return; } if (bml==0x05) { if (bmm==01) /* CALL nn */ { TSTATE(17); CALL; } else { if (!(bmm&1)) /* PUSH rr */ { TSTATE(11); PUSH(*Get16_AF(cpu,bmm>>1)); } /* Everything else is a shift */ } return; } if (bml==0x06) { TSTATE(8); switch(bmm) { case 0x00: /* ADD A,n */ ADD8(cpu,FETCH_BYTE); break; case 0x01: /* ADC A,n */ ADC8(cpu,FETCH_BYTE); break; case 0x02: /* SUB A,n */ SUB8(cpu,FETCH_BYTE); break; case 0x03: /* SBC A,n */ SBC8(cpu,FETCH_BYTE); break; case 0x04: /* AND n */ AND(cpu,FETCH_BYTE); break; case 0x05: /* XOR n */ XOR(cpu,FETCH_BYTE); break; case 0x06: /* OR n */ OR(cpu,FETCH_BYTE); break; case 0x07: /* CP n */ CMP8(cpu,FETCH_BYTE); break; } return; } if (bml==0x07) /* RST nn */ { TSTATE(11); PUSH(cpu->PC); cpu->PC=bmm*8; return; } } /* ---------------------------------------- NORMAL OPCODE DECODER */ void Z80_Decode(Z80 *cpu, Z80Byte opcode) { Z80Byte bmh,bmm,bml; /* IX/IY shifts */ if (opcode==0xdd || opcode==0xfd) { TSTATE(4); INC_R; cpu->shift=opcode; Z80_Decode(cpu,FETCH_BYTE); return; } /* CB shifts */ if (opcode==0xcb) { INC_R; /* Check for previous IX/IY shift. */ if (cpu->shift!=0) { cpu->use_cb_off=TRUE; cpu->cb_off=(Z80Relative)FETCH_BYTE; } DecodeCB(cpu,FETCH_BYTE); return; } /* DE shifts */ if (opcode==0xed) { INC_R; DecodeED(cpu,FETCH_BYTE); return; } /* Split up the opcode into it's bitmasks */ bmh=(opcode>>6)&3; bmm=(opcode>>3)&7; bml=opcode&7; /* Instruction set 00 */ if (bmh==0x00) { Normal_00(cpu,opcode,bmh,bmm,bml); return; } /* Instruction set 01 */ if (bmh==0x01) { Normal_01(cpu,opcode,bmh,bmm,bml); return; } /* Instruction set 10 */ if (bmh==0x02) { Normal_10(cpu,opcode,bmh,bmm,bml); return; } /* Instruction set 11 */ Normal_11(cpu,opcode,bmh,bmm,bml); } /* END OF FILE */