/* * Copyright 2007, 2015 Alex Makeev * * This file is part of ZXMAK2 (ZX Spectrum virtual machine). * * ZXMAK2 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 3 of the License, or * (at your option) any later version. * * ZXMAK2 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 ZXMAK2. If not, see . * * Description: Z80 CPU Emulator [direct opcode part] * Date: 13.04.2007 * */ namespace ZXMAK2.Engine.Cpu.Processor { public partial class Z80Cpu { #region direct/DD/FD private void INA_NN_(byte cmd) // IN A,(N) [11T] { // 11T (4, 3, 4) regs.MW = RDMEM(regs.PC++); Tact += 3; regs.MW += (ushort)(regs.A << 8); regs.A = RDPORT(regs.MW); Tact += 4; regs.MW++; } private void OUT_NN_A(byte cmd) // OUT (N),A [11T]+ { // 11T (4, 3, 4) regs.MW = RDMEM(regs.PC++); Tact += 3; regs.MW += (ushort)(regs.A << 8); WRPORT(regs.MW, regs.A); Tact += 4; regs.ML++; } private void DI(byte cmd) { IFF1 = false; IFF2 = false; } private void EI(byte cmd) { IFF1 = true; IFF2 = true; BINT = true; } private void LDSPHL(byte cmd) // LD SP,HL { RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; regs.SP = regs.HL; } private void EX_SP_HL(byte cmd) // EX (SP),HL { // 19T (4, 3, 4, 3, 5) ushort tmpsp = regs.SP; regs.MW = RDMEM(tmpsp); Tact += 3; tmpsp++; regs.MW += (ushort)(RDMEM(tmpsp) * 0x100); Tact += 3; RDNOMREQ(tmpsp); Tact += 1; WRMEM(tmpsp, regs.H); Tact += 3; tmpsp--; WRMEM(tmpsp, regs.L); Tact += 3; WRNOMREQ(tmpsp); Tact++; WRNOMREQ(tmpsp); Tact++; regs.HL = regs.MW; } private void JP_HL_(byte cmd) // JP (HL) { regs.PC = regs.HL; } private void EXDEHL(byte cmd) // EX DE,HL { (regs.DE, regs.HL) = (regs.HL, regs.DE); } private void EXAFAF(byte cmd) // EX AF,AF' { (regs._AF, regs.AF) = (regs.AF, regs._AF); } private void EXX(byte cmd) // EXX { (regs._BC, regs.BC) = (regs.BC, regs._BC); (regs._DE, regs.DE) = (regs.DE, regs._DE); (regs._HL, regs.HL) = (regs.HL, regs._HL); } #endregion #region logical private void RLCA(byte cmd) { regs.F = (byte)(CpuTables.Rlcaf[regs.A] | (regs.F & CpuFlags.SZP)); int x = regs.A; x <<= 1; if ((x & 0x100) != 0) x |= 0x01; regs.A = (byte)x; } private void RRCA(byte cmd) { regs.F = (byte)(CpuTables.Rrcaf[regs.A] | (regs.F & CpuFlags.SZP)); int x = regs.A; if ((x & 0x01) != 0) x = (x >> 1) | 0x80; else x >>= 1; regs.A = (byte)x; } private void RLA(byte cmd) { var carry = (regs.F & CpuFlags.C) != 0; regs.F = (byte)(CpuTables.Rlcaf[regs.A] | (regs.F & CpuFlags.SZP)); // use same table with rlca regs.A = (byte)(regs.A << 1); if (carry) regs.A |= 0x01; } private void RRA(byte cmd) { var carry = (regs.F & CpuFlags.C) != 0; regs.F = (byte)(CpuTables.Rrcaf[regs.A] | (regs.F & CpuFlags.SZP)); // use same table with rrca regs.A = (byte)(regs.A >> 1); if (carry) regs.A |= 0x80; } private void DAA(byte cmd) { regs.AF = CpuTables.Daaf[regs.A + 0x100 * ((regs.F & 3) + ((regs.F >> 2) & 4))]; } private void CPL(byte cmd) { regs.A = (byte)~regs.A; regs.F = (byte)((regs.F & CpuFlags.NotF3F5) | CpuFlags.HN | (regs.A & CpuFlags.F3F5)); } private void SCF_ZILOG(byte cmd) { regs.F = (byte)((regs.F & CpuFlags.SZP) | (((regs.F ^ regs.Q) | regs.A) & CpuFlags.F3F5) | CpuFlags.C); } private void SCF_ST_CMOS(byte cmd) { regs.F = (byte)((regs.F & CpuFlags.SZP) | ((((regs.F ^ regs.Q) & CpuFlags.F5) | regs.A) & CpuFlags.F3F5) | CpuFlags.C); } private void SCF_NEC_NMOS(byte cmd) { regs.F = (byte)((regs.F & CpuFlags.SZP) | (regs.A & CpuFlags.F3F5) | CpuFlags.C); } private void CCF_ZILOG(byte cmd) { regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) | (((regs.F ^ regs.Q) | regs.A) & CpuFlags.F3F5) | ((regs.F & CpuFlags.C) << 4)); } private void CCF_ST_CMOS(byte cmd) { regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) | ((((regs.F ^ regs.Q) & CpuFlags.F5) | regs.A) & CpuFlags.F3F5) | ((regs.F & CpuFlags.C) << 4)); } private void CCF_NEC_NMOS(byte cmd) { regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) | (regs.A & CpuFlags.F3F5) | ((regs.F & CpuFlags.C) << 4)); } #endregion #region jmp/call/ret/jr private static readonly byte[] s_conds = [ CpuFlags.Z, CpuFlags.C, CpuFlags.P, CpuFlags.S ]; private void DJNZ(byte cmd) // DJNZ nn { // B==0 => 8T (5, 3) // B!=0 => 13T (5, 3, 5) RDNOMREQ(regs.IR); Tact++; int drel = (sbyte)RDMEM(regs.PC); Tact += 3; regs.PC++; if (--regs.B != 0) { RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; regs.MW = (ushort)(regs.PC + drel); regs.PC = regs.MW; } } private void JRNN(byte cmd) // JR nn { // 12T (4, 3, 5) int drel = (sbyte)RDMEM(regs.PC); Tact += 3; regs.PC++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; regs.MW = (ushort)(regs.PC + drel); regs.PC = regs.MW; } private void JRXNN(byte cmd) // JR x,nn { // false => 7T (4, 3) // true => 12 (4, 3, 5) var cond = (cmd & 0x18) >> 3; var mask = s_conds[cond >> 1]; var f = regs.AF & mask; if ((cond & 1) != 0) f ^= mask; int drel = (sbyte)RDMEM(regs.PC); Tact += 3; regs.PC++; if (f == 0) { RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; RDNOMREQ(regs.PC); Tact++; regs.MW = (ushort)(regs.PC + drel); regs.PC = regs.MW; } } private void CALLNNNN(byte cmd) // CALL { // 17T (4, 3, 4, 3, 3) regs.MW = RDMEM(regs.PC); Tact += 3; regs.PC++; regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3; RDNOMREQ(regs.PC); Tact++; regs.PC++; regs.SP--; WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3; regs.SP--; WRMEM(regs.SP, (byte)regs.PC); Tact += 3; regs.PC = regs.MW; } private void CALLXNNNN(byte cmd) // CALL x,#nn { // false => 10T (4, 3, 3) // true => 17T (4, 3, 4, 3, 3) var cond = (cmd & 0x38) >> 3; var mask = s_conds[cond >> 1]; var f = regs.AF & mask; if ((cond & 1) != 0) f ^= mask; regs.MW = RDMEM(regs.PC); Tact += 3; regs.PC++; regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3; if (f == 0) { RDNOMREQ(regs.PC); Tact++; } regs.PC++; if (f == 0) { regs.SP--; WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3; regs.SP--; WRMEM(regs.SP, (byte)regs.PC); Tact += 3; regs.PC = regs.MW; } } private void RET(byte cmd) // RET { // 10T (4, 3, 3) regs.MW = RDMEM(regs.SP); Tact += 3; regs.SP++; regs.MW += (ushort)(RDMEM(regs.SP) * 0x100); Tact += 3; regs.SP++; regs.PC = regs.MW; } private void RETX(byte cmd) // RET x { // false => 5T (5) // true => 11T (5, 3, 3) var cond = (cmd & 0x38) >> 3; var mask = s_conds[cond >> 1]; var f = regs.AF & mask; if ((cond & 1) != 0) f ^= mask; RDNOMREQ(regs.IR); Tact++; if (f == 0) { regs.MW = RDMEM(regs.SP); Tact += 3; regs.SP++; regs.MW += (ushort)(RDMEM(regs.SP) * 0x100); Tact += 3; regs.SP++; regs.PC = regs.MW; } } private void JPNNNN(byte cmd) // JP nnnn { // 10T (4, 3, 3) regs.MW = RDMEM(regs.PC); Tact += 3; regs.PC++; regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3; regs.PC = regs.MW; } private void JPXNN(byte cmd) // JP x,#nn ??? { // 10T (4, 3, 3) var cond = (cmd & 0x38) >> 3; var mask = s_conds[cond >> 1]; var f = regs.AF & mask; if ((cond & 1) != 0) f ^= mask; regs.MW = RDMEM(regs.PC); Tact += 3; regs.PC++; regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3; regs.PC++; if (f == 0) regs.PC = regs.MW; } private void RSTNN(byte cmd) // RST #nn ?TIME? { // 11T (5, 3, 3) var rst = (ushort)(cmd & 0x38); RDNOMREQ(regs.IR); Tact++; regs.SP--; WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3; regs.SP--; WRMEM(regs.SP, (byte)regs.PC); Tact += 3; regs.MW = rst; regs.PC = regs.MW; } #endregion #region push/pop private void PUSHRR(byte cmd) // PUSH RR ?TIME? { // 11T (5, 3, 3) var rr = (cmd & 0x30) >> 4; RDNOMREQ(regs.IR); Tact += 1; ushort val = rr == CpuRegId.Sp ? regs.AF : _pairGetters[rr](); regs.SP--; WRMEM(regs.SP, (byte)(val >> 8)); Tact += 3; regs.SP--; WRMEM(regs.SP, (byte)val); Tact += 3; } private void POPRR(byte cmd) // POP RR { // 10T (4, 3, 3) var rr = (cmd & 0x30) >> 4; var val = (ushort)RDMEM(regs.SP); regs.SP++; Tact += 3; val |= (ushort)(RDMEM(regs.SP) << 8); regs.SP++; if (rr == CpuRegId.Sp) regs.AF = val; else _pairSetters[rr](val); Tact += 3; } #endregion #region ALU private void ALUAN(byte cmd) { // 7T (4, 3) var op = (cmd & 0x38) >> 3; var val = RDMEM(regs.PC); regs.PC++; _alualg[op](val); Tact += 3; } private void ALUAR(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,R { var r = cmd & 0x07; var op = (cmd & 0x38) >> 3; _alualg[op](_regGetters[r]()); } private void ALUA_HL_(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,(HL) { // 7T (4, 3) var op = (cmd & 0x38) >> 3; var val = RDMEM(regs.HL); _alualg[op](val); Tact += 3; } private void ADDHLRR(byte cmd) // ADD HL,RR { RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; regs.MW = (ushort)(regs.HL + 1); regs.HL = ALU_ADDHLRR(regs.HL, _pairGetters[(cmd & 0x30) >> 4]()); } #endregion #region loads private void LDA_RR_(byte cmd) // LD A,(RR) { // 7T (4, 3) var rr = (cmd & 0x30) >> 4; var rrValue = _pairGetters[rr](); regs.A = RDMEM(rrValue); regs.MW = (ushort)(rrValue + 1); Tact += 3; } private void LDA_NN_(byte cmd) // LD A,(nnnn) { // 13T (4, 3, 3, 3) ushort adr = RDMEM(regs.PC); regs.PC++; Tact += 3; adr += (ushort)(RDMEM(regs.PC) * 0x100); regs.PC++; Tact += 3; regs.A = RDMEM(adr); regs.MW = (ushort)(adr + 1); Tact += 3; } private void LDHL_NN_(byte cmd) // LD HL,(nnnn) { // 16T (4, 3, 3, 3, 3) var adr = (ushort)RDMEM(regs.PC); regs.PC++; Tact += 3; adr += (ushort)(RDMEM(regs.PC) * 0x100); regs.PC++; Tact += 3; var val = (ushort)RDMEM(adr); regs.MW = (ushort)(adr + 1); Tact += 3; val += (ushort)(RDMEM(regs.MW) * 0x100); regs.HL = val; Tact += 3; } private void LD_RR_A(byte cmd) // LD (RR),A { // 7T (4, 3) var rr = (cmd & 0x30) >> 4; var rrValue = _pairGetters[rr](); WRMEM(rrValue, regs.A); regs.MH = regs.A; regs.ML = (byte)(rrValue + 1); Tact += 3; } private void LD_NN_A(byte cmd) // LD (nnnn),A { // 13T (4, 3, 3, 3) ushort adr = RDMEM(regs.PC); regs.PC++; Tact += 3; adr += (ushort)(RDMEM(regs.PC) * 0x100); regs.PC++; Tact += 3; WRMEM(adr, regs.A); regs.MH = regs.A; regs.ML = (byte)(adr + 1); Tact += 3; } private void LD_NN_HL(byte cmd) // LD (nnnn),HL { // 16T (4, 3, 3, 3, 3) ushort adr = RDMEM(regs.PC); regs.PC++; Tact += 3; adr += (ushort)(RDMEM(regs.PC) * 0x100); regs.PC++; Tact += 3; WRMEM(adr, regs.L); regs.MW = (ushort)(adr + 1); Tact += 3; WRMEM(regs.MW, regs.H); Tact += 3; } private void LDRRNNNN(byte cmd) // LD RR,nnnn { // 10T (4, 3, 3) var rr = (cmd & 0x30) >> 4; ushort val = RDMEM(regs.PC); regs.PC++; Tact += 3; val |= (ushort)(RDMEM(regs.PC) << 8); regs.PC++; _pairSetters[rr](val); Tact += 3; } private void LDRNN(byte cmd) // LD R,nn { // 7T (4, 3) var r = (cmd & 0x38) >> 3; _regSetters[r](RDMEM(regs.PC)); regs.PC++; Tact += 3; } private void LD_HL_NN(byte cmd) // LD (HL),nn { // 10T (4, 3, 3) var val = RDMEM(regs.PC); regs.PC++; Tact += 3; WRMEM(regs.HL, val); Tact += 3; } private void LDRdRs(byte cmd) // LD R1,R2 { var rsrc = cmd & 0x07; var rdst = (cmd & 0x38) >> 3; _regSetters[rdst](_regGetters[rsrc]()); } private void LD_HL_R(byte cmd) // LD (HL),R { // 7T (4, 3) var r = cmd & 0x07; WRMEM(regs.HL, _regGetters[r]()); Tact += 3; } private void LDR_HL_(byte cmd) // LD R,(HL) { // 7T (4, 3) var r = (cmd & 0x38) >> 3; _regSetters[r](RDMEM(regs.HL)); Tact += 3; } #endregion #region INC/DEC private void DECRR(byte cmd) // DEC RR { var rr = (cmd & 0x30) >> 4; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; _pairSetters[rr]((ushort)(_pairGetters[rr]() - 1)); } private void INCRR(byte cmd) // INC RR { var rr = (cmd & 0x30) >> 4; RDNOMREQ(regs.IR); Tact++; RDNOMREQ(regs.IR); Tact++; _pairSetters[rr]((ushort)(_pairGetters[rr]() + 1)); } private void DECR(byte cmd) // DEC R { var r = (cmd & 0x38) >> 3; _regSetters[r](ALU_DECR(_regGetters[r]())); } private void INCR(byte cmd) // INC R { var r = (cmd & 0x38) >> 3; _regSetters[r](ALU_INCR(_regGetters[r]())); } private void DEC_HL_(byte cmd) // DEC (HL) { // 11T (4, 4, 3) var val = RDMEM(regs.HL); Tact += 3; RDNOMREQ(regs.HL); Tact++; val = ALU_DECR(val); WRMEM(regs.HL, val); Tact += 3; } private void INC_HL_(byte cmd) // INC (HL) { // 11T (4, 4, 3) var val = RDMEM(regs.HL); Tact += 3; RDNOMREQ(regs.HL); Tact++; val = ALU_INCR(val); WRMEM(regs.HL, val); Tact += 3; } #endregion private void HALT(byte cmd) { HALTED = true; regs.PC--; // workaround for Z80 snapshot halt issue + comfortable debugging } } }