/***************************************************************************
 *   Copyright (C) 2020 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#include "picmrcore.h"
#include "datautils.h"
#include "regwatcher.h"

PicMrCore::PicMrCore( eMcu* mcu )
         : McuCpu( mcu )
{
    m_sp = 0;
    m_bank = 0;

    m_PCLaddr = mcu->getRegAddress("PCL");
    m_PCHaddr = mcu->getRegAddress("PCLATH");
}
PicMrCore::~PicMrCore() {}

void PicMrCore::reset()
{
    CpuBase::reset();

    *m_Wreg = 0;
}

void PicMrCore::setBank( uint8_t bank )
{
    m_bank = getRegBitsVal( bank, m_bankBits );
    m_bank <<= 7;
}

uint8_t PicMrCore::add( uint8_t val1, uint8_t val2 )
{
    uint16_t newV = val1 + val2;
    write_S_Bit( Z, (newV & 0xFF)==0 );
    write_S_Bit( C, newV & 0x100 );
    write_S_Bit( DC, ((val1&0xF)+(val2 & 0xF)) > 0xF );
    return newV;
}

uint8_t PicMrCore::sub( uint8_t val1, uint8_t val2 )
{
    int16_t newV = val1 - val2;
    write_S_Bit( Z, (newV & 0xFF)==0 );
    write_S_Bit( C, newV >= 0 );
    write_S_Bit( DC, ((int16_t)((val1&0xF)-(val2&0xF)) >= 0) ); // Carry and Half carry set when doing 0-0
    return newV;
}

// Miscellaneous instructions

inline void PicMrCore::RETURN() { RET(); }

inline void PicMrCore::RETFIE() { RETI(); }

inline void PicMrCore::OPTION() { *m_OPTION = *m_Wreg; }

inline void PicMrCore::SLEEP()
{
    write_S_Bit( PD, false );
    write_S_Bit( TO, true );
    m_mcu->sleep( true );
}

void PicMrCore::exitSleep() { write_S_Bit( TO, false ); }

inline void PicMrCore::CLRWDT()
{
    write_S_Bit( PD, true );
    write_S_Bit( TO, true );
}

// ALU operations: dest ← OP(f,W)

inline void PicMrCore::MOVWF( uint8_t f )
{
    SET_RAM( f, *m_Wreg);
}

inline void PicMrCore::CLRF( uint8_t f )
{
    SET_RAM( f, 0 );
    write_S_Bit( Z, true );
}

inline void PicMrCore::SUBWF( uint8_t f, uint8_t d )
{
    uint8_t newV = sub( GET_RAM( f ), *m_Wreg );
    setValue( newV, f, d );
}

inline void PicMrCore::DECF( uint8_t f, uint8_t d )
{
    uint8_t newV = GET_RAM( f );
    setValueZ( --newV, f, d );
}

inline void PicMrCore::IORWF( uint8_t f, uint8_t d )
{
    uint8_t oldV = GET_RAM( f ) ;
    uint8_t newV = oldV | *m_Wreg;
    setValueZ( newV, f, d );
}

inline void PicMrCore::ANDWF( uint8_t f, uint8_t d )
{
    uint8_t oldV = GET_RAM( f ) ;
    uint8_t newV = oldV & *m_Wreg;
    setValueZ( newV, f, d );
}

inline void PicMrCore::XORWF( uint8_t f, uint8_t d )
{
    uint8_t oldV = GET_RAM( f ) ;
    uint8_t newV = oldV ^ *m_Wreg;
    setValueZ( newV, f, d );
}

inline void PicMrCore::ADDWF( uint8_t f, uint8_t d )
{
    uint8_t newV = add( GET_RAM( f ), *m_Wreg );
    setValue( newV, f, d );
}

inline void PicMrCore::MOVF( uint8_t f, uint8_t d )
{
    uint8_t newV = GET_RAM( f );
    setValueZ( newV, f, d );
}

inline void PicMrCore::COMF( uint8_t f, uint8_t d )
{
    uint8_t newV = GET_RAM( f ) ^ 0xFF;
    setValueZ( newV, f, d );
}

inline void PicMrCore::INCF( uint8_t f, uint8_t d )
{
    uint8_t newV = GET_RAM( f );
    setValueZ( ++newV, f, d );
}

inline void PicMrCore::DECFSZ( uint8_t f, uint8_t d )
{
    uint8_t newV = GET_RAM( f ) - 1;
    setValue( newV, f, d );
    if( newV == 0 ) incDefault();
}

inline void PicMrCore::RRF( uint8_t f, uint8_t d )
{
    uint8_t oldV = GET_RAM( f ) ;
    uint8_t newV = oldV >> 1;
    if( *m_STATUS & 1<<C ) newV |= 1<<7; // Carry In
    write_S_Bit( C, oldV & 1 );          // Carry Out
    setValue( newV, f, d );
}

inline void PicMrCore::RLF( uint8_t f, uint8_t d )
{
    uint8_t oldV = GET_RAM( f ) ;
    uint8_t newV = oldV << 1;
    if( *m_STATUS & 1<<C ) newV |= 1; // Carry In
    write_S_Bit( C, oldV & 1<<7 );    // Carry Out
    setValue( newV, f, d );
}

inline void PicMrCore::SWAPF( uint8_t f, uint8_t d )
{
    uint8_t oldV = GET_RAM( f );
    uint8_t newV = ((oldV >> 4) & 0x0F) | ((oldV << 4) & 0xF0);
    setValue( newV, f, d );
}

inline void PicMrCore::INCFSZ( uint8_t f, uint8_t d )
{
    uint8_t newV = GET_RAM( f ) + 1;
    setValue( newV, f, d );
    if( newV == 0 ) incDefault();
}

// Bit operations

inline void PicMrCore::BCF( uint8_t f, uint8_t b )
{
    uint8_t newV = GET_RAM( f );
    newV &= ~(1<<b);
    SET_RAM( f, newV );
}

inline void PicMrCore::BSF( uint8_t f, uint8_t b )
{
    uint8_t newV = GET_RAM( f );
    newV |= 1<<b;
    SET_RAM( f, newV );
}

inline void PicMrCore::BTFSC( uint8_t f, uint8_t b )
{
    uint8_t oldV = GET_RAM( f );
    uint8_t bitMask = 1<<b;
    if( (oldV & bitMask) == 0 ) incDefault();
}

inline void PicMrCore::BTFSS( uint8_t f, uint8_t b )
{
    uint8_t oldV = GET_RAM( f );
    if( oldV & 1<<b  ) incDefault();
}

// Control transfers

inline void PicMrCore::CALL( uint16_t k )
{
    CALL_ADDR( k | ((uint16_t)(m_dataMem[m_PCHaddr] & 0b00011000)<<8) );
}

inline void PicMrCore::GOTO( uint16_t k )
{
    setPC( k | ((uint16_t)(m_dataMem[m_PCHaddr] & 0b00011000)<<8) );
    m_mcu->cyclesDone = 2;
}

// Operations with W and 8-bit literal: W ← OP(k,W)

inline void PicMrCore::MOVLW( uint8_t k )
{
    *m_Wreg = k;
}

inline void PicMrCore::RETLW( uint8_t k )
{
    *m_Wreg = k;
    RETURN();
}

inline void PicMrCore::IORLW( uint8_t k )
{
    *m_Wreg |= k;
    write_S_Bit( Z, *m_Wreg==0 );
}

inline void PicMrCore::ANDLW( uint8_t k )
{
    *m_Wreg &= k;
    write_S_Bit( Z, *m_Wreg==0 );
}

inline void PicMrCore::XORLW( uint8_t k )
{
    *m_Wreg ^= k;
    write_S_Bit( Z, *m_Wreg==0 );
}

inline void PicMrCore::SUBLW( uint8_t k ) //// C,DC,Z
{
    *m_Wreg = sub( k, *m_Wreg );
}

inline void PicMrCore::ADDLW( uint8_t k ) //// C,DC,Z
{
    *m_Wreg = add( k, *m_Wreg );
}

void PicMrCore::runStep()
{
    uint16_t instr = m_progMem[m_PC] & 0x3FFF;

    m_mcu->cyclesDone = 0;
    incDefault();

    runStep( instr );
}

void PicMrCore::runStep( uint16_t instr )
{
    if( (instr & 0x3F80) == 0 )  // Miscellaneous instrs
    {
        switch( instr & 0x000C)
        {
            case 0x0008: {
                if     ( instr == 0x0008 ) RETURN(); // RETURN 00 0000 0000 1000
                else if( instr == 0x0009 ) RETFIE(); // RETFIE 00 0000 0000 1001
            } return;
            case 0x0000: {
                if     ( instr == 0x0062 ) OPTION(); // OPTION 00 0000 0110 0010
                else if( instr == 0x0063 ) SLEEP();  // SLEEP  00 0000 0110 0011
            } return;
            case 0x0004: {
                if     ( instr == 0x0064 ) CLRWDT(); // CLRWDT 00 0000 0110 0100
            } return;
        }
    }
    else if( (instr & 0x3000) == 0 ) // ALU operations: dest ← OP(f,W)
    {
        uint8_t f = instr & 0x7F;
        uint8_t d = instr>>7 & 1;

        if( (instr & 0x3800) == 0 ) {
            switch( instr & 0x0700) {
                case 0x0000: MOVWF( f );    return; // MOVWF f   00 0000 1fff ffff
                case 0x0100: CLRF( f );     return; // CLR   f   00 0001 1fff ffff
                case 0x0200: SUBWF( f, d ); return; // SUBWF f,d 00 0010 dfff ffff
                case 0x0300: DECF( f, d );  return; // DECF  f,d 00 0011 dfff ffff
                case 0x0400: IORWF( f, d ); return; // IORWF f,d 00 0100 dfff ffff
                case 0x0500: ANDWF( f, d ); return; // ANDWF f,d 00 0101 dfff ffff
                case 0x0600: XORWF( f, d ); return; // XORWF f,d 00 0110 dfff ffff
                case 0x0700: ADDWF( f, d ); return; // ADDWF f,d 00 0111 dfff ffff
           }
        } else {
            switch( instr & 0x0700) {
                case 0x0000: MOVF( f, d );   return; // MOVF   f,d 00 1000 dfff ffff
                case 0x0100: COMF( f, d );   return; // COMF   f,d 00 0001 dfff ffff
                case 0x0200: INCF( f, d );   return; // INCF   f,d 00 0010 dfff ffff
                case 0x0300: DECFSZ( f, d ); return; // DECFSZ f,d 00 0011 dfff ffff
                case 0x0400: RRF( f, d );    return; // RRF    f,d 00 0100 dfff ffff
                case 0x0500: RLF( f, d );    return; // RLF    f,d 00 0101 dfff ffff
                case 0x0600: SWAPF( f, d );  return; // SWAPF  f,d 00 0110 dfff ffff
                case 0x0700: INCFSZ( f, d ); return; // INCFSZ f,d 00 0111 dfff ffff
            }
        }
    } else {
        if( (instr & 0x3000) == 0x1000 ) // Bit operations
        {
            uint8_t f = instr & 0x7F;
            uint8_t b = instr>>7 & 7;

            switch( instr & 0x3C00){
                case 0x1000: BCF( f, b );   return; // BCF   f,b 01 00bb bkkk kkkk
                case 0x1400: BSF( f, b );   return; // BSF   f,b 01 01bb bkkk kkkk
                case 0x1800: BTFSC( f, b ); return; // BTFSC f,b 01 10bb bkkk kkkk
                case 0x1C00: BTFSS( f, b ); return; // BTFSS f,b 01 11bb bkkk kkkk
            }
        }
        else if( (instr & 0x3000) == 0x2000 ) // Control transfers
        {
            uint16_t k = instr & 0x07FF;

            if( (instr & 0x0800) == 0 ) CALL( k ); // CALL k 10 0kkk kkkk kkkk
            else                        GOTO( k ); // GOTO k 10 1kkk kkkk kkkk
        }
        else if( (instr & 0x3000) == 0x3000 ) // Operations with W and 8-bit literal: W ← OP(k,W)
        {
            uint8_t k = instr & 0xFF;

            switch( instr & 0x3C00){
                case 0x3000: MOVLW( k ); return; // MOVLW k 11 00xx kkkk kkkk
                case 0x3400: RETLW( k ); return; // RETLW k 11 01xx kkkk kkkk
                case 0x3800: {
                    switch( instr & 0x3F00) {
                        case 0x3800: IORLW( k ); return; // IORLW k 11 1000 kkkk kkkk
                        case 0x3900: ANDLW( k ); return; // ANDLW k 11 1001 kkkk kkkk
                        case 0x3A00: XORLW( k ); return; // XORLW k 11 1010 kkkk kkkk
                    }
                } return;
                case 0x3C00: {
                    if((instr & 0x0200)==0 ) SUBLW( k ); // SUBLW k 11 110x kkkk kkkk
                    else                     ADDLW( k ); // ADDLW k 11 111x kkkk kkkk
                }
            }
        }
    }
}
