← index
ats/system/instructions.cpp
Source: ats/system/instructions.cpp
#include "code.hpp"
#include "translate.hpp"
#include <bitset>

namespace interp {

    void trackMemoryWrite(uint16_t address, uint8_t value) {
        modifiedMemoryAddresses.push_back(std::make_pair(address, value));
    }

    void updateZNFlags(icode::Processor &proc, uint8_t value) {
        proc.setFlag(icode::FLAG_ZERO, (value == 0));
        proc.setFlag(icode::FLAG_NEGATIVE, ((value & 0x80) != 0));
    }

    void i_adc(Code &c) {
        int in = c.proc.getIp();
        uint8_t carry = c.proc.getFlag(icode::FLAG_CARRY);

        switch (c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY: {
                uint8_t val = 0;
                switch (c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        val = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        val = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t effective_addr = base_addr + c.proc.reg_y;

                        val = c.peek(effective_addr);
                    }
                    break;
                    case INDEXED_I: { // (address,X)
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t indexed_addr = (zp_addr + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(indexed_addr);
                        uint8_t high = c.peek((indexed_addr + 1) & 0xFF);
                        uint16_t effective_addr = (high << 8) | low;

                        val = c.peek(effective_addr);
                    }
                    break;
                    default:
                        return;
                }
                uint16_t total = c.proc.reg_a + val + carry;
                c.proc.setFlag(icode::FLAG_CARRY, (total > 0xFF));

                bool A7 = (c.proc.reg_a & 0x80);
                bool M7 = (val & 0x80);
                bool R7 = ((static_cast<uint8_t>(total) & 0x80) != 0);
                c.proc.setFlag(icode::FLAG_OVERFLOW, ((A7 == M7) && (A7 != R7)));

                c.proc.reg_a = static_cast<uint8_t>(total & 0xFF);
                updateZNFlags(c.proc, c.proc.reg_a);
            }
            break;

            case icode::op_type::OP_DECIMAL: {
                uint8_t val = c.instruct.at(in).op1.op;
                uint16_t total = c.proc.reg_a + val + carry;
                if (c.proc.getFlag(icode::FLAG_DECIMAL)) {
                    uint8_t lo = (c.proc.reg_a & 0x0F) + (val & 0x0F) + carry;
                    if (lo > 9) lo += 6;
                    uint8_t hi = (c.proc.reg_a >> 4) + (val >> 4) + (lo > 0x0F ? 1 : 0);
                    if (hi > 9) hi += 6;
                    total = (hi << 4) | (lo & 0x0F);
                    c.proc.setFlag(icode::FLAG_CARRY, (hi > 0x0F));
                } else {
                    c.proc.setFlag(icode::FLAG_CARRY, (total > 0xFF));
                }
                bool A7 = (c.proc.reg_a & 0x80);
                bool M7 = (val & 0x80);
                bool R7 = (total & 0x80);
                c.proc.setFlag(icode::FLAG_OVERFLOW, ((A7 == M7) && (A7 != R7)));
                c.proc.reg_a = static_cast<uint8_t>(total & 0xFF);
                updateZNFlags(c.proc, c.proc.reg_a);
            }
            break;

            default:
                break;
        }
    }

    void i_sbc(Code &c) {
        int in = c.proc.getIp();
        uint8_t carry = c.proc.getFlag(icode::FLAG_CARRY);

        switch (c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY: {
                uint8_t val = 0;
                switch (c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        val = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        val = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t effective_addr = base_addr + c.proc.reg_y;

                        val = c.peek(effective_addr);
                    }
                    break;
                    case INDEXED_I: { // (address,X)
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t indexed_addr = (zp_addr + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(indexed_addr);
                        uint8_t high = c.peek((indexed_addr + 1) & 0xFF);
                        uint16_t effective_addr = (high << 8) | low;

                        val = c.peek(effective_addr);
                    }
                    break;
                    default:
                        return;
                }

                uint16_t total = c.proc.reg_a - val - (1 - carry);
                c.proc.setFlag(icode::FLAG_CARRY, (c.proc.reg_a >= (val + (1 - carry))));

                bool A7 = (c.proc.reg_a & 0x80);
                bool M7 = (val & 0x80);
                bool R7 = (total & 0x80);
                c.proc.setFlag(icode::FLAG_OVERFLOW, ((A7 != M7) && (A7 != R7)));

                c.proc.reg_a = static_cast<uint8_t>(total & 0xFF);
                updateZNFlags(c.proc, c.proc.reg_a);
            }
            break;

            case icode::op_type::OP_DECIMAL: {
                uint8_t val = c.instruct.at(in).op1.op;
                uint16_t total = c.proc.reg_a - val - (1 - carry);
                if (c.proc.getFlag(icode::FLAG_DECIMAL)) {
                    uint8_t lo = (c.proc.reg_a & 0x0F) - (val & 0x0F) - (1 - carry);
                    if (lo & 0x10) lo -= 6;
                    uint8_t hi = (c.proc.reg_a >> 4) - (val >> 4) - ((lo & 0x10) ? 1 : 0);
                    if (hi & 0x10) hi -= 6;
                    total = (hi << 4) | (lo & 0x0F);
                    c.proc.setFlag(icode::FLAG_CARRY, !(hi & 0x10));
                } else {
                    c.proc.setFlag(icode::FLAG_CARRY, (c.proc.reg_a >= (val + (1 - carry))));
                }
                bool A7 = (c.proc.reg_a & 0x80);
                bool M7 = (val & 0x80);
                bool R7 = (total & 0x80);
                c.proc.setFlag(icode::FLAG_OVERFLOW, ((A7 != M7) && (A7 != R7)));
                c.proc.reg_a = static_cast<uint8_t>(total & 0xFF);
                updateZNFlags(c.proc, c.proc.reg_a);
            }
            break;

            default:
                break;
        }
    }

    void i_and(Code &c) {
        int in = c.proc.getIp();
        uint8_t val = 0;
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY: {
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        val = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        val = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t effective_addr = base_addr + c.proc.reg_y;

                        val = c.peek(effective_addr);
                    }
                    break;
                    case INDEXED_I: { // (address,X)
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t indexed_addr = (zp_addr + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(indexed_addr);
                        uint8_t high = c.peek((indexed_addr + 1) & 0xFF);
                        uint16_t effective_addr = (high << 8) | low;

                        val = c.peek(effective_addr);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            case icode::op_type::OP_DECIMAL: {
                val = c.instruct.at(in).op1.op;
            }
            break;
            default:
                return;
        }
        c.proc.reg_a &= val;
        updateZNFlags(c.proc, c.proc.reg_a);
    }

    void i_asl(Code &c) {
        int in = c.proc.getIp();
        
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_REGISTER_A: { 
                uint8_t oldVal = c.proc.reg_a;  // Declare oldVal here
                
                // Set carry flag to bit 7 of accumulator
                c.proc.setFlag(icode::FLAG_CARRY, (oldVal & 0x80) != 0);
                
                // Shift left
                c.proc.reg_a <<= 1;
                
                // Update Zero and Negative flags
                updateZNFlags(c.proc, c.proc.reg_a);
            } break;
            
            case icode::op_type::OP_MEMORY: {
                uint16_t addr = c.instruct.at(in).op1.op;
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE: {
                        uint8_t cc = c.peek(addr);
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc <<= 1;
                        c.poke(addr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE: {
                        uint8_t realAddr = addr & 0xFF;
                        uint8_t cc = c.peek(realAddr);
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc <<= 1;
                        c.poke(realAddr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ABSOLUTE_X: {
                        uint16_t addrX = (addr + c.proc.reg_x) & 0xFFFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc <<= 1;
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addrX = (addr + c.proc.reg_x) & 0xFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc <<= 1;
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            default:
                break;
        }
    }

    void i_bcc(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_CARRY) == 0) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_bcs(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_CARRY) == 1) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_beq(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_ZERO) == 1) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_bit(Code &c) {
        int in = c.proc.getIp();
        
        if (c.instruct.at(in).op1.op_t != icode::op_type::OP_MEMORY) {
            return;
        }   
        uint8_t operand = 0;
        switch (c.instruct.at(in).mode) {
            case interp::ABSOLUTE:
            case interp::ZEROPAGE:
                operand = c.peek(c.instruct.at(in).op1.op);
                break;
            default:
                return;
        }
        uint8_t result = c.proc.reg_a & operand;
        c.proc.setFlag(icode::FLAG_ZERO, (result == 0));
        c.proc.setFlag(icode::FLAG_NEGATIVE, ((operand & 0x80) != 0));
        c.proc.setFlag(icode::FLAG_OVERFLOW, ((operand & 0x40) != 0));
    }

    void i_bmi(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_NEGATIVE) == 1) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_bne(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_ZERO) == 0) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_bpl(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_NEGATIVE) == 0) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_brk(Code &c) {
        uint16_t pc_plus_2 = c.proc.ip + 2;
        c.poke(0x0100 + c.proc.sp, (pc_plus_2 >> 8) & 0xFF);
        c.proc.sp = (c.proc.sp - 1) & 0xFF;
        c.poke(0x0100 + c.proc.sp, pc_plus_2 & 0xFF);
        c.proc.sp = (c.proc.sp - 1) & 0xFF;
        uint8_t status = c.proc.valFlags() | 0x10; 
        c.poke(0x0100 + c.proc.sp, status);
        c.proc.sp = (c.proc.sp - 1) & 0xFF;
        
        c.proc.setFlag(icode::FLAG_INTERRUPT, 1);
        c.stop();
        std::cout << "BRK instruction executed\n";
    }

    void i_bvc(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_OVERFLOW) == 0) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_bvs(Code &c) {
        unsigned int ip = c.proc.getIp();
        if(c.proc.getFlag(icode::FLAG_OVERFLOW) == 1) {
            c.proc.ip = c.instruct[ip].op1.label_index - 1;
        }
    }

    void i_clc(Code &c) {
        c.proc.setFlag(icode::FLAG_CARRY, 0);
    }

    void i_cld(Code &c) {
        c.proc.setFlag(icode::FLAG_DECIMAL, 0);
    }

    void i_cli(Code &c) {
        c.proc.setFlag(icode::FLAG_INTERRUPT, 0);
    }

    void i_clv(Code &c) {
        c.proc.setFlag(icode::FLAG_OVERFLOW, 0);
    }

    void i_cmp(Code &c) {
        int in = c.proc.getIp();
        uint8_t operand = 0;
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        operand = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        operand = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        operand = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        operand = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        operand = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        operand = c.peek(addr);
                    }
                    break;
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t effective_addr = base_addr + c.proc.reg_y;

                        operand = c.peek(effective_addr);
                    }
                    break;
                    case INDEXED_I: { // (address,X)
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t indexed_addr = (zp_addr + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(indexed_addr);
                        uint8_t high = c.peek((indexed_addr + 1) & 0xFF);
                        uint16_t effective_addr = (high << 8) | low;

                        operand = c.peek(effective_addr);
                    }
                    break;
                    default:
                        return;
                }
                break;
            case icode::op_type::OP_DECIMAL:
                operand = c.instruct.at(in).op1.op;
                break;
            default:
                return;
        }
        uint8_t result = c.proc.reg_a - operand;
        c.proc.setFlag(icode::FLAG_CARRY, (c.proc.reg_a >= operand));
        updateZNFlags(c.proc, result);
    }

    void i_cpx(Code &c) {
        int in = c.proc.getIp();
        uint8_t operand = 0;
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        operand = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        operand = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    default:
                        return;
                }
                break;
            case icode::op_type::OP_DECIMAL:
                operand = c.instruct.at(in).op1.op;
                break;
            default:
                return;
        }
        uint8_t result = c.proc.reg_x - operand;
        c.proc.setFlag(icode::FLAG_CARRY, (c.proc.reg_x >= operand));
        updateZNFlags(c.proc, result);
    }

    void i_cpy(Code &c) {
        int in = c.proc.getIp();
        uint8_t operand = 0;
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        operand = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        operand = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    default:
                        return;
                }
                break;
            case icode::op_type::OP_DECIMAL:
                operand = c.instruct.at(in).op1.op;
                break;
            default:
                return;
        }
        uint8_t result = c.proc.reg_y - operand;
        c.proc.setFlag(icode::FLAG_CARRY, (c.proc.reg_y >= operand));
        updateZNFlags(c.proc, result);
    }

    void i_dec(Code &c) {
        int in = c.proc.getIp();
        uint16_t addr = c.instruct.at(in).op1.op;
        switch(c.instruct.at(in).mode) {
            case ABSOLUTE: {
                uint8_t value = c.peek(addr) - 1;
                c.poke(addr, value);
                updateZNFlags(c.proc, value);
            }
            break;
            case ZEROPAGE: {
                uint8_t realAddr = addr & 0xFF;
                uint8_t value = c.peek(realAddr) - 1;
                c.poke(realAddr, value);
                updateZNFlags(c.proc, value);
            }
            break;
            case ABSOLUTE_X: {
                uint16_t addrX = (addr + c.proc.reg_x) & 0xFFFF;  
                uint8_t value = c.peek(addrX) - 1;
                c.poke(addrX, value);
                updateZNFlags(c.proc, value);
            }
            break;
            case ZEROPAGE_X: {
                uint8_t addrX = (addr + c.proc.reg_x) & 0xFF;
                uint8_t value = c.peek(addrX) - 1;
                c.poke(addrX, value);
                updateZNFlags(c.proc, value);
            }
            break;
            default:
                return;
        }
    }

    void i_int(Code &c) {

    }

    void i_inx(Code &c) {
        c.proc.reg_x = c.proc.reg_x + 1;
        updateZNFlags(c.proc, c.proc.reg_x);
    }

    void i_iny(Code &c) {
        c.proc.reg_y = c.proc.reg_y + 1;
        updateZNFlags(c.proc, c.proc.reg_y);
    }

    void i_jmp(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_LABELTEXT:
            case icode::op_type::OP_LABEL:
                c.proc.ip = c.instruct.at(in).op1.label_index - 1;
                return;
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.proc.ip = c.instruct.at(in).op1.op - 1;
                        break;
                    case INDIRECT: { 
                        uint16_t addr = c.instruct.at(in).op1.op;
                        uint8_t low = c.peek(addr);
                        uint8_t high = c.peek(addr + 1);
                        uint16_t target = (high << 8) | low;
                        c.proc.ip = target - 1;
                    }
                    break;
                    default:
                        return;
                }
                break;
            default:
                break;
        }
    }


    // new
    void i_jsr(Code &c) {
        auto &p = c.proc;
        uint16_t return_addr = p.ip - 1;
        c.poke(0x0100 + p.sp, (return_addr >> 8) & 0xFF);
        p.sp = (p.sp - 1) & 0xFF;
        c.poke(0x0100 + p.sp, return_addr & 0xFF);
        p.sp = (p.sp - 1) & 0xFF;
        if(c.instruct.at(p.getIp()).op1.op_t == icode::op_type::OP_LABELTEXT ||
           c.instruct.at(p.getIp()).op1.op_t == icode::op_type::OP_LABEL) {
            p.ip = c.instruct.at(p.getIp()).op1.label_index - 1;
        } else if(c.instruct.at(p.getIp()).op1.op_t == icode::op_type::OP_MEMORY &&
                  c.instruct.at(p.getIp()).mode == ABSOLUTE) {
            p.ip = c.instruct.at(p.getIp()).op1.op - 1;
        } 
    }


    // new
    void i_rts(Code &c) {
        auto &p = c.proc;
        p.sp = (p.sp + 1) & 0xFF;
        uint8_t low  = c.peek(0x0100 + p.sp);
        p.sp = (p.sp + 1) & 0xFF;
        uint8_t high = c.peek(0x0100 + p.sp);
        uint16_t return_addr = ((uint16_t)high << 8) | low;
        p.ip = return_addr + 1;
    }


    void i_lda(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.proc.reg_a = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        c.proc.reg_a = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        c.proc.reg_a = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        c.proc.reg_a = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        c.proc.reg_a = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        c.proc.reg_a = c.peek(addr);
                    }
                    break;
                    case INDEXED_I: {  // (address,X)
                        uint8_t zp_addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t target_addr = (high << 8) | low;
                        c.proc.reg_a = c.peek(target_addr);
                    }
                    break;
                    
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t target_addr = (base_addr + c.proc.reg_y) & 0xFFFF;
                        c.proc.reg_a = c.peek(target_addr);
                    }
                    break;
                    
                    default:
                        return;
                }
                updateZNFlags(c.proc, c.proc.reg_a);
                break;
            case icode::op_type::OP_DECIMAL:
                c.proc.reg_a = c.instruct.at(in).op1.op;
                updateZNFlags(c.proc, c.proc.reg_a);
                break;
            default:
                break;
        }
    }

    void i_ldm(Code &c) {
        // To be implemented.
    }

    void i_ldx(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.proc.reg_x = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        c.proc.reg_x = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        c.proc.reg_x = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        c.proc.reg_x = c.peek(addr);
                    }
                    break;
                    default:
                        return;
                }
                updateZNFlags(c.proc, c.proc.reg_x);
                break;
            case icode::op_type::OP_DECIMAL:
                c.proc.reg_x = c.instruct.at(in).op1.op;
                updateZNFlags(c.proc, c.proc.reg_x);
                break;
            default:
                break;
        }
    }

    void i_ldy(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.proc.reg_y = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        c.proc.reg_y = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        c.proc.reg_y = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        c.proc.reg_y = c.peek(addr);
                    }
                    break;
                    default:
                        return;
                }
                updateZNFlags(c.proc, c.proc.reg_y);
                break;
            case icode::op_type::OP_DECIMAL:
                c.proc.reg_y = c.instruct.at(in).op1.op;
                updateZNFlags(c.proc, c.proc.reg_y);
                break;
            default:
                break;
        }
    }

    void i_lsr(Code &c) {
        unsigned int ip = c.proc.getIp();
        switch(c.instruct[ip].op1.op_t) {
            case icode::op_type::OP_REGISTER_A: { // ACCUMULATOR mode
                uint8_t oldVal = c.proc.reg_a;  // Declare oldVal here
                
                // Set carry flag to bit 0 of accumulator
                c.proc.setFlag(icode::FLAG_CARRY, (oldVal & 0x01) != 0);
                
                // Shift right
                c.proc.reg_a >>= 1;
                
                // Update Zero and Negative flags
                updateZNFlags(c.proc, c.proc.reg_a);
            } break;
            
            case icode::op_type::OP_MEMORY: {
                uint16_t addr = c.instruct[ip].op1.op;
                switch(c.instruct[ip].mode) {
                    case ABSOLUTE: {
                        uint8_t cc = c.peek(addr);
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc >>= 1;
                        c.poke(addr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE: {
                        uint8_t realAddr = addr & 0xFF;
                        uint8_t cc = c.peek(realAddr);
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc >>= 1;
                        c.poke(realAddr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ABSOLUTE_X: {
                        uint16_t addrX = (addr + c.proc.reg_x) & 0xFFFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc >>= 1;
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addrX = (addr + c.proc.reg_x) & 0xFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc >>= 1;
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            default:
                break;
        }
    }

    void i_nop(Code &c) {
        std::cout << "nop\n";
    }

    void i_ora(Code &c) {
        int in = c.proc.getIp();
        uint8_t val = 0;
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY: {
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        val = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        val = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t effective_addr = base_addr + c.proc.reg_y;

                        val = c.peek(effective_addr);
                    }
                    break;
                    case INDEXED_I: { // (address,X)
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t indexed_addr = (zp_addr + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(indexed_addr);
                        uint8_t high = c.peek((indexed_addr + 1) & 0xFF);
                        uint16_t effective_addr = (high << 8) | low;

                        val = c.peek(effective_addr);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            case icode::op_type::OP_DECIMAL: {
                val = c.instruct.at(in).op1.op;
            }
            break;
            default:
                return;
        }
        c.proc.reg_a |= val;
        updateZNFlags(c.proc, c.proc.reg_a);
    }

    void i_eor(Code &c) {
        int in = c.proc.getIp();
        uint8_t val = 0;
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY: {
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        val = c.peek(c.instruct.at(in).op1.op);
                        break;
                    case ZEROPAGE:
                        val = c.peek(c.instruct.at(in).op1.op & 0xFF);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        val = c.peek(addr);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        val = c.peek(addr);
                    }
                    break;
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t effective_addr = base_addr + c.proc.reg_y;

                        val = c.peek(effective_addr);
                    }
                    break;
                    case INDEXED_I: { // (address,X)
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t indexed_addr = (zp_addr + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(indexed_addr);
                        uint8_t high = c.peek((indexed_addr + 1) & 0xFF);
                        uint16_t effective_addr = (high << 8) | low;

                        val = c.peek(effective_addr);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            case icode::op_type::OP_DECIMAL: {
                val = c.instruct.at(in).op1.op;
            }
            break;
            default:
                return;
        }
        c.proc.reg_a ^= val;
        updateZNFlags(c.proc, c.proc.reg_a);
    }

    void i_inc(Code &c) {
        int in = c.proc.getIp();
        uint16_t addr = c.instruct.at(in).op1.op;
        switch(c.instruct.at(in).mode) {
            case ABSOLUTE: {
                uint8_t value = c.peek(addr) + 1;
                c.poke(addr, value);
                updateZNFlags(c.proc, value);
            }
            break;
            case ZEROPAGE: {
                uint8_t realAddr = addr & 0xFF;
                uint8_t value = c.peek(realAddr) + 1;
                c.poke(realAddr, value);
                updateZNFlags(c.proc, value);
            }
            break;
            case ABSOLUTE_X: {
                uint16_t addrX = (addr + c.proc.reg_x) & 0xFFFF;  // Add & 0xFFFF
                uint8_t value = c.peek(addrX) + 1;
                c.poke(addrX, value);
                updateZNFlags(c.proc, value);
            }
            break;
            case ZEROPAGE_X: {
                uint8_t addrX = (addr + c.proc.reg_x) & 0xFF;
                uint8_t value = c.peek(addrX) + 1;
                c.poke(addrX, value);
                updateZNFlags(c.proc, value);
            }
            break;
            default:
                return;
        }
    }

    void i_dex(Code &c) {
        c.proc.reg_x = c.proc.reg_x - 1;
        updateZNFlags(c.proc, c.proc.reg_x);
    }

    void i_dey(Code &c) {
        c.proc.reg_y = c.proc.reg_y - 1;
        updateZNFlags(c.proc, c.proc.reg_y);
    }

    void i_end(Code &c) {
        c.stop();
        std::cout << "END instruction executed\n";
    }
    



    void i_pha(Code &c) {
        c.poke(0x0100 + c.proc.sp, c.proc.reg_a);
        c.proc.sp = (c.proc.sp - 1) & 0xFF;  
    }

    void i_pla(Code &c) {
        c.proc.sp = (c.proc.sp + 1) & 0xFF;
        c.proc.reg_a = c.peek(0x0100 + c.proc.sp);
        updateZNFlags(c.proc, c.proc.reg_a);
    }
    void i_php(Code &c) {
        uint8_t status = c.proc.valFlags() | 0x30;
        c.poke(0x0100 + c.proc.sp, status);
        c.proc.sp = (c.proc.sp - 1) & 0xFF;
    }

    void i_plp(Code &c) {
        c.proc.sp = (c.proc.sp + 1) & 0xFF;
        uint8_t flags = c.peek(0x0100 + c.proc.sp);
        c.proc.setFlags(flags);
    }

    void i_rti(Code &c) {
        c.proc.sp = (c.proc.sp + 1) & 0xFF;
        uint8_t status = c.peek(0x0100 + c.proc.sp);
        c.proc.sp = (c.proc.sp + 1) & 0xFF;
        uint8_t pcl = c.peek(0x0100 + c.proc.sp);
        c.proc.sp = (c.proc.sp + 1) & 0xFF;
        uint8_t pch = c.peek(0x0100 + c.proc.sp);
        
        c.proc.setFlags(status);
        c.proc.ip = ((uint16_t)pch << 8) | pcl;
    }

    void i_rol(Code &c) {
        int in = c.proc.getIp();
    
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_REGISTER_A: { 
                uint8_t oldVal = c.proc.reg_a;  
                uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                
                // Set carry flag to bit 7 of accumulator
                c.proc.setFlag(icode::FLAG_CARRY, (oldVal & 0x80) != 0);
                
                // Rotate left with old carry
                c.proc.reg_a = (oldVal << 1) | oldCarry;
                
                // Update Zero and Negative flags
                updateZNFlags(c.proc, c.proc.reg_a);
            } break;
            
            case icode::op_type::OP_MEMORY: {
                uint16_t addr = c.instruct.at(in).op1.op;
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE: {
                        uint8_t cc = c.peek(addr);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc = (cc << 1) | oldCarry;
                        c.poke(addr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE: {
                        uint8_t realAddr = addr & 0xFF;
                        uint8_t cc = c.peek(realAddr);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc = (cc << 1) | oldCarry;
                        c.poke(realAddr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ABSOLUTE_X: {
                        uint16_t addrX = (addr + c.proc.reg_x) & 0xFFFF;  
                        uint8_t cc = c.peek(addrX);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc = (cc << 1) | oldCarry;
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addrX = (addr + c.proc.reg_x) & 0xFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x80) ? 1 : 0;
                        cc = (cc << 1) | oldCarry;
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            default:
                break;
        }
    }

    void i_ror(Code &c) {
        int in = c.proc.getIp();
    
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_REGISTER_A: { 
                uint8_t oldVal = c.proc.reg_a;  
                uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                c.proc.setFlag(icode::FLAG_CARRY, (oldVal & 0x01) != 0);
                c.proc.reg_a = (oldVal >> 1) | (oldCarry << 7);
                updateZNFlags(c.proc, c.proc.reg_a);
            } break;
            
            case icode::op_type::OP_MEMORY: {
                uint16_t addr = c.instruct.at(in).op1.op;
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE: {
                        uint8_t cc = c.peek(addr);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc = (cc >> 1) | (oldCarry << 7);
                        c.poke(addr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE: {
                        uint8_t realAddr = addr & 0xFF;
                        uint8_t cc = c.peek(realAddr);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc = (cc >> 1) | (oldCarry << 7);
                        c.poke(realAddr, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ABSOLUTE_X: {
                        uint16_t addrX = (addr + c.proc.reg_x) & 0xFFFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc = (cc >> 1) | (oldCarry << 7);
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addrX = (addr + c.proc.reg_x) & 0xFF;
                        uint8_t cc = c.peek(addrX);
                        uint8_t oldCarry = c.proc.getFlag(icode::FLAG_CARRY) ? 1 : 0;
                        uint8_t newCarry = (cc & 0x01) ? 1 : 0;
                        cc = (cc >> 1) | (oldCarry << 7);
                        c.poke(addrX, cc);
                        c.proc.setFlag(icode::FLAG_CARRY, newCarry);
                        updateZNFlags(c.proc, cc);
                    }
                    break;
                    default:
                        return;
                }
            }
            break;
            default:
                break;
        }
    }

    void i_sta(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.poke(c.instruct.at(in).op1.op, c.proc.reg_a);
                        ///trackMemoryWrite(c.instruct.at(in).op1.op, c.proc.reg_a);
                        break;
                    case ZEROPAGE:
                        c.poke(c.instruct.at(in).op1.op & 0xFF, c.proc.reg_a);
                        //trackMemoryWrite(c.instruct.at(in).op1.op & 0xFF, c.proc.reg_a);
                        break;
                    case ABSOLUTE_X: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFFFF;
                        c.poke(addr, c.proc.reg_a);
                        //trackMemoryWrite(addr, c.proc.reg_a);
                    }
                    break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        //trackMemoryWrite(addr, c.proc.reg_a);
                        c.poke(addr, c.proc.reg_a);
                    }
                    break;
                    case ABSOLUTE_Y: {
                        uint16_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFFFF;
                        //trackMemoryWrite(addr, c.proc.reg_a);
                        c.poke(addr, c.proc.reg_a);
                    }
                    break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        //trackMemoryWrite(addr, c.proc.reg_a);
                        c.poke(addr, c.proc.reg_a);
                    }
                    break;
                    case INDEXED_I: {  // (address,X)
                        uint8_t zp_addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t target_addr = (high << 8) | low;
                        c.poke(target_addr, c.proc.reg_a);
                    }
                    break;
                    
                    case INDIRECT_I: { // (address),Y
                        uint8_t zp_addr = c.instruct.at(in).op1.op & 0xFF;
                        uint8_t low = c.peek(zp_addr);
                        uint8_t high = c.peek((zp_addr + 1) & 0xFF);
                        uint16_t base_addr = (high << 8) | low;
                        uint16_t target_addr = (base_addr + c.proc.reg_y) & 0xFFFF;
                        c.poke(target_addr, c.proc.reg_a);
                    }
                    break;
                    
                    default:
                        return;
                }
                break;
            default:
                break;
        }
    }

    void i_stx(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.poke(c.instruct.at(in).op1.op, c.proc.reg_x);
                        break;
                    case ZEROPAGE:
                        c.poke(c.instruct.at(in).op1.op & 0xFF, c.proc.reg_x);
                        break;
                    case ZEROPAGE_Y: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_y) & 0xFF;
                        c.poke(addr, c.proc.reg_x);
                    }
                    break;
                    default:
                        return;
                }
                break;
            default:
                break;
        }
    }

    void i_sty(Code &c) {
        int in = c.proc.getIp();
        switch(c.instruct.at(in).op1.op_t) {
            case icode::op_type::OP_MEMORY:
                switch(c.instruct.at(in).mode) {
                    case ABSOLUTE:
                        c.poke(c.instruct.at(in).op1.op, c.proc.reg_y);
                        break;
                    case ZEROPAGE:
                        c.poke(c.instruct.at(in).op1.op & 0xFF, c.proc.reg_y);
                        break;
                    case ZEROPAGE_X: {
                        uint8_t addr = (c.instruct.at(in).op1.op + c.proc.reg_x) & 0xFF;
                        c.poke(addr, c.proc.reg_y);
                    }
                    break;
                    default:
                        return;
                }
                break;
            default:
                break;
        }
    }

    void i_tax(Code &c) {
        c.proc.reg_x = c.proc.reg_a;
        updateZNFlags(c.proc, c.proc.reg_x);
    }

    void i_tay(Code &c) {
        c.proc.reg_y = c.proc.reg_a;
        updateZNFlags(c.proc, c.proc.reg_y);
    }

    void i_tsx(Code &c) {
        c.proc.reg_x = c.proc.sp;
        updateZNFlags(c.proc, c.proc.reg_x);
    }

    void i_txa(Code &c) {
        c.proc.reg_a = c.proc.reg_x;
        updateZNFlags(c.proc, c.proc.reg_a);
    }

    void i_txs(Code &c) {
        c.proc.sp = c.proc.reg_x; 
    }

    void i_tya(Code &c) {
        c.proc.reg_a = c.proc.reg_y;
        updateZNFlags(c.proc, c.proc.reg_a);
    }
    
    void i_sec(Code &c) {
        c.proc.setFlag(icode::FLAG_CARRY, 1);
    }

    void i_sed(Code &c) {
        c.proc.setFlag(icode::FLAG_DECIMAL, 1);
    }

    void i_sei(Code &c) {
        c.proc.setFlag(icode::FLAG_INTERRUPT, 1);
    }

}