libz80 with compilation problems (uses up to much ram)

z80.c

Committer:
gertk
Date:
2011-03-12
Revision:
1:78a39c3a30f6
Parent:
0:b612024f5aee

File content as of revision 1:78a39c3a30f6:

/* =========================================================
 *  libz80 - Z80 emulation library
 * =========================================================
 *
 * (C) Gabriel Gambetta (ggambett@adinet.com.uy) 2000 - 2002
 *
 * Version 1.99
 * modified for mbed 2011 Gert van der Knokke
 * ---------------------------------------------------------
 *
 *  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)
static void setFlag(Z80Context* ctx, int flag)
{
    BR.F |= flag;
}

/** Resets a flag */
// static void resFlag(Z80Context* ctx, Z80Flags flag)
static void resFlag(Z80Context* ctx, int flag)
{
    BR.F &= ~flag;
}

/** Puts a value in a flag */
//static void valFlag(Z80Context* ctx, Z80Flags flag, int val)
static void valFlag(Z80Context* ctx, int flag, int val)
{
    if (val)
        SETFLAG(flag);
    else
        RESFLAG(flag);
}

/** Returns a flag */
// static int getFlag(Z80Context* ctx, Z80Flags flag)
static int getFlag(Z80Context* ctx, int 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.h"


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