#include "sm83.h"
#include "mem.h"
#include "debug.h"
#include <stdio.h>

namespace sm83
{
    static uint32_t clock = 4194304;
    static uint32_t t = 0;
    static uint16_t current_opcode_address = 0;
    bool options[SM83_NUM_OPTIONS] = { true, false };

    #define f0 0b00000001
    #define f1 0b00000010
    #define f2 0b00000100
    #define f3 0b00001000
    #define fC 0b00010000
    #define fH 0b00100000
    #define fN 0b01000000
    #define fZ 0b10000000

    #define cUnconditional 255
    #define cNZ 0
    #define cZ  1
    #define cNC 2
    #define cC  3

    uint8_t regs[13];

    uint8_t *_rF = &regs[0];
    uint8_t *_rA = &regs[1];
    uint8_t *_rC = &regs[2];
    uint8_t *_rB = &regs[3];
    uint8_t *_rE = &regs[4];
    uint8_t *_rD = &regs[5];
    uint8_t *_rL = &regs[6];
    uint8_t *_rH = &regs[7];

    uint16_t *_rAF = (uint16_t*)&regs[0];
    uint16_t *_rBC = (uint16_t*)&regs[2];
    uint16_t *_rDE = (uint16_t*)&regs[4];
    uint16_t *_rHL = (uint16_t*)&regs[6];

    uint16_t *_rSP = (uint16_t*)&regs[8];
    uint16_t *_rPC = (uint16_t*)&regs[10];

    uint8_t *_rIME = &regs[12];

    bool halted = false;
    bool exit_from_halt = false;
    int pending_ei = 0;
    
    #define rA (*_rA)
    #define rF (*_rF)
    #define rB (*_rB)
    #define rC (*_rC)
    #define rD (*_rD)
    #define rE (*_rE)
    #define rH (*_rH)
    #define rL (*_rL)

    #define rAF (*_rAF)
    #define rBC (*_rBC)
    #define rDE (*_rDE)
    #define rHL (*_rHL)

    #define rSP (*_rSP)
    #define rPC (*_rPC)

    #define ime (*_rIME)

    #define EX(a,b) {auto temp=a;a=b;b=temp;}

    bool reading_m1 = false;

    uint8_t READ_MEM_8(const uint16_t addr, const bool code=false)
    {
        if (debug::isbreak(addr, 2)) debug::stop();
        t+=4;
        
        const uint8_t tag = mem::getTag(addr);
        if ( !(tag&MEMTAG_IGNORE) ) {
            if (!code) {
                if ( tag & MEMTAG_INST ) {
                } else {
                    mem::setTag(addr, tag | MEMTAG_DATA);
                }
            } else {
                if ( (reading_m1) && ( tag & MEMTAG_DATA ) ) {
                }
            }
        }
        reading_m1 = false;
        return mem::readMem(addr);
    }

    uint8_t READ_MEM_8()
    {
        const uint8_t data = READ_MEM_8(rPC, true);
        const uint8_t tag = mem::getTag(rPC);
        if ( !(tag & MEMTAG_IGNORE) ) mem::setTag(rPC, tag | MEMTAG_CODE);
        rPC++;
        return data;
    }

    uint8_t READ_M1()
    {
        reading_m1 = true;
        return READ_MEM_8();
    }

    uint16_t READ_MEM_16()
    {
        const uint8_t L = READ_MEM_8();
        const uint8_t H = READ_MEM_8();
        return (H << 8) + L;
    }

    uint16_t READ_MEM_16(const uint16_t addr)
    {
        return READ_MEM_8(addr) + (READ_MEM_8(addr+1) << 8);
    }

    const uint8_t WRITE_MEM_8(const uint16_t addr, const uint8_t value)
    {
        t+=4;
        mem::writeMem(addr, value);
        
        if (debug::isbreak(addr, 4)) debug::stop();
        debug::setmemmodified(addr);

        const uint8_t tag = mem::getTag(addr);
        if ( !(tag & MEMTAG_IGNORE) ) {
            if ( tag & MEMTAG_INST ) {
                //printf("WARNING! WRITING DATA OVER CODE!!! $%4X\n", addr);
                //z80debug::stop();
            } else {
                mem::setTag(addr, tag | MEMTAG_DATA | MEMTAG_TDATA);
            }
        }
        return value;
    }

    void WRITE_MEM_16(const uint16_t addr, const uint16_t value)
    {
        WRITE_MEM_8(addr, value & 0xff);
        WRITE_MEM_8(addr+1, (value>>8) & 0xff);
    }

    void PUSH(uint16_t b)
    {
        rSP-=2;
        WRITE_MEM_16(rSP, b);
        t+=4;
    }

    void POP(uint16_t *a)
    {
        *a = READ_MEM_16(rSP);
        if (a == _rAF) *a = *a & 0xfff0;
        rSP+=2;
    }

    inline void KEEP_FLAGS(const uint8_t flags)
    {
        rF &= flags;
    }

    inline void SET_FLAGS(const uint8_t flags)
    {
        rF |= flags;
    }

    void INC16(uint16_t *reg)
    {
        t+=4;
        (*reg)++;
    }

    void DEC16(uint16_t *reg)
    {
        t+=4;
        (*reg)--;
    }

    void ADD16(uint16_t* a, uint16_t b)
    {
        t+=4;
        const uint32_t res = *a + b;
        KEEP_FLAGS(fZ);
        if ( ((*a&0xfff)+(b&0xfff))&0x1000 ) SET_FLAGS(fH);
        if ( res > 0xffff ) SET_FLAGS(fC);
        *a = (uint16_t)res;
    }

    void INC8(uint8_t *reg)
    {
        uint8_t value = *reg + 1;

        KEEP_FLAGS(fC);
        if (value==0) SET_FLAGS(fZ);
        if ( (value & 0x0f) == 0x00 ) SET_FLAGS(fH);

        *reg = value;
    }

    void DEC8(uint8_t *reg)
    {
        uint8_t value = *reg - 1;

        KEEP_FLAGS(fC);
        SET_FLAGS(fN);
        if (value==0) SET_FLAGS(fZ);
        if ( (value & 0x0f) == 0x0f ) SET_FLAGS(fH);

        *reg = value;
    }

    void INCMEM8(const uint16_t addr)
    {
        uint8_t value = READ_MEM_8(addr);
        value++;

        KEEP_FLAGS(fC);
        if (value==0) SET_FLAGS(fZ);
        if ( (value & 0x0f) == 0x00 ) SET_FLAGS(fH);

        WRITE_MEM_8(addr, value);
    }

    void DECMEM8(const uint16_t addr)
    {
        uint8_t value = READ_MEM_8(addr);
        value--;

        KEEP_FLAGS(fC);
        SET_FLAGS(fN);
        if (value==0) SET_FLAGS(fZ);
        if ( (value & 0x0f) == 0x0f ) SET_FLAGS(fH);

        WRITE_MEM_8(addr, value);
    }

    void ADD8(uint8_t b)
    {
        const uint8_t res = rA + b;
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        //if ( (res & 0x0f) < (rA & 0x0f) ) SET_FLAGS(fH);
        if ( (rA&0x0f) + (b&0x0f) > 0x0f ) SET_FLAGS(fH);
        if ( res < rA ) SET_FLAGS(fC);
        rA = (uint8_t)res;
    }

    void ADC8(uint8_t b)
    {
        if (!(rF & fC))
            ADD8(b);
        else {
            uint16_t res = rA + b + 1;
            rF=0;
            if (res==0) SET_FLAGS(fZ);
            //if ( (res & 0x0f) <= (rA & 0x0f) ) SET_FLAGS(fH);
            if ( (rA&0x0f) + (b&0x0f) + 1 > 0x0f ) SET_FLAGS(fH);
            if ( res > 255 ) SET_FLAGS(fC);
            rA = (uint8_t)res;
        }
    }

    void SUB8(uint8_t b, const bool update=true)
    {
        const uint8_t res = rA - b;
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        SET_FLAGS(fN);
        //if ( (res & 0x0f) > (rA & 0x0f) ) SET_FLAGS(fH);
        if ( int(rA&0x0f) - int(b&0x0f) < 0 ) SET_FLAGS(fH);
        if ( res > rA ) SET_FLAGS(fC);
        if (update) rA = (uint8_t)res;
    }

    void SBC8(uint8_t b)
    {
        if (!(rF & fC))
            SUB8(b);
        else {
            const uint8_t res = rA - b - 1;
            rF=0;
            if (res==0) SET_FLAGS(fZ);
            SET_FLAGS(fN);
            //if ( (res & 0x0f) >= (rA & 0x0f) ) SET_FLAGS(fH);
            if ( int(rA&0x0f) - int(b&0x0f) - 1 < 0 ) SET_FLAGS(fH);
            if ( res >= rA ) SET_FLAGS(fC);
            rA = (uint8_t)res;
        }
    }

    void AND(uint8_t b)
    {
        rA = rA & b;
        rF=fH;
        if (rA==0) SET_FLAGS(fZ);
    }

    void XOR(uint8_t b)
    {
        rA = rA ^ b;
        rF=0;
        if (rA==0) SET_FLAGS(fZ);
    }

    void OR(uint8_t b)
    {
        rA = rA | b;
        rF=0;
        if (rA==0) SET_FLAGS(fZ);
    }

    void CP(uint8_t b)
    {
        SUB8(b, false);
    }

    void RLCA()
    {
        const bool should_carry = rA & 0x80;
        rA = (rA>>7) | (rA<<1);
        rF = 0;
        if (should_carry) SET_FLAGS(fC);
    }

    void RRCA()
    {
        const bool should_carry = rA & 0x01;
        rA = (rA<<7) | (rA>>1);
        rF = 0;
        if (should_carry) SET_FLAGS(fC);
    }

    void RLA()
    {
        const bool should_carry = rA & 0x80;
        rA = (rA<<1) | ((rF&fC)>>4);
        rF = 0;
        if (should_carry) SET_FLAGS(fC);
    }

    void RRA()
    {
        const bool should_carry = rA & 0x01;
        rA = ((rF&fC)<<3) | (rA>>1);
        rF = 0;
        if (should_carry) SET_FLAGS(fC);
    }

/*
    void new_DAA()
    {
        uint8_t cf = rA > 0x99;
        uint8_t diff = ((rF & fH) || (rA & 0xF) > 9) ? 6 : 0;

        if ((rF & fC) || cf) diff |= 0x60;
        diff = (rF & fN) ? rA - diff : rA + diff;

        KEEP_FLAGS(fN | fC);
        FLAGS_SZXY(diff);
        SET_FLAGS(!(diff) << 6);
        SET_FLAGS((rA ^ diff) & fH);
        SET_PARITY_FLAG(diff);
        SET_FLAGS(cf);
        rA = diff;
        t+=4;
    }
*/
    void oldDAA()
    {
        bool carry_set = false;
        uint8_t old = rA;
        if ( (rA & 0x0F)>9 || (rF & fH) )
        rA += 0x06;
        if ( (rA >> 4)>9 || (rF & fC) ) { rA += 0x60; carry_set = true; }
        KEEP_FLAGS(fN);
        if (rA==0) SET_FLAGS(fZ);
        if ( (rA & 0x0F) < (old & 0x0F) ) SET_FLAGS(fH);
        if (carry_set) SET_FLAGS(fC);
    }

    void notsooldDAA()
    {
        bool carry_set = false;
        bool halfcarry_set = true;
        if (!(rF & fN)) {
            if ((rF & fC) || rA > 0x99) { rA += 0x60; carry_set = true; }
            if ((rF & fH) || ((rA & 0x0f) > 0x09)) { halfcarry_set = false; rA += 0x6; }
        } else if ( (rF & fC) && (rF & fH) ) {
            rA += 0x9A;
            halfcarry_set = false;
        } else if (rF & fC) {
            rA += 0xA0;
        } else if (rF & fH) {
            rA += 0xFA;
            halfcarry_set = false;
        }
        KEEP_FLAGS(fN);
        if (rA==0) SET_FLAGS(fZ);
        if (carry_set) SET_FLAGS(fC);
        //if (halfcarry_set) SET_FLAGS(fH);
        //rF = rF & ~fH;
    }

    void DAA()
    {
        int t = rA;
        int corr = 0;
        corr |= (rF & fH) ? 0x06 : 0x00;
        corr |= (rF & fC) ? 0x60 : 0x00;
        if (rF & fN)
            t -= corr;
        else {
            corr |= (t & 0x0F) > 0x09 ? 0x06 : 0x00;
            corr |= (t > 0x99) ? 0x60 : 0x00;
            t += corr;
        }
        KEEP_FLAGS(fN);
        if (t==0) SET_FLAGS(fZ);
        if (corr & 0x60 != 0) SET_FLAGS(fC);
        t &= 0xFF;
        rA = t;
    }

    void CPL()
    {
        rA = ~rA;
        KEEP_FLAGS(fZ | fC);
        SET_FLAGS(fH | fN);
    }

    void SCF()
    {
        KEEP_FLAGS( fZ );
        SET_FLAGS(fC);
    }

    void CCF()
    {
        const bool carry = (rF & fC);
        KEEP_FLAGS( fZ );
        SET_FLAGS(carry ? fH : fC);
    }

    void JR(uint8_t cond = cUnconditional)
    {
        const int8_t d = (int8_t)READ_MEM_8();
        if (   cond == cUnconditional ||
            ( (cond == cNZ) && !(rF & fZ) ) ||
            ( (cond == cZ)  &&  (rF & fZ) ) ||
            ( (cond == cNC) && !(rF & fC) ) ||
            ( (cond == cC)  &&  (rF & fC) ) )
        {
            rPC = rPC + d;
            t+=4;
        }
    }

    void JP(uint8_t cond, uint16_t addr, const bool fast = false)
    {
        if (   cond == cUnconditional ||
            ( (cond == cNZ) && !(rF & fZ) ) ||
            ( (cond == cZ)  &&  (rF & fZ) ) ||
            ( (cond == cNC) && !(rF & fC) ) ||
            ( (cond == cC)  &&  (rF & fC) )  )
            rPC = addr;
            if (!fast) t+=4;
    }

    void CALL(uint8_t cond, uint16_t addr)
    {
        if (   cond == cUnconditional ||
            ( (cond == cNZ) && !(rF & fZ) ) ||
            ( (cond == cZ)  &&  (rF & fZ) ) ||
            ( (cond == cNC) && !(rF & fC) ) ||
            ( (cond == cC)  &&  (rF & fC) )  )
        {
            PUSH(rPC);
            rPC = addr;

            /*if (options[Z80_OPTION_BREAK_ON_RET]) {
                calls_stacked++;
                printf("CALL: calls stacked: %i", calls_stacked);
            }*/
        }
    }

    void RET(uint8_t cond)
    {
        t+=4;
        if (   cond == cUnconditional ||
            ( (cond == cNZ) && !(rF & fZ) ) ||
            ( (cond == cZ)  &&  (rF & fZ) ) ||
            ( (cond == cNC) && !(rF & fC) ) ||
            ( (cond == cC)  &&  (rF & fC) )  )
        {
            POP(_rPC);
            if (cond != cUnconditional) t += 4;

            /*if (options[Z80_OPTION_BREAK_ON_RET]) {
                if (calls_stacked>0) {
                    calls_stacked--;
                    printf("RET: calls still stacked: %i\n", calls_stacked);
                } else {
                    printf("RET: BREAK\n");
                    options[Z80_OPTION_BREAK_ON_RET] = false;
                    z80debug::setcursor(rPC);
                    z80debug::history::store();
                    z80debug::stop();
                }
            }*/
        }
    }

    void DI();

    void processInterrupts()
    {
        uint8_t *IE = mem::rawHram(0xffff);
        uint8_t *IF = mem::rawHram(0xff0f);
        if ( (*IF & *IE) == 0 ) return;
        if (halted) {
            //exit_from_halt = true;
            halted = false;
            rPC++;
        }
        if (ime==0) return;
        DI();
        PUSH(rPC);
        //t+=20;
        if (*IF & INTERRUPT_VBLANK) {
            rPC = 0x40;
            *IF = *IF & ~INTERRUPT_VBLANK;
        } else if (*IF & INTERRUPT_LCD) {
            rPC = 0x48;
            *IF = *IF & ~INTERRUPT_LCD;
        } else if (*IF & INTERRUPT_TIMER) {
            rPC = 0x50;
            *IF = *IF & ~INTERRUPT_TIMER;
        } else if (*IF & INTERRUPT_SERIAL) {
            rPC = 0x58;
            *IF = *IF & ~INTERRUPT_SERIAL;
        } else if (*IF & INTERRUPT_JOYPAD) {
            rPC = 0x60;
            *IF = *IF & ~INTERRUPT_JOYPAD;
        }
        /*if (options[Z80_OPTION_BREAK_ON_INTERRUPT]) {
            printf("Break on interrupt! 0x%2x, PC: 0x%2x\n", address, rPC);
            z80debug::setcursor(rPC);
            z80debug::history::store();
            z80debug::stop();
        }*/
    }

    void RETI()
    {
        RET(cUnconditional);
        *_rIME = 1;
        processInterrupts();
    }

    void RST(uint8_t vec)
    {
        t-=4; // PUSH i RST duren el mateix en la gameboy, per tant llevem els 4 t que duiem de llegir el opcode
        PUSH(rPC);
        rPC = vec;
    }

    void DI()
    {
        pending_ei = 0; // If interrupts are disabled right after enabling them, they keep disabled
        *_rIME = 0;
    }

    void EI()
    {
        pending_ei = 1; // Enabling interrupts is delayed by one instruction
    }

    void actualEI()
    {
        *_rIME = 1;
        processInterrupts();
    }

    void HALT()
    {
        if (!halted) {
            const uint8_t IE = mem::readMem(0xffff);
            const uint8_t IF = mem::readMem(0xff0f);
            if ( (ime==0) && ((IF & IE) != 0) ) {
                // [TODO] HALT BUG
                if (pending_ei==2) rPC--;
                return;
            } else {
                halted = true;
            }
        }
        /*if (exit_from_halt) {
            exit_from_halt = false;
            halted = false;
        } else {*/
            //printf("HALT\n");
            rPC--;
        //}
    }

    void STOP()
    {
        HALT();
    }

    void interrupt(uint8_t type)
    {
        const uint8_t IE = mem::readMem(0xffff);
        //if (IE & type) exit_from_halt = true;
        const uint8_t IF = mem::readMem(0xff0f);
        mem::writeMem(0xff0f, IF | type);
        //processInterrupts();
    }

    static inline const uint8_t RLC(const uint8_t v)
    {
        const uint8_t res = (v>>7) | (v<<1);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (res&1) SET_FLAGS(fC);
        return res;
    }

    static inline const uint8_t RRC(const uint8_t v)
    {
        const uint8_t res = (v<<7) | (v>>1);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (res&0x80) SET_FLAGS(fC);
        return res;
    }

    static inline const uint8_t RL(const uint8_t v)
    {
        const uint8_t res = (v<<1) | ((rF&fC)>>4);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (v&0x80) SET_FLAGS(fC);
        return res;
    }

    static inline const uint8_t RR(const uint8_t v)
    {
        const uint8_t res = ((rF&fC)<<3) | (v>>1);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (v&1) SET_FLAGS(fC);
        return res;
    }

    static inline const uint8_t SLA(const uint8_t v)
    {
        const uint8_t res = (v<<1);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (v&0x80) SET_FLAGS(fC);
        return res;
    }

    static inline const uint8_t SRA(const uint8_t v)
    {
        const uint8_t res = (v&0x80) | (v>>1);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (v&1) SET_FLAGS(fC);
        return res;
    }

    static inline const uint8_t SWAP(const uint8_t v)
    {
        const uint8_t res = (v<<4) | (v>>4);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        //if (v&0x80) SET_FLAGS(fC); // [TODO] fC reset o calculat?
        return res;
    }

    static inline const uint8_t SRL(const uint8_t v)
    {
        const uint8_t res = (v>>1);
        rF=0;
        if (res==0) SET_FLAGS(fZ);
        if (v&1) SET_FLAGS(fC);
        return res;
    }

    void BIT(uint8_t pos, uint8_t v)
    {
        const uint8_t res = v & 1<<pos;
        KEEP_FLAGS(fC);
        SET_FLAGS(fH);
        if (res==0) SET_FLAGS(fZ);
    }

    static inline const uint8_t SET(uint8_t pos, const uint8_t v)
    {
        return v | (1<<pos);
    }

    static inline const uint8_t RES(uint8_t pos, const uint8_t v)
    {
        return v & ~(1<<pos);
    }

    void LDI_A_HL()
    {
        rA = READ_MEM_8(rHL);
        rHL++;
    }

    void LDI_HL_A()
    {
        WRITE_MEM_8(rHL, rA);
        rHL++;
    }

    void LDD_A_HL()
    {
        rA = READ_MEM_8(rHL);
        rHL--;
    }

    void LDD_HL_A()
    {
        WRITE_MEM_8(rHL, rA);
        rHL--;
    }

    void INVALID(uint8_t opcode)
    {
        printf("INVALID OPCODE AT: %04x\n", current_opcode_address);
        //if (options[Z80_OPTION_STOP_ON_INVALID]) z80debug::stop();
    }

    bool opcode_ignored = false;
    void IgnoreOpcode()
    {
        debug::setcursor(rPC);
        debug::history::store();
        debug::stop();
        /*t-=3;
        rPC--;
        opcode_ignored=true;*/
    }

    void reset()
    {  
        mem::reset();

        rPC = ime = 0;
        rAF = rBC = rDE = rHL = rSP = 0xffff;
        t = 0;
    }

    void setClock(uint32_t freq)
    {
        clock = freq;
    }

    uint32_t getClock()
    {
        return clock;
    }

    void BIT_INSTRUCTIONS();

    uint32_t step()
    {
        do {
            opcode_ignored = false;
            current_opcode_address = rPC;
            t = 0;
            const uint8_t opcode = READ_M1();

            uint8_t tag = mem::getTag(current_opcode_address);
            if ( !(tag & MEMTAG_IGNORE) )
                tag = tag | MEMTAG_INST;
            mem::setTag(current_opcode_address, tag | (!(tag&MEMTAG_TOUCHED) ? MEMTAG_TREPEAT : MEMTAG_TINST) );

            uint16_t tmp;
            
            //if (opcode!=0xED && opcode!=0xCB && opcode!=0xDD && opcode!=0xFD ) z80debug::useOpcode(opcode, 0);

            switch (opcode)
            {
                case 0x00: /* NOP */                        break;
                case 0x01: rBC = READ_MEM_16();             break;
                case 0x02: WRITE_MEM_8(rBC, rA);            break;
                case 0x03: INC16(_rBC);                     break;
                case 0x04: INC8(_rB);                       break;
                case 0x05: DEC8(_rB);                       break;
                case 0x06: rB = READ_MEM_8();               break;
                case 0x07: RLCA();                          break;
                case 0x08: WRITE_MEM_16(READ_MEM_16(), rSP);break;
                case 0x09: ADD16(_rHL, rBC);                break;
                case 0x0A: rA = READ_MEM_8(rBC);            break;
                case 0x0B: DEC16(_rBC);                     break;
                case 0x0C: INC8(_rC);                       break;
                case 0x0D: DEC8(_rC);                       break;
                case 0x0E: rC = READ_MEM_8();               break;
                case 0x0F: RRCA();                          break;

                case 0x10: STOP();                          break;
                case 0x11: rDE = READ_MEM_16();             break;
                case 0x12: WRITE_MEM_8(rDE, rA);            break;
                case 0x13: INC16(_rDE);                     break;
                case 0x14: INC8(_rD);                       break;
                case 0x15: DEC8(_rD);                       break;
                case 0x16: rD = READ_MEM_8();               break;
                case 0x17: RLA();                           break;
                case 0x18: JR();                            break;
                case 0x19: ADD16(_rHL, rDE);                break;
                case 0x1A: rA = READ_MEM_8(rDE);            break;
                case 0x1B: DEC16(_rDE);                     break;
                case 0x1C: INC8(_rE);                       break;
                case 0x1D: DEC8(_rE);                       break;
                case 0x1E: rE = READ_MEM_8();               break;
                case 0x1F: RRA();                           break;

                case 0x20: JR(cNZ);                         break;
                case 0x21: rHL = READ_MEM_16();             break;
                case 0x22: LDI_HL_A();                      break;
                case 0x23: INC16(_rHL);                     break;
                case 0x24: INC8(_rH);                       break;
                case 0x25: DEC8(_rH);                       break;
                case 0x26: rH = READ_MEM_8();               break;
                case 0x27: DAA();                           break;
                case 0x28: JR(cZ);                          break;
                case 0x29: ADD16(_rHL, rHL);                break;
                case 0x2A: LDI_A_HL();                      break;
                case 0x2B: DEC16(_rHL);                     break;
                case 0x2C: INC8(_rL);                       break;
                case 0x2D: DEC8(_rL);                       break;
                case 0x2E: rL = READ_MEM_8();               break;
                case 0x2F: CPL();                           break;

                case 0x30: JR(cNC);                         break;
                case 0x31: rSP = READ_MEM_16();             break;
                case 0x32: LDD_HL_A();                      break;
                case 0x33: INC16(_rSP);                     break;
                case 0x34: INCMEM8(rHL);                    break;
                case 0x35: DECMEM8(rHL);                    break;
                case 0x36: WRITE_MEM_8(rHL, READ_MEM_8());  break;
                case 0x37: SCF();                           break;
                case 0x38: JR(cC);                          break;
                case 0x39: ADD16(_rHL, rSP);                break;
                case 0x3A: LDD_A_HL();                      break;
                case 0x3B: DEC16(_rSP);                     break;
                case 0x3C: INC8(_rA);                       break;
                case 0x3D: DEC8(_rA);                       break;
                case 0x3E: rA = READ_MEM_8();               break;
                case 0x3F: CCF();                           break;

                case 0x40: rB = rB;                         break;
                case 0x41: rB = rC;                         break;
                case 0x42: rB = rD;                         break;
                case 0x43: rB = rE;                         break;
                case 0x44: rB = rH;                         break;
                case 0x45: rB = rL;                         break;
                case 0x46: rB = READ_MEM_8(rHL);            break;
                case 0x47: rB = rA;                         break;
                case 0x48: rC = rB;                         break;
                case 0x49: rC = rC;                         break;
                case 0x4A: rC = rD;                         break;
                case 0x4B: rC = rE;                         break;
                case 0x4C: rC = rH;                         break;
                case 0x4D: rC = rL;                         break;
                case 0x4E: rC = READ_MEM_8(rHL);            break;
                case 0x4F: rC = rA;                         break;

                case 0x50: rD = rB;                         break;
                case 0x51: rD = rC;                         break;
                case 0x52: rD = rD;                         break;
                case 0x53: rD = rE;                         break;
                case 0x54: rD = rH;                         break;
                case 0x55: rD = rL;                         break;
                case 0x56: rD = READ_MEM_8(rHL);            break;
                case 0x57: rD = rA;                         break;
                case 0x58: rE = rB;                         break;
                case 0x59: rE = rC;                         break;
                case 0x5A: rE = rD;                         break;
                case 0x5B: rE = rE;                         break;
                case 0x5C: rE = rH;                         break;
                case 0x5D: rE = rL;                         break;
                case 0x5E: rE = READ_MEM_8(rHL);            break;
                case 0x5F: rE = rA;                         break;
                
                case 0x60: rH = rB;                         break;
                case 0x61: rH = rC;                         break;
                case 0x62: rH = rD;                         break;
                case 0x63: rH = rE;                         break;
                case 0x64: rH = rH;                         break;
                case 0x65: rH = rL;                         break;
                case 0x66: rH = READ_MEM_8(rHL);            break;
                case 0x67: rH = rA;                         break;
                case 0x68: rL = rB;                         break;
                case 0x69: rL = rC;                         break;
                case 0x6A: rL = rD;                         break;
                case 0x6B: rL = rE;                         break;
                case 0x6C: rL = rH;                         break;
                case 0x6D: rL = rL;                         break;
                case 0x6E: rL = READ_MEM_8(rHL);            break;
                case 0x6F: rL = rA;                         break;
                
                case 0x70: WRITE_MEM_8(rHL, rB);            break;
                case 0x71: WRITE_MEM_8(rHL, rC);            break;
                case 0x72: WRITE_MEM_8(rHL, rD);            break;
                case 0x73: WRITE_MEM_8(rHL, rE);            break;
                case 0x74: WRITE_MEM_8(rHL, rH);            break;
                case 0x75: WRITE_MEM_8(rHL, rL);            break;
                case 0x76: HALT();                          break;
                case 0x77: WRITE_MEM_8(rHL, rA);            break;
                case 0x78: rA = rB;                         break;
                case 0x79: rA = rC;                         break;
                case 0x7A: rA = rD;                         break;
                case 0x7B: rA = rE;                         break;
                case 0x7C: rA = rH;                         break;
                case 0x7D: rA = rL;                         break;
                case 0x7E: rA = READ_MEM_8(rHL);            break;
                case 0x7F: rA = rA;                         break;
                
                case 0x80: ADD8(rB);                        break;
                case 0x81: ADD8(rC);                        break;
                case 0x82: ADD8(rD);                        break;
                case 0x83: ADD8(rE);                        break;
                case 0x84: ADD8(rH);                        break;
                case 0x85: ADD8(rL);                        break;
                case 0x86: ADD8(READ_MEM_8(rHL));           break;
                case 0x87: ADD8(rA);                        break;
                case 0x88: ADC8(rB);                        break;
                case 0x89: ADC8(rC);                        break;
                case 0x8A: ADC8(rD);                        break;
                case 0x8B: ADC8(rE);                        break;
                case 0x8C: ADC8(rH);                        break;
                case 0x8D: ADC8(rL);                        break;
                case 0x8E: ADC8(READ_MEM_8(rHL));           break;
                case 0x8F: ADC8(rA);                        break;

                case 0x90: SUB8(rB);                        break;
                case 0x91: SUB8(rC);                        break;
                case 0x92: SUB8(rD);                        break;
                case 0x93: SUB8(rE);                        break;
                case 0x94: SUB8(rH);                        break;
                case 0x95: SUB8(rL);                        break;
                case 0x96: SUB8(READ_MEM_8(rHL));           break;
                case 0x97: SUB8(rA);                        break;
                case 0x98: SBC8(rB);                        break;
                case 0x99: SBC8(rC);                        break;
                case 0x9A: SBC8(rD);                        break;
                case 0x9B: SBC8(rE);                        break;
                case 0x9C: SBC8(rH);                        break;
                case 0x9D: SBC8(rL);                        break;
                case 0x9E: SBC8(READ_MEM_8(rHL));           break;
                case 0x9F: SBC8(rA);                        break;

                case 0xA0: AND(rB);                         break;
                case 0xA1: AND(rC);                         break;
                case 0xA2: AND(rD);                         break;
                case 0xA3: AND(rE);                         break;
                case 0xA4: AND(rH);                         break;
                case 0xA5: AND(rL);                         break;
                case 0xA6: AND(READ_MEM_8(rHL));            break;
                case 0xA7: AND(rA);                         break;
                case 0xA8: XOR(rB);                         break;
                case 0xA9: XOR(rC);                         break;
                case 0xAA: XOR(rD);                         break;
                case 0xAB: XOR(rE);                         break;
                case 0xAC: XOR(rH);                         break;
                case 0xAD: XOR(rL);                         break;
                case 0xAE: XOR(READ_MEM_8(rHL));            break;
                case 0xAF: XOR(rA);                         break;

                case 0xB0: OR(rB);                          break;
                case 0xB1: OR(rC);                          break;
                case 0xB2: OR(rD);                          break;
                case 0xB3: OR(rE);                          break;
                case 0xB4: OR(rH);                          break;
                case 0xB5: OR(rL);                          break;
                case 0xB6: OR(READ_MEM_8(rHL));             break;
                case 0xB7: OR(rA);                          break;
                case 0xB8: CP(rB);                          break;
                case 0xB9: CP(rC);                          break;
                case 0xBA: CP(rD);                          break;
                case 0xBB: CP(rE);                          break;
                case 0xBC: CP(rH);                          break;
                case 0xBD: CP(rL);                          break;
                case 0xBE: CP(READ_MEM_8(rHL));             break;
                case 0xBF: CP(rA);                          break;

                case 0xC0: RET(cNZ);                        break;
                case 0xC1: POP(_rBC);                       break;
                case 0xC2: JP(cNZ, READ_MEM_16());          break;
                case 0xC3: JP(cUnconditional, READ_MEM_16());break;
                case 0xC4: CALL(cNZ, READ_MEM_16());        break;
                case 0xC5: PUSH(rBC);                       break;
                case 0xC6: ADD8(READ_MEM_8());              break;
                case 0xC7: RST(0x00);                       break;
                case 0xC8: RET(cZ);                         break;
                case 0xC9: RET(cUnconditional);             break;
                case 0xCA: JP(cZ, READ_MEM_16());           break;
                case 0xCB: BIT_INSTRUCTIONS();              break;
                case 0xCC: CALL(cZ, READ_MEM_16());         break;
                case 0xCD: CALL(cUnconditional, READ_MEM_16());break;
                case 0xCE: ADC8(READ_MEM_8());              break;
                case 0xCF: RST(0x08);                       break;

                case 0xD0: RET(cNC);                        break;
                case 0xD1: POP(_rDE);                       break;
                case 0xD2: JP(cNC, READ_MEM_16());          break;
                case 0xD3: IgnoreOpcode();                  break;
                case 0xD4: CALL(cNC, READ_MEM_16());        break;
                case 0xD5: PUSH(rDE);                       break;
                case 0xD6: SUB8(READ_MEM_8());              break;
                case 0xD7: RST(0x10);                       break;
                case 0xD8: RET(cC);                         break;
                case 0xD9: RETI();                          break;
                case 0xDA: JP(cC, READ_MEM_16());           break;
                case 0xDB: IgnoreOpcode();                  break;
                case 0xDC: CALL(cC, READ_MEM_16());         break;
                case 0xDD: IgnoreOpcode();                  break;
                case 0xDE: SBC8(READ_MEM_8());              break;
                case 0xDF: RST(0x18);                       break;

                case 0xE0: WRITE_MEM_8(READ_MEM_8()|0xff00, rA);break;
                case 0xE1: POP(_rHL);                       break;
                case 0xE2: WRITE_MEM_8(rC|0xff00, rA);      break;
                case 0xE3: IgnoreOpcode();                  break;
                case 0xE4: IgnoreOpcode();                  break;
                case 0xE5: PUSH(rHL);                       break;
                case 0xE6: AND(READ_MEM_8());               break;
                case 0xE7: RST(0x20);                       break;
                case 0xE8: ADD16(_rSP, (int8_t)READ_MEM_8());break; /*[TODO] es correcte? ha de ser signed*/
                case 0xE9: JP(cUnconditional, rHL, true);   break;
                case 0xEA: WRITE_MEM_8(READ_MEM_16(), rA);  break;
                case 0xEB: IgnoreOpcode();                  break;
                case 0xEC: IgnoreOpcode();                  break;
                case 0xED: IgnoreOpcode();                  break;
                case 0xEE: XOR(READ_MEM_8());               break;
                case 0xEF: RST(0x28);                       break;

                case 0xF0: rA = READ_MEM_8(READ_MEM_8()|0xff00);break;
                case 0xF1: POP(_rAF);                       break;
                case 0xF2: rA = READ_MEM_8(rC|0xff00);      break;
                case 0xF3: DI();                            break;
                case 0xF4: IgnoreOpcode();                  break;
                case 0xF5: PUSH(rAF);                       break;
                case 0xF6: OR(READ_MEM_8());                break;
                case 0xF7: RST(0x30);                       break;
                case 0xF8: rHL = rSP + (int8_t)READ_MEM_8();break; /*[TODO] es correcte? ha de ser signed*/
                case 0xF9: rSP = rHL; t+=2;                 break;
                case 0xFA: rA = READ_MEM_8(READ_MEM_16());  break;
                case 0xFB: EI();                            break;
                case 0xFC: IgnoreOpcode();                  break;
                case 0xFD: IgnoreOpcode();                  break;
                case 0xFE: CP(READ_MEM_8());                break;
                case 0xFF: RST(0x38);                       break;
            }

        } while (opcode_ignored);

        if (pending_ei==2) { pending_ei=0; actualEI(); }
        if (pending_ei==1) pending_ei=2;

        debug::setcursor(rPC);
        debug::history::store();
        mem::update_mapped(t);
        return t;
    }

    void BIT_INSTRUCTIONS()
    {
        const uint8_t opcode = READ_M1();

        //z80debug::useOpcode(opcode, 2);

        switch (opcode)
        {
            case 0x00: rB = RLC(rB);                            break;
            case 0x01: rC = RLC(rC);                            break;
            case 0x02: rD = RLC(rD);                            break;
            case 0x03: rE = RLC(rE);                            break;
            case 0x04: rH = RLC(rH);                            break;
            case 0x05: rL = RLC(rL);                            break;
            case 0x06: WRITE_MEM_8(rHL, RLC(READ_MEM_8(rHL)));  break;
            case 0x07: rA = RLC(rA);                            break;
            case 0x08: rB = RRC(rB);                            break;
            case 0x09: rC = RRC(rC);                            break;
            case 0x0A: rD = RRC(rD);                            break;
            case 0x0B: rE = RRC(rE);                            break;
            case 0x0C: rH = RRC(rH);                            break;
            case 0x0D: rL = RRC(rL);                            break;
            case 0x0E: WRITE_MEM_8(rHL, RRC(READ_MEM_8(rHL)));  break;
            case 0x0F: rA = RRC(rA);                            break;

            case 0x10: rB = RL(rB);                             break;
            case 0x11: rC = RL(rC);                             break;
            case 0x12: rD = RL(rD);                             break;
            case 0x13: rE = RL(rE);                             break;
            case 0x14: rH = RL(rH);                             break;
            case 0x15: rL = RL(rL);                             break;
            case 0x16: WRITE_MEM_8(rHL, RL(READ_MEM_8(rHL)));   break;
            case 0x17: rA = RL(rA);                             break;
            case 0x18: rB = RR(rB);                             break;
            case 0x19: rC = RR(rC);                             break;
            case 0x1A: rD = RR(rD);                             break;
            case 0x1B: rE = RR(rE);                             break;
            case 0x1C: rH = RR(rH);                             break;
            case 0x1D: rL = RR(rL);                             break;
            case 0x1E: WRITE_MEM_8(rHL, RR(READ_MEM_8(rHL)));   break;
            case 0x1F: rA = RR(rA);                             break;

            case 0x20: rB = SLA(rB);                            break;
            case 0x21: rC = SLA(rC);                            break;
            case 0x22: rD = SLA(rD);                            break;
            case 0x23: rE = SLA(rE);                            break;
            case 0x24: rH = SLA(rH);                            break;
            case 0x25: rL = SLA(rL);                            break;
            case 0x26: WRITE_MEM_8(rHL, SLA(READ_MEM_8(rHL)));  break;
            case 0x27: rA = SLA(rA);                            break;
            case 0x28: rB = SRA(rB);                            break;
            case 0x29: rC = SRA(rC);                            break;
            case 0x2A: rD = SRA(rD);                            break;
            case 0x2B: rE = SRA(rE);                            break;
            case 0x2C: rH = SRA(rH);                            break;
            case 0x2D: rL = SRA(rL);                            break;
            case 0x2E: WRITE_MEM_8(rHL, SRA(READ_MEM_8(rHL)));  break;
            case 0x2F: rA = SRA(rA);                            break;

            case 0x30: rB = SWAP(rB);                           break;
            case 0x31: rC = SWAP(rC);                           break;
            case 0x32: rD = SWAP(rD);                           break;
            case 0x33: rE = SWAP(rE);                           break;
            case 0x34: rH = SWAP(rH);                           break;
            case 0x35: rL = SWAP(rL);                           break;
            case 0x36: WRITE_MEM_8(rHL, SWAP(READ_MEM_8(rHL))); break;
            case 0x37: rA = SWAP(rA);                           break;
            case 0x38: rB = SRL(rB);                            break;
            case 0x39: rC = SRL(rC);                            break;
            case 0x3A: rD = SRL(rD);                            break;
            case 0x3B: rE = SRL(rE);                            break;
            case 0x3C: rH = SRL(rH);                            break;
            case 0x3D: rL = SRL(rL);                            break;
            case 0x3E: WRITE_MEM_8(rHL, SRL(READ_MEM_8(rHL)));  break;
            case 0x3F: rA = SRL(rA);                            break;

            case 0x40: BIT(0, rB);                              break;
            case 0x41: BIT(0, rC);                              break;
            case 0x42: BIT(0, rD);                              break;
            case 0x43: BIT(0, rE);                              break;
            case 0x44: BIT(0, rH);                              break;
            case 0x45: BIT(0, rL);                              break;
            case 0x46: BIT(0, READ_MEM_8(rHL));                 break;
            case 0x47: BIT(0, rA);                              break;
            case 0x48: BIT(1, rB);                              break;
            case 0x49: BIT(1, rC);                              break;
            case 0x4A: BIT(1, rD);                              break;
            case 0x4B: BIT(1, rE);                              break;
            case 0x4C: BIT(1, rH);                              break;
            case 0x4D: BIT(1, rL);                              break;
            case 0x4E: BIT(1, READ_MEM_8(rHL));                 break;
            case 0x4F: BIT(1, rA);                              break;

            case 0x50: BIT(2, rB);                              break;
            case 0x51: BIT(2, rC);                              break;
            case 0x52: BIT(2, rD);                              break;
            case 0x53: BIT(2, rE);                              break;
            case 0x54: BIT(2, rH);                              break;
            case 0x55: BIT(2, rL);                              break;
            case 0x56: BIT(2, READ_MEM_8(rHL));                 break;
            case 0x57: BIT(2, rA);                              break;
            case 0x58: BIT(3, rB);                              break;
            case 0x59: BIT(3, rC);                              break;
            case 0x5A: BIT(3, rD);                              break;
            case 0x5B: BIT(3, rE);                              break;
            case 0x5C: BIT(3, rH);                              break;
            case 0x5D: BIT(3, rL);                              break;
            case 0x5E: BIT(3, READ_MEM_8(rHL));                 break;
            case 0x5F: BIT(3, rA);                              break;

            case 0x60: BIT(4, rB);                              break;
            case 0x61: BIT(4, rC);                              break;
            case 0x62: BIT(4, rD);                              break;
            case 0x63: BIT(4, rE);                              break;
            case 0x64: BIT(4, rH);                              break;
            case 0x65: BIT(4, rL);                              break;
            case 0x66: BIT(4, READ_MEM_8(rHL));                 break;
            case 0x67: BIT(4, rA);                              break;
            case 0x68: BIT(5, rB);                              break;
            case 0x69: BIT(5, rC);                              break;
            case 0x6A: BIT(5, rD);                              break;
            case 0x6B: BIT(5, rE);                              break;
            case 0x6C: BIT(5, rH);                              break;
            case 0x6D: BIT(5, rL);                              break;
            case 0x6E: BIT(5, READ_MEM_8(rHL));                 break;
            case 0x6F: BIT(5, rA);                              break;

            case 0x70: BIT(6, rB);                              break;
            case 0x71: BIT(6, rC);                              break;
            case 0x72: BIT(6, rD);                              break;
            case 0x73: BIT(6, rE);                              break;
            case 0x74: BIT(6, rH);                              break;
            case 0x75: BIT(6, rL);                              break;
            case 0x76: BIT(6, READ_MEM_8(rHL));                 break;
            case 0x77: BIT(6, rA);                              break;
            case 0x78: BIT(7, rB);                              break;
            case 0x79: BIT(7, rC);                              break;
            case 0x7A: BIT(7, rD);                              break;
            case 0x7B: BIT(7, rE);                              break;
            case 0x7C: BIT(7, rH);                              break;
            case 0x7D: BIT(7, rL);                              break;
            case 0x7E: BIT(7, READ_MEM_8(rHL));                 break;
            case 0x7F: BIT(7, rA);                              break;

            case 0x80: rB = RES(0, rB);                         break;
            case 0x81: rC = RES(0, rC);                         break;
            case 0x82: rD = RES(0, rD);                         break;
            case 0x83: rE = RES(0, rE);                         break;
            case 0x84: rH = RES(0, rH);                         break;
            case 0x85: rL = RES(0, rL);                         break;
            case 0x86: WRITE_MEM_8(rHL, RES(0, READ_MEM_8(rHL)));break;
            case 0x87: rA = RES(0, rA);                         break;
            case 0x88: rB = RES(1, rB);                         break;
            case 0x89: rC = RES(1, rC);                         break;
            case 0x8A: rD = RES(1, rD);                         break;
            case 0x8B: rE = RES(1, rE);                         break;
            case 0x8C: rH = RES(1, rH);                         break;
            case 0x8D: rL = RES(1, rL);                         break;
            case 0x8E: WRITE_MEM_8(rHL, RES(1, READ_MEM_8(rHL)));break;
            case 0x8F: rA = RES(1, rA);                         break;

            case 0x90: rB = RES(2, rB);                         break;
            case 0x91: rC = RES(2, rC);                         break;
            case 0x92: rD = RES(2, rD);                         break;
            case 0x93: rE = RES(2, rE);                         break;
            case 0x94: rH = RES(2, rH);                         break;
            case 0x95: rL = RES(2, rL);                         break;
            case 0x96: WRITE_MEM_8(rHL, RES(2, READ_MEM_8(rHL)));break;
            case 0x97: rA = RES(2, rA);                         break;
            case 0x98: rB = RES(3, rB);                         break;
            case 0x99: rC = RES(3, rC);                         break;
            case 0x9A: rD = RES(3, rD);                         break;
            case 0x9B: rE = RES(3, rE);                         break;
            case 0x9C: rH = RES(3, rH);                         break;
            case 0x9D: rL = RES(3, rL);                         break;
            case 0x9E: WRITE_MEM_8(rHL, RES(3, READ_MEM_8(rHL)));break;
            case 0x9F: rA = RES(3, rA);                         break;

            case 0xA0: rB = RES(4, rB);                         break;
            case 0xA1: rC = RES(4, rC);                         break;
            case 0xA2: rD = RES(4, rD);                         break;
            case 0xA3: rE = RES(4, rE);                         break;
            case 0xA4: rH = RES(4, rH);                         break;
            case 0xA5: rL = RES(4, rL);                         break;
            case 0xA6: WRITE_MEM_8(rHL, RES(4, READ_MEM_8(rHL)));break;
            case 0xA7: rA = RES(4, rA);                         break;
            case 0xA8: rB = RES(5, rB);                         break;
            case 0xA9: rC = RES(5, rC);                         break;
            case 0xAA: rD = RES(5, rD);                         break;
            case 0xAB: rE = RES(5, rE);                         break;
            case 0xAC: rH = RES(5, rH);                         break;
            case 0xAD: rL = RES(5, rL);                         break;
            case 0xAE: WRITE_MEM_8(rHL, RES(5, READ_MEM_8(rHL)));break;
            case 0xAF: rA = RES(5, rA);                         break;

            case 0xB0: rB = RES(6, rB);                         break;
            case 0xB1: rC = RES(6, rC);                         break;
            case 0xB2: rD = RES(6, rD);                         break;
            case 0xB3: rE = RES(6, rE);                         break;
            case 0xB4: rH = RES(6, rH);                         break;
            case 0xB5: rL = RES(6, rL);                         break;
            case 0xB6: WRITE_MEM_8(rHL, RES(6, READ_MEM_8(rHL)));break;
            case 0xB7: rA = RES(6, rA);                         break;
            case 0xB8: rB = RES(7, rB);                         break;
            case 0xB9: rC = RES(7, rC);                         break;
            case 0xBA: rD = RES(7, rD);                         break;
            case 0xBB: rE = RES(7, rE);                         break;
            case 0xBC: rH = RES(7, rH);                         break;
            case 0xBD: rL = RES(7, rL);                         break;
            case 0xBE: WRITE_MEM_8(rHL, RES(7, READ_MEM_8(rHL)));break;
            case 0xBF: rA = RES(7, rA);                         break;

            case 0xC0: rB = SET(0, rB);                         break;
            case 0xC1: rC = SET(0, rC);                         break;
            case 0xC2: rD = SET(0, rD);                         break;
            case 0xC3: rE = SET(0, rE);                         break;
            case 0xC4: rH = SET(0, rH);                         break;
            case 0xC5: rL = SET(0, rL);                         break;
            case 0xC6: WRITE_MEM_8(rHL, SET(0, READ_MEM_8(rHL)));break;
            case 0xC7: rA = SET(0, rA);                         break;
            case 0xC8: rB = SET(1, rB);                         break;
            case 0xC9: rC = SET(1, rC);                         break;
            case 0xCA: rD = SET(1, rD);                         break;
            case 0xCB: rE = SET(1, rE);                         break;
            case 0xCC: rH = SET(1, rH);                         break;
            case 0xCD: rL = SET(1, rL);                         break;
            case 0xCE: WRITE_MEM_8(rHL, SET(1, READ_MEM_8(rHL)));break;
            case 0xCF: rA = SET(1, rA);                         break;

            case 0xD0: rB = SET(2, rB);                         break;
            case 0xD1: rC = SET(2, rC);                         break;
            case 0xD2: rD = SET(2, rD);                         break;
            case 0xD3: rE = SET(2, rE);                         break;
            case 0xD4: rH = SET(2, rH);                         break;
            case 0xD5: rL = SET(2, rL);                         break;
            case 0xD6: WRITE_MEM_8(rHL, SET(2, READ_MEM_8(rHL)));break;
            case 0xD7: rA = SET(2, rA);                         break;
            case 0xD8: rB = SET(3, rB);                         break;
            case 0xD9: rC = SET(3, rC);                         break;
            case 0xDA: rD = SET(3, rD);                         break;
            case 0xDB: rE = SET(3, rE);                         break;
            case 0xDC: rH = SET(3, rH);                         break;
            case 0xDD: rL = SET(3, rL);                         break;
            case 0xDE: WRITE_MEM_8(rHL, SET(3, READ_MEM_8(rHL)));break;
            case 0xDF: rA = SET(3, rA);                         break;

            case 0xE0: rB = SET(4, rB);                         break;
            case 0xE1: rC = SET(4, rC);                         break;
            case 0xE2: rD = SET(4, rD);                         break;
            case 0xE3: rE = SET(4, rE);                         break;
            case 0xE4: rH = SET(4, rH);                         break;
            case 0xE5: rL = SET(4, rL);                         break;
            case 0xE6: WRITE_MEM_8(rHL, SET(4, READ_MEM_8(rHL)));break;
            case 0xE7: rA = SET(4, rA);                         break;
            case 0xE8: rB = SET(5, rB);                         break;
            case 0xE9: rC = SET(5, rC);                         break;
            case 0xEA: rD = SET(5, rD);                         break;
            case 0xEB: rE = SET(5, rE);                         break;
            case 0xEC: rH = SET(5, rH);                         break;
            case 0xED: rL = SET(5, rL);                         break;
            case 0xEE: WRITE_MEM_8(rHL, SET(5, READ_MEM_8(rHL)));break;
            case 0xEF: rA = SET(5, rA);                         break;

            case 0xF0: rB = SET(6, rB);                         break;
            case 0xF1: rC = SET(6, rC);                         break;
            case 0xF2: rD = SET(6, rD);                         break;
            case 0xF3: rE = SET(6, rE);                         break;
            case 0xF4: rH = SET(6, rH);                         break;
            case 0xF5: rL = SET(6, rL);                         break;
            case 0xF6: WRITE_MEM_8(rHL, SET(6, READ_MEM_8(rHL)));break;
            case 0xF7: rA = SET(6, rA);                         break;
            case 0xF8: rB = SET(7, rB);                         break;
            case 0xF9: rC = SET(7, rC);                         break;
            case 0xFA: rD = SET(7, rD);                         break;
            case 0xFB: rE = SET(7, rE);                         break;
            case 0xFC: rH = SET(7, rH);                         break;
            case 0xFD: rL = SET(7, rL);                         break;
            case 0xFE: WRITE_MEM_8(rHL, SET(7, READ_MEM_8(rHL)));break;
            case 0xFF: rA = SET(7, rA);                         break;
        }
    }

    //uint8_t *getMem() { return memory; }
    uint8_t *getRegs() { return regs; }

    uint16_t getAF() { return rAF; }
    uint16_t getBC() { return rBC; }
    uint16_t getDE() { return rDE; }
    uint16_t getHL() { return rHL; }

    uint16_t getSP() { return rSP; }

    uint16_t getPC() { return rPC; }

    void setPC(const uint16_t addr) { rPC = addr; }


    const bool getOption(const int option)
    {
        return options[option];
    }

    void setOption(const int option, const bool value)
    {
        options[option] = value;
    }

    void toggleOption(const int option)
    {
        options[option] = !options[option];
    }
}