libz80 with compilation problems (uses up to much ram)
Diff: z80.c
- Revision:
- 0:b612024f5aee
- Child:
- 1:78a39c3a30f6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/z80.c Thu Mar 10 20:32:59 2011 +0000 @@ -0,0 +1,786 @@ +/* ========================================================= + * libz80 - Z80 emulation library + * ========================================================= + * + * (C) Gabriel Gambetta (ggambett@adinet.com.uy) 2000 - 2002 + * + * Version 1.99 + * + * --------------------------------------------------------- + * + * 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 wheter 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; + + +/* --------------------------------------------------------- + * 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->memWrite(ctx->memParam, addr, val); +} + + +static void write16 (Z80Context* ctx, ushort addr, ushort val) +{ + ctx->memWrite(ctx->memParam, addr, (byte)(val & 0xFF)); + val >>= 8; + addr++; + ctx->memWrite(ctx->memParam, addr, (byte)(val & 0xFF)); +} + + +static byte read8 (Z80Context* ctx, ushort addr) +{ + return ctx->memRead(ctx->memParam, addr); +} + + +static ushort read16 (Z80Context* ctx, ushort addr) +{ + return (ctx->memRead(ctx->memParam, addr) | (ctx->memRead(ctx->memParam, ++addr) << 8)); +} + + +static byte ioRead (Z80Context* ctx, ushort addr) +{ + return ctx->ioRead(ctx->ioParam, addr); +} + + +static void ioWrite (Z80Context* ctx, ushort addr, byte val) +{ + 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] = { + 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, + 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 }; + + +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 GETFLAG(F_); + + 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) | GETFLAG(F_Z)); + 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 == 0)); + VALFLAG(F_PV, (((BR.A & 0x80) == (value & 0x80)) && ((BR.A & 0x80) != (res & 0x80))) != 0); + + adjustFlags(ctx, BR.A); + + return (byte)(res & 0xFF); +} + + +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); + + RESFLAG(F_5); + if ((b == 5) && !GETFLAG(F_Z)) + SETFLAG(F_5); + + RESFLAG(F_3); + if ((b == 3) && !GETFLAG(F_Z)) + SETFLAG(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)); + } + 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, BR.A); + + 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) != 0); + + VALFLAG(F_C, (val & 0x01) != 0); + val >>= 1; + + if (isArith) + val |= (byte)b; + + adjustFlags(ctx, val); + RESFLAG(F_H | F_N); + adjustFlagSZP(ctx, val); + + return val; +} + + +static void doPush (Z80Context* ctx, ushort val) +{ + WR.SP--; + write16(ctx, WR.SP, val); + WR.SP--; +} + + +static ushort doPop (Z80Context* ctx) +{ + ushort val; + + WR.SP++; + val = read16(ctx, WR.SP); + WR.SP++; + + return val; +} + + +/* The DAA opcode + * According to the value in A and the flags set, add a value to A + * + * Flags set Byte (0..9)(0..9) + * -------------------------------------------- + * (None) + &00 + * Carry:+ &60 + * Subtract:+ &00 + * Subtract+Carry:+ &A0 + * Half-carry:+ &06 + * Half-carry+Carry:+ &66 + * Half-carry+Subtract:+ &FA + * Half-carry+Subtract+Carry:+ &9A + * + * Flags set Byte (0..9)(A..F) + * -------------------------------------------- + * (None) + &06 + * Carry:+ &66 + * Subtract:+ &00 + * Subtract+Carry:+ &a0 + * Half-carry:+ &06 + * Half-carry+Carry:+ &66 + * Half-carry+Subtract:+ &fa + * Half-carry+Subtract+Carry:+ &9A + * + * Flags set Byte (A..F)(0..9) + * -------------------------------------------- + * (None) + &60 + * Carry:+ &60 + * Subtract:+ &00 + * Subtract+Carry:+ &A0 + * Half-carry:+ &66 + * Half-carry+Carry:+ &66 + * Half-carry+Subtract:+ &fa + * Half-carry+Subtract+Carry:+ &9A + * + * Flags set Byte (A..F)(A..F) + * -------------------------------------------- + * (None) + &66 + * Carry:+ &66 + * Subtract:+ &00 + * Subtract+Carry:+ &a0 + * Half-carry:+ &66 + * Half-carry+Carry:+ &66 + * Half-carry+Subtract:+ &fa + * Half-carry+Subtract+Carry:+ &9A + */ + +static int DAA_BYTETYPE[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }; + +static byte DAA_ADJUSTMENT[4][8] = { + { 0x00, 0x60, 0x00, 0xA0, 0x06, 0x66, 0xFA, 0x9A }, + { 0x06, 0x66, 0x00, 0xA0, 0x06, 0x66, 0xFA, 0x9A }, + { 0x60, 0x60, 0x00, 0xA0, 0x66, 0x66, 0xFA, 0x9A }, + { 0x66, 0x66, 0x00, 0xA0, 0x66, 0x66, 0xFA, 0x9A } }; + +static void doDAA (Z80Context* ctx) +{ + /* (0..9)(0..9) = 0 */ + /* (0..9)(A..F) = 1 */ + /* (A..F)(0..9) = 2 */ + /* (A..F)(A..F) = 3 */ + int byteType = DAA_BYTETYPE[BR.A] | ((DAA_BYTETYPE[BR.A >> 4]) << 1); + + int flagMask = 0; + if (GETFLAG(F_C)) + flagMask |= 1; + if (GETFLAG(F_S)) + flagMask |= 2; + if (GETFLAG(F_H)) + flagMask |= 4; + + BR.A += DAA_ADJUSTMENT[byteType][flagMask]; + + adjustFlags(ctx, BR.A); +} + +#include "opcodes_impl.c" + + +/* --------------------------------------------------------- + * The top-level functions + * --------------------------------------------------------- + */ +void Z80Execute (Z80Context* ctx) +{ + struct Z80OpcodeTable* current = &opcodes_main; + struct Z80OpcodeEntry* entries = current->entries; + Z80OpcodeFunc func; + + byte opcode; + int offset = 0; + do + { + opcode = read8(ctx, ctx->PC + offset); + + ctx->PC++; + 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; + } + + else + { + /* NOP */ + break; + } + } while(1); +} + + +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++; + ctx->R++; + 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; +} + + +void Z80INT (Z80Context* ctx, byte value) +{ + if (!ctx->IFF1) + return; + + if (ctx->IM == 0) + { + /* FIXME What to do? */ +/* opcode = Val; + execute();*/ + } + else if (ctx->IM == 1) + { + doPush(ctx, ctx->PC); + ctx->PC = 0x0038; + } + else if (ctx->IM == 2) + { + doPush(ctx, ctx->PC); + ctx->PC = (ctx->I << 8) | value; + } +} + + +void Z80NMI (Z80Context* ctx) +{ + ctx->IFF1 = 0; + doPush(ctx, ctx->PC); + ctx->PC = 0x0066; +}