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

#ifndef MCS65CPU_H
#define MCS65CPU_H

#include "mcs65interface.h"
#include "iopin.h"

#define CONSTANT  0x20
#define BREAK     0x10

#define SET_NEGATIVE(x)  write_S_Bit( N, x & 0x80 ) //(x ? (m_STATUS |= NEGATIVE) : (m_STATUS &= (~NEGATIVE)) )
#define SET_OVERFLOW(x)  write_S_Bit( V, x ) //(x ? (m_STATUS |= OVERFLOW) : (m_STATUS &= (~OVERFLOW)) )
#define SET_DECIMAL(x)   write_S_Bit( D, x ) //(x ? (m_STATUS |= DECIMAL) : (m_STATUS &= (~DECIMAL)) )
#define SET_INTERRUPT(x) write_S_Bit( I, x ) //(x ? (m_STATUS |= INTERRUPT) : (m_STATUS &= (~INTERRUPT)) )
#define SET_ZERO(x)      write_S_Bit( Z, x ) //(x ? (m_STATUS |= ZERO) : (m_STATUS &= (~ZERO)) )
#define SET_CARRY(x)     write_S_Bit( C, x ) //(x ? (m_STATUS |= CARRY) : (m_STATUS &= (~CARRY)) )

class IoPort;
class IoPin;

class Mcs65Cpu : public Mcs65Interface
{
    public:
        Mcs65Cpu( eMcu* mcu );
        ~Mcs65Cpu();

        int     getIntReg( QString reg ) override;
        QString getStrReg( QString reg ) override;

        void stamp() override;
        void runEvent() override;

        void reset() override;
        void runStep() override;
        void extClock( bool clkState ) override;

        uint getPC() override { return m_debugPC; }

        enum { C=0,Z,I,D,B,O,V,N }; // STATUS bits

        enum cpuState_t{
            cRESET=0,
            cFETCH,
            cDECODE,
            cREAD,
            cEXEC,
            cWRITE
        };
        enum addrMode_t{
            aNONE=0,
            aACCU,
            aIMME,
            aABSO,
            aZERO,
            aZEDX,
            aINDI,
        };
        enum addrFlags_t{
            iX=1<<0,       // X indexed
            iY=1<<1,       // Y indexed
            iC=1<<2        // Page Cross carry
        };

    protected:
        inline void clkRisingEdge();
        inline void clkFallingEdge();
        inline void resetSeq();
        inline void decode();

        inline void readPGM();
        inline void Read();
        inline void readMem( uint16_t addr );
        inline uint8_t readDataBus();

        inline void writeMem( uint16_t addr );

        uint32_t m_debugPC; // PC for dDebugger

        bool m_nextClock;
        bool m_halt;

        bool m_nmiState;
        int8_t m_isrSource;

        //uint64_t m_psStep;
        int m_cycle;

        // Buses
        IoPort* m_dataBus;
        IoPort* m_addrBus;
        IoPin* m_rwPin;

        // Control Pins
        IoPort* m_ctrlPort;
        IoPin* m_phi0Pin;
        IoPin* m_phi1Pin;
        IoPin* m_phi2Pin;
        IoPin* m_syncPin;

        // Interrupt Pins
        IoPin* m_irqPin;
        IoPin* m_nmiPin;

        // User Pins
        IoPin* m_rdyPin;
        IoPin* m_soPin;
        IoPin* m_dbePin;

        QMap<QString, uint8_t*> m_cpuRegs;

        uint8_t m_P;   // status
        uint8_t m_SP;
        uint8_t m_Ac;
        uint8_t m_IR;
        uint8_t m_rX;
        uint8_t m_rY;
        //uint8_t* m_rI;

        cpuState_t m_state;
        cpuState_t m_nextState;

        uint16_t m_Isr; // Interrupt vector

        addrMode_t m_aMode;
        uint8_t m_aFlags;

        uint8_t m_u8Tmp0;
        uint8_t m_u8Tmp1;
        uint8_t m_op0;
        uint16_t m_opAddr;

        uint16_t m_busAddr;
        pinMode_t m_dataMode;

        // Timing
        uint64_t m_tHR = 1000*10; // 10 ns Read Data Hold Time: Time to release Data Bus
        uint64_t m_tHA = 1000*25; // 25 ns Address delay Time:  Time to set Address Bus
        uint64_t m_tHW = 1000*20; // 20 ns Write delay Time:    Time to set RW, Address, Sync;

        typedef void (Mcs65Cpu::* funcPtr_t)();
        funcPtr_t m_EXEC;   // Pointer to function execute instruction

        inline void setNZ( uint8_t val );
        inline void writeWflags( bool c );

        inline void pushStack8( uint8_t byte );
        inline void popStack8();

        inline void ADC();
        inline void AND();
        inline void ASL();
        inline void BIT();
        inline void BRK();
        inline void CLC();
        inline void CLD();
        inline void CLI();
        inline void CLV();
        inline void CMP();
        inline void CPX();
        inline void CPY();
        inline void DEC();
        inline void DEX();
        inline void DEY();
        inline void EOR();
        inline void INC();
        inline void INX();
        inline void INY();
        inline void JMP();
        inline void JSR();
        inline void LDA();
        inline void LDX();
        inline void LDY();
        inline void LSR();
        inline void NOP();
        inline void ORA();
        inline void PHA();
        inline void PHP();
        inline void PLA();
        inline void PLP();
        inline void ROL();
        inline void ROR();
        inline void RTI();
        inline void RTS();
        inline void SBC();
        inline void SEC();
        inline void SED();
        inline void SEI();
        inline void STA();
        inline void STX();
        inline void STY();
        inline void TAX();
        inline void TAY();
        inline void TSX();
        inline void TXA();
        inline void TXS();
        inline void TYA();
        inline void BXX();
};
#endif
