|
- // 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;
- }
|