// This unit has been copied from libz80 into Collapse OS and was slighly changed /* ========================================================= * libz80 - Z80 emulation library * ========================================================= * * (C) Gabriel Gambetta (gabriel.gambetta@gmail.com) 2000 - 2012 * * Version 2.1.0 * * --------------------------------------------------------- * * 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. */ #include "z80.h" #include "string.h" #define BR (ctx->R1.br) #define WR (ctx->R1.wr) #define SETFLAG(F) setFlag(ctx, F) #define RESFLAG(F) resFlag(ctx, F) #define GETFLAG(F) getFlag(ctx, F) #define VALFLAG(F,V) valFlag(ctx, F, V) /* --------------------------------------------------------- * Flag tricks * --------------------------------------------------------- * * To avoid repeating entries in the spec files, many operations that look similar are treated as special cases * of a more general operation. * * For example, ADD and ADC are similar in syntax and operation - the difference is that ADC takes the carry flag * into account. * * So we define a general operation doArithmetic(...) which accepts a boolean parameter specifying whether to do * a Carry-operation or not. Then, when we parse, we can say * * (ADD|ADC) .... * doArithmetic(FLAG_FOR_%1) * * and everything works fine. * */ /* Flags for doIncDec() */ static const int ID_INC = 0; static const int ID_DEC = 1; /* Flags for enable / disable interrupts */ static const int IE_DI = 0; static const int IE_EI = 1; /* Flags for doSetRes() */ static const int SR_RES = 0; static const int SR_SET = 1; /* Flags for logical / arithmetic operations */ static const int IA_L = 0; static const int IA_A = 1; /* Flags for doArithmetic() - F1 = withCarry, F2 = isSub */ static const int F1_ADC = 1; static const int F1_SBC = 1; static const int F1_ADD = 0; static const int F1_SUB = 0; static const int F2_ADC = 0; static const int F2_SBC = 1; static const int F2_ADD = 0; static const int F2_SUB = 1; /* Increment or decrement R, preserving bit 7 */ #define INCR (ctx->R = (ctx->R & 0x80) | ((ctx->R + 1) & 0x7f)) #define DECR (ctx->R = (ctx->R & 0x80) | ((ctx->R - 1) & 0x7f)) /* --------------------------------------------------------- * The opcode implementations * --------------------------------------------------------- */ #include "opcodes_decl.h" typedef enum { OP_NONE, OP_BYTE, OP_OFFSET, OP_WORD } Z80OperandType; typedef void (*Z80OpcodeFunc) (Z80Context* ctx); struct Z80OpcodeEntry { Z80OpcodeFunc func; int operand_type; char* format; struct Z80OpcodeTable* table; }; struct Z80OpcodeTable { int opcode_offset; struct Z80OpcodeEntry entries[256]; }; #include "opcodes_table.h" /* --------------------------------------------------------- * Data operations * --------------------------------------------------------- */ static void write8 (Z80Context* ctx, ushort addr, byte val) { ctx->tstates += 3; ctx->memWrite(ctx->memParam, addr, val); } static void write16 (Z80Context* ctx, ushort addr, ushort val) { write8(ctx, addr, val); write8(ctx, addr + 1, val >> 8); } static byte read8 (Z80Context* ctx, ushort addr) { ctx->tstates += 3; return ctx->memRead(ctx->memParam, addr); } static ushort read16 (Z80Context* ctx, ushort addr) { byte lsb = read8(ctx, addr); byte msb = read8(ctx, addr + 1); return msb << 8 | lsb; } static byte ioRead (Z80Context* ctx, ushort addr) { ctx->tstates += 4; return ctx->ioRead(ctx->ioParam, addr); } static void ioWrite (Z80Context* ctx, ushort addr, byte val) { ctx->tstates += 4; ctx->ioWrite(ctx->ioParam, addr, val); } /* --------------------------------------------------------- * Flag operations * --------------------------------------------------------- */ /** Sets a flag */ static void setFlag(Z80Context* ctx, Z80Flags flag) { BR.F |= flag; } /** Resets a flag */ static void resFlag(Z80Context* ctx, Z80Flags flag) { BR.F &= ~flag; } /** Puts a value in a flag */ static void valFlag(Z80Context* ctx, Z80Flags flag, int val) { if (val) SETFLAG(flag); else RESFLAG(flag); } /** Returns a flag */ static int getFlag(Z80Context* ctx, Z80Flags flag) { return (BR.F & flag) != 0; } /* --------------------------------------------------------- * Flag adjustments * --------------------------------------------------------- */ static int parityBit[256] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }; static void adjustFlags (Z80Context* ctx, byte val) { VALFLAG(F_5, (val & F_5) != 0); VALFLAG(F_3, (val & F_3) != 0); } static void adjustFlagSZP (Z80Context* ctx, byte val) { VALFLAG(F_S, (val & 0x80) != 0); VALFLAG(F_Z, (val == 0)); VALFLAG(F_PV, parityBit[val]); } /* Adjust flags after AND, OR, XOR */ static void adjustLogicFlag (Z80Context* ctx, int flagH) { VALFLAG(F_S, (BR.A & 0x80) != 0); VALFLAG(F_Z, (BR.A == 0)); VALFLAG(F_H, flagH); VALFLAG(F_N, 0); VALFLAG(F_C, 0); VALFLAG(F_PV, parityBit[BR.A]); adjustFlags(ctx, BR.A); } /* --------------------------------------------------------- * Condition checks * --------------------------------------------------------- */ typedef enum { C_, C_Z, C_NZ, C_C, C_NC, C_M, C_P, C_PE, C_PO } Z80Condition; static int condition(Z80Context* ctx, Z80Condition cond) { if (cond == C_) return 1; if (cond == C_Z) return GETFLAG(F_Z); if (cond == C_NZ) return !GETFLAG(F_Z); if (cond == C_C) return GETFLAG(F_C); if (cond == C_NC) return !GETFLAG(F_C); if (cond == C_M) return GETFLAG(F_S); if (cond == C_P) return !GETFLAG(F_S); if (cond == C_PE) return GETFLAG(F_PV); /* if (cond == C_PO)*/ return !GETFLAG(F_PV); } /* --------------------------------------------------------- * Generic operations * --------------------------------------------------------- */ static int doComplement(byte v) { if ((v & 0x80) == 0) return v; v = ~v; v &= 0x7F; v++; return -v; } /** Do an arithmetic operation (ADD, SUB, ADC, SBC y CP) */ static byte doArithmetic (Z80Context* ctx, byte value, int withCarry, int isSub) { ushort res; /* To detect carry */ if (isSub) { SETFLAG(F_N); VALFLAG(F_H, (((BR.A & 0x0F) - (value & 0x0F)) & 0x10) != 0); res = BR.A - value; if (withCarry && GETFLAG(F_C)) res--; } else { RESFLAG(F_N); VALFLAG(F_H, (((BR.A & 0x0F) + (value & 0x0F)) & 0x10) != 0); res = BR.A + value; if (withCarry && GETFLAG(F_C)) res++; } VALFLAG(F_S, ((res & 0x80) != 0)); VALFLAG(F_C, ((res & 0x100) != 0)); VALFLAG(F_Z, ((res & 0xff) == 0)); int minuend_sign = BR.A & 0x80; int subtrahend_sign = value & 0x80; int result_sign = res & 0x80; int overflow; if(isSub) overflow = minuend_sign != subtrahend_sign && result_sign != minuend_sign; else overflow = minuend_sign == subtrahend_sign && result_sign != minuend_sign; VALFLAG(F_PV, overflow); adjustFlags(ctx, res); return (byte)(res & 0xFF); } /* Do a 16-bit addition, setting the appropriate flags. */ static ushort doAddWord(Z80Context* ctx, ushort a1, ushort a2, int withCarry, int isSub) { if(withCarry && GETFLAG(F_C)) a2++; int sum = a1; if(isSub) { sum -= a2; VALFLAG(F_H, ((a1 & 0x0fff) - (a2 & 0x0fff)) & 0x1000); } else { sum += a2; VALFLAG(F_H, ((a1 & 0x0fff) + (a2 & 0x0fff)) & 0x1000); } VALFLAG(F_C, sum & 0x10000); if(withCarry || isSub) { int minuend_sign = a1 & 0x8000; int subtrahend_sign = a2 & 0x8000; int result_sign = sum & 0x8000; int overflow; if(isSub) overflow = minuend_sign != subtrahend_sign && result_sign != minuend_sign; else overflow = minuend_sign == subtrahend_sign && result_sign != minuend_sign; VALFLAG(F_PV, overflow); VALFLAG(F_S, (sum & 0x8000) != 0); VALFLAG(F_Z, (sum & 0xFFFF) == 0); } VALFLAG(F_N, isSub); adjustFlags(ctx, sum >> 8); return sum; } static void doAND (Z80Context* ctx, byte value) { BR.A &= value; adjustLogicFlag(ctx, 1); } static void doOR (Z80Context* ctx, byte value) { BR.A |= value; adjustLogicFlag(ctx, 0); } static void doXOR (Z80Context* ctx, byte value) { BR.A ^= value; adjustLogicFlag(ctx, 0); } static void doBIT (Z80Context* ctx, int b, byte val) { if (val & (1 << b)) RESFLAG(F_Z | F_PV); else SETFLAG(F_Z | F_PV); SETFLAG(F_H); RESFLAG(F_N); RESFLAG(F_S); if ((b == 7) && !GETFLAG(F_Z)) SETFLAG(F_S); } static void doBIT_r(Z80Context* ctx, int b, byte val) { doBIT(ctx, b, val); VALFLAG(F_5, val & F_5); VALFLAG(F_3, val & F_3); } static void doBIT_indexed(Z80Context* ctx, int b, ushort address) { byte val = read8(ctx, address); doBIT(ctx, b, val); VALFLAG(F_5, (address >> 8) & F_5); VALFLAG(F_3, (address >> 8) & F_3); } byte doSetRes (Z80Context* ctx, int bit, int pos, byte val) { if (bit) val |= (1 << pos); else val &= ~(1 << pos); return val; } static byte doIncDec (Z80Context* ctx, byte val, int isDec) { if (isDec) { VALFLAG(F_PV, (val & 0x80) && !((val - 1) & 0x80)); val--; VALFLAG(F_H, (val & 0x0F) == 0x0F); } else { VALFLAG(F_PV, !(val & 0x80) && ((val + 1) & 0x80)); val++; VALFLAG(F_H, !(val & 0x0F)); } VALFLAG(F_S, ((val & 0x80) != 0)); VALFLAG(F_Z, (val == 0)); VALFLAG(F_N, isDec); adjustFlags(ctx, val); return val; } static byte doRLC (Z80Context* ctx, int adjFlags, byte val) { VALFLAG(F_C, (val & 0x80) != 0); val <<= 1; val |= (byte)GETFLAG(F_C); adjustFlags(ctx, val); RESFLAG(F_H | F_N); if (adjFlags) adjustFlagSZP(ctx, val); return val; } static byte doRL (Z80Context* ctx, int adjFlags, byte val) { int CY = GETFLAG(F_C); VALFLAG(F_C, (val & 0x80) != 0); val <<= 1; val |= (byte)CY; adjustFlags(ctx, val); RESFLAG(F_H | F_N); if (adjFlags) adjustFlagSZP(ctx, val); return val; } static byte doRRC (Z80Context* ctx, int adjFlags, byte val) { VALFLAG(F_C, (val & 0x01) != 0); val >>= 1; val |= ((byte)GETFLAG(F_C) << 7); adjustFlags(ctx, val); RESFLAG(F_H | F_N); if (adjFlags) adjustFlagSZP(ctx, val); return val; } static byte doRR (Z80Context* ctx, int adjFlags, byte val) { int CY = GETFLAG(F_C); VALFLAG(F_C, (val & 0x01)); val >>= 1; val |= (CY << 7); adjustFlags(ctx, val); RESFLAG(F_H | F_N); if (adjFlags) adjustFlagSZP(ctx, val); return val; } static byte doSL (Z80Context* ctx, byte val, int isArith) { VALFLAG(F_C, (val & 0x80) != 0); val <<= 1; if (!isArith) val |= 1; adjustFlags(ctx, val); RESFLAG(F_H | F_N); adjustFlagSZP(ctx, val); return val; } static byte doSR (Z80Context* ctx, byte val, int isArith) { int b = val & 0x80; VALFLAG(F_C, (val & 0x01) != 0); val >>= 1; if (isArith) val |= b; adjustFlags(ctx, val); RESFLAG(F_H | F_N); adjustFlagSZP(ctx, val); return val; } static void doPush (Z80Context* ctx, ushort val) { WR.SP--; WR.SP--; write16(ctx, WR.SP, val); } static ushort doPop (Z80Context* ctx) { ushort val; val = read16(ctx, WR.SP); WR.SP++; WR.SP++; return val; } static byte doCP_HL(Z80Context * ctx) { byte val = read8(ctx, WR.HL); byte result = doArithmetic(ctx, val, 0, 1); adjustFlags(ctx, val); return result; } /* The DAA opcode * According to the value in A and the flags set, add a value to A * This algorithm taken from: * http://www.worldofspectrum.org/faq/reference/z80reference.htm * and verified against the specification in the Zilog * Z80 Family CPU User Manual, rev. 04, Dec. 2004, pp. 166-167 */ static void doDAA(Z80Context * ctx) { int correction_factor = 0x00; int carry = 0; if(BR.A > 0x99 || GETFLAG(F_C)) { correction_factor |= 0x60; carry = 1; } if((BR.A & 0x0f) > 9 || GETFLAG(F_H)) correction_factor |= 0x06; int a_before = BR.A; if(GETFLAG(F_N)) BR.A -= correction_factor; else BR.A += correction_factor; VALFLAG(F_H, (a_before ^ BR.A) & 0x10); VALFLAG(F_C, carry); VALFLAG(F_S, (BR.A & 0x80) != 0); VALFLAG(F_Z, (BR.A == 0)); VALFLAG(F_PV, parityBit[BR.A]); adjustFlags(ctx, BR.A); } #include "opcodes_impl.c" /* --------------------------------------------------------- * The top-level functions * --------------------------------------------------------- */ static void do_execute(Z80Context* ctx) { struct Z80OpcodeTable* current = &opcodes_main; struct Z80OpcodeEntry* entries = current->entries; Z80OpcodeFunc func; byte opcode; int offset = 0; do { if (ctx->exec_int_vector) { opcode = ctx->int_vector; ctx->tstates += 6; } else { opcode = read8(ctx, ctx->PC + offset); ctx->PC++; ctx->tstates += 1; } INCR; func = entries[opcode].func; if (func != NULL) { ctx->PC -= offset; func(ctx); ctx->PC += offset; break; } else if (entries[opcode].table != NULL) { current = entries[opcode].table; entries = current->entries; offset = current->opcode_offset; if (offset > 0) DECR; } else { /* NOP */ break; } } while(1); } static void unhalt(Z80Context* ctx) { if (ctx->halted) { ctx->halted = 0; ctx->PC++; } } static void do_nmi(Z80Context* ctx) { unhalt(ctx); ctx->IFF2 = ctx->IFF1; ctx->IFF1 = 0; doPush(ctx, ctx->PC); ctx->PC = 0x0066; ctx->nmi_req = 0; ctx->tstates += 5; } static void do_int(Z80Context* ctx) { unhalt(ctx); ctx->IFF1 = 0; ctx->IFF2 = 0; ctx->int_req = 0; if (ctx->IM == 0) { ctx->exec_int_vector = 1; do_execute(ctx); ctx->exec_int_vector = 0; } else if (ctx->IM == 1) { doPush(ctx, ctx->PC); ctx->PC = 0x0038; ctx->tstates += 7; } else if (ctx->IM == 2) { doPush(ctx, ctx->PC); ushort vector_address = (ctx->I << 8) | ctx->int_vector; ctx->PC = read16(ctx, vector_address); ctx->tstates += 7; } } void Z80Execute (Z80Context* ctx) { if (ctx->nmi_req) do_nmi(ctx); else if (ctx->int_req && !ctx->defer_int && ctx->IFF1) do_int(ctx); else { ctx->defer_int = 0; do_execute(ctx); } } unsigned Z80ExecuteTStates(Z80Context* ctx, unsigned tstates) { ctx->tstates = 0; while (ctx->tstates < tstates) Z80Execute(ctx); return ctx->tstates; } void Z80Debug (Z80Context* ctx, char* dump, char* decode) { char tmp[20]; struct Z80OpcodeTable* current = &opcodes_main; struct Z80OpcodeEntry* entries = current->entries; char* fmt; byte opcode; ushort parm; int offset = 0; int PC = ctx->PC; int size = 0; if (dump) dump[0] = 0; if (decode) decode[0] = 0; do { opcode = read8(ctx, PC + offset); size++; PC++; fmt = entries[opcode].format; if (fmt != NULL) { PC -= offset; parm = read16(ctx, PC); if (entries[opcode].operand_type == OP_NONE) size++; else size += 2; if (entries[opcode].operand_type != OP_WORD) { parm &= 0xFF; size--; } if (decode) sprintf(decode, fmt, parm); PC += offset; break; } else if (entries[opcode].table != NULL) { current = entries[opcode].table; entries = current->entries; offset = current->opcode_offset; } else { if (decode != NULL) strcpy(decode, "NOP (ignored)"); break; } } while(1); if (dump) { for (offset = 0; offset < size; offset++) { sprintf(tmp, "%02X", read8(ctx, ctx->PC + offset)); strcat(dump, tmp); } } } void Z80RESET (Z80Context* ctx) { ctx->PC = 0x0000; BR.F = 0; ctx->IM = 0; ctx->IFF1 = ctx->IFF2 = 0; ctx->R = 0; ctx->I = 0; ctx->halted = 0; ctx->tstates = 0; ctx->nmi_req = 0; ctx->int_req = 0; ctx->defer_int = 0; ctx->exec_int_vector = 0; } void Z80INT (Z80Context* ctx, byte value) { ctx->int_req = 1; ctx->int_vector = value; } void Z80NMI (Z80Context* ctx) { ctx->nmi_req = 1; }