/*
    Z80 emulator

    Copyright (c) 1996-2003,2004 Alessandro Scotti
    http://www.ascotti.org/

    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"

/* Constructor */
Z80::Z80( Z80Environment & env )
    : env_( env )
{
    reset();
}

/* Copy constructor (it's ok to use the assignment operator in this case) */
Z80::Z80( const Z80 & cpu )
    : env_( cpu.env_ )
{
    operator = ( cpu );
}

/* Assignment operator */
Z80 & Z80::operator = ( const Z80 & cpu )
{
    B = cpu.B;
    C = cpu.C;
    D = cpu.D;
    E = cpu.E;
    H = cpu.H;
    L = cpu.L;
    A = cpu.A;
    F = cpu.F;
    B1 = cpu.B1;
    C1 = cpu.C1;
    D1 = cpu.D1;
    E1 = cpu.E1;
    H1 = cpu.H1;
    L1 = cpu.L1;
    A1 = cpu.A1;
    F1 = cpu.F1;
    IX = cpu.IX;
    IY = cpu.IY;
    PC = cpu.PC;
    SP = cpu.SP;
    I = cpu.I;
    R = cpu.R;

    iflags_ = iflags_;
    cycles_ = cycles_;

    return *this;
}

/* Resets the CPU */
void Z80::reset()
{
    PC = 0;         // Program counter is zero
    I = 0;          // Interrupt register cleared
    R = 0;          // Memory refresh register cleared
    iflags_ = 0;    // IFF1 and IFF2 cleared, IM0 enabled
    cycles_ = 0;    // Could that be 2 (according to some Zilog docs)?

    // There is no official documentation for the following!
    B = B1 = 0; 
    C = C1 = 0;
    D = D1 = 0; 
    E = E1 = 0;
    H = H1 = 0;
    L = L1 = 0;
    A = A1 = 0;
    F = F1 = 0;
    IX = 0;
    IY = 0;
    SP = 0xF000;
}

unsigned Z80::getSizeOfSnapshotBuffer() const
{
    unsigned result =
        8*2 +   // 8-bit registers
        1 +     // I
        1 +     // R
        2 +     // IX
        2 +     // IY
        2 +     // PC
        2 +     // SP
        4 +     // iflags_
        4;      // cycles_    

    return result;
}

static unsigned saveUint16( unsigned char * buffer, unsigned u )
{
    *buffer++ = (unsigned char) (u >> 8);
    *buffer   = (unsigned char) (u);

    return 2;
}

unsigned Z80::takeSnapshot( unsigned char * buffer )
{
    unsigned char * buf = buffer;

    *buf++ = A; *buf++ = A1;
    *buf++ = B; *buf++ = B1;
    *buf++ = C; *buf++ = C1;
    *buf++ = D; *buf++ = D1;
    *buf++ = E; *buf++ = E1;
    *buf++ = H; *buf++ = H1;
    *buf++ = L; *buf++ = L1;
    *buf++ = F; *buf++ = F1;

    *buf++ = I;
    *buf++ = R;

    buf += saveUint16( buf, IX );
    buf += saveUint16( buf, IY );
    buf += saveUint16( buf, PC );
    buf += saveUint16( buf, SP );

    buf += saveUint16( buf, iflags_ >> 16 );
    buf += saveUint16( buf, iflags_ );
    buf += saveUint16( buf, cycles_ >> 16 );
    buf += saveUint16( buf, cycles_ );

    return buffer - buf;
}

static unsigned loadUint16( unsigned char ** buffer )
{
    unsigned char * buf = *buffer;
    unsigned result = *buf++;

    result = (result << 8) | *buf++;

    *buffer = buf;

    return result;
}

unsigned Z80::restoreSnapshot( unsigned char * buffer )
{
    unsigned char * buf = buffer;

    A = *buf++; A1 = *buf++;
    B = *buf++; B1 = *buf++;
    C = *buf++; C1 = *buf++;
    D = *buf++; D1 = *buf++;
    E = *buf++; E1 = *buf++;
    H = *buf++; H1 = *buf++;
    L = *buf++; L1 = *buf++;
    F = *buf++; F1 = *buf++;

    I = *buf++;
    R = *buf++;

    IX = loadUint16( &buf );
    IY = loadUint16( &buf );
    PC = loadUint16( &buf );
    SP = loadUint16( &buf );

    iflags_ = loadUint16( &buf );
    iflags_ = (iflags_ << 16) | loadUint16(&buf);
    cycles_ = loadUint16( &buf );
    cycles_ = (cycles_ << 16) | loadUint16(&buf);

    return buf - buffer;
}

/* Executes one instruction */
void Z80::step()
{
    // Update memory refresh register (not strictly needed but...)
    R = (R+1) & 0x7F; 

    if( iflags_ & Halted ) {
        // CPU is halted, do a NOP instruction
        cycles_ += OpInfo_[0].cycles; // NOP
    }
    else {
        // Get the opcode to execute
        unsigned op = fetchByte();

        // Update the cycles counter with the number of cycles for this opcode
        cycles_ += OpInfo_[ op ].cycles;

        // Execute the opcode handler
        (this->*(OpInfo_[ op ].handler))();

        // Update registers
        PC &= 0xFFFF; // Clip program counter
        SP &= 0xFFFF; // Clip stack pointer
    }
}

/*
    Runs the CPU for the specified number of cycles.

    Note: the memory refresh register is not updated!
*/
unsigned Z80::run( unsigned runCycles )
{
    unsigned target_cycles = cycles_ + runCycles;

    // Execute instructions until the specified number of
    // cycles has elapsed
    while( cycles_ < target_cycles ) {
        if( iflags_ & Halted ) {
            // CPU is halted, do NOPs for the rest of cycles
            // (this may be off by a few cycles)
            cycles_ = target_cycles;
        }
        else {
            // Get the opcode to execute
            unsigned op = fetchByte();

            // Update the cycles counter with the number of cycles for this opcode
            cycles_ += OpInfo_[ op ].cycles; 

            // Execute the opcode handler
            (this->*(OpInfo_[ op ].handler))();
        }
    }

    // Update registers
    PC &= 0xFFFF; // Clip program counter
    SP &= 0xFFFF; // Clip stack pointer

    // Return the number of extra cycles executed
    return cycles_ - target_cycles;
}

/* Interrupt */
void Z80::interrupt( unsigned char data )
{
    // Execute interrupt only if interrupts are enabled
    if( iflags_ & IFF1 ) {
        // Disable maskable interrupts and restart the CPU if halted
        iflags_ &= ~(IFF1 | IFF2 | Halted); 

        switch( getInterruptMode() ) {
        case 0:
            (this->*(OpInfo_[ data ].handler))();
            cycles_ += 11;
            break;
        case 1:
            callSub( 0x38 );
            cycles_ += 11;
            break;
        case 2:
            callSub( readWord( ((unsigned)I) << 8 | (data & 0xFE) ) );
            cycles_ += 19;
            break;
        }
    }
}

/* Non-maskable interrupt */
void Z80::nmi()
{
    // Disable maskable interrupts but preserve IFF2 (that is a copy of IFF1),
    // also restart the CPU if halted
    iflags_ &= ~(IFF1 | Halted);

    callSub( 0x66 );

    cycles_ += 11;
}
