/*
    Copyright © 2019 Michal Schulz <michal.schulz@gmx.de>
    https://github.com/michalsc

    This Source Code Form is subject to the terms of the
    Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
    with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include "support.h"
#include "M68k.h"
#include "RegisterAllocator.h"
#include "cache.h"

uint32_t *EMIT_MUL_DIV(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr);

uint32_t *EMIT_MUL_DIV_(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    return EMIT_MUL_DIV(ptr, opcode, m68k_ptr);
}


extern uint32_t insn_count;

uint32_t *EMIT_CLR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_count = 0;
    uint8_t size = 1;
    uint8_t zero = 0xff;

    zero = 31;

    /* Determine the size of operation */
    switch (opcode & 0x00c0)
    {
        case 0x0000: /* Byte operation */
            size = 1;
            break;
        case 0x0040: /* Short operation */
            size = 2;
            break;
        case 0x0080: /* Long operation */
            size = 4;
            break;
    }

    if ((opcode & 0x38) == 0)
    {
        if (size != 4) {
            uint8_t dn = RA_MapM68kRegister(&ptr, opcode & 7);
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);
            switch (size) {
                case 1:
                    *ptr++ = bic_immed(dn, dn, 8, 0);
                    break;
                case 2:
                    *ptr++ = bic_immed(dn, dn, 16, 0);
            }
        }
        else {
            uint8_t dn = RA_MapM68kRegisterForWrite(&ptr, opcode & 7);
            *ptr++ = mov_reg(dn, 31);
        }
    }
    else
        ptr = EMIT_StoreToEffectiveAddress(ptr, size, &zero, opcode & 0x3f, *m68k_ptr, &ext_count, 0);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_count + 1));
    (*m68k_ptr) += ext_count;

    if (update_mask)
    {
        uint8_t cc = RA_ModifyCC(&ptr);
        if (update_mask & ~SR_Z)
        {
            uint8_t alt_flags = update_mask;
            if ((alt_flags & 3) != 0 && (alt_flags & 3) < 3)
                alt_flags ^= 3;
            ptr = EMIT_ClearFlags(ptr, cc, alt_flags);
        }
        if (update_mask & SR_Z)
            ptr = EMIT_SetFlags(ptr, cc, SR_Z);
    }
    return ptr;
}

uint32_t *EMIT_NOT(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_count = 0;
    uint8_t dest = 0xff;
    uint8_t size = 0;
    uint8_t test_register = 0xff;

    /* Determine the size of operation */
    switch (opcode & 0x00c0)
    {
        case 0x0000: /* Byte operation */
            size = 1;
            break;
        case 0x0040: /* Short operation */
            size = 2;
            break;
        case 0x0080: /* Long operation */
            size = 4;
            break;
    }

    /* handle clearing D register here */
    if ((opcode & 0x0038) == 0)
    {
        if (size == 4)
        {
            dest = RA_MapM68kRegister(&ptr, opcode & 7);
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);
            test_register = dest;

            *ptr++ = mvn_reg(dest, dest, LSL, 0);
        }
        else
        {
            uint8_t tmp = RA_AllocARMRegister(&ptr);

            /* Fetch m68k register for write */
            dest = RA_MapM68kRegister(&ptr, opcode & 7);

            /* Mark register dirty */
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);

            test_register = dest;

            switch(size)
            {
                case 2:
                    *ptr++ = mvn_reg(tmp, dest, LSL, 0);
                    *ptr++ = bfi(dest, tmp, 0, 16);
                    break;
                case 1:
                    *ptr++ = mvn_reg(tmp, dest, LSL, 0);
                    *ptr++ = bfi(dest, tmp, 0, 8);
                    break;
            }

            RA_FreeARMRegister(&ptr, tmp);
        }
    }
    else
    {
        uint8_t tmp = RA_AllocARMRegister(&ptr);
        uint8_t mode = (opcode & 0x0038) >> 3;
        test_register = tmp;

        /* Load effective address */
        if (mode == 4 || mode == 3)
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 0, NULL);
        else
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 1, NULL);

        /* Fetch data into temporary register, perform add, store it back */
        switch (size)
        {
        case 4:
            if (mode == 4)
            {
                *ptr++ = ldr_offset_preindex(dest, tmp, -4);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldr_offset(dest, tmp, 0);

            *ptr++ = mvn_reg(tmp, tmp, LSL, 0);

            if (mode == 3)
            {
                *ptr++ = str_offset_postindex(dest, tmp, 4);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = str_offset(dest, tmp, 0);
            break;
        case 2:
            if (mode == 4)
            {
                *ptr++ = ldrh_offset_preindex(dest, tmp, -2);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldrh_offset(dest, tmp, 0);

            *ptr++ = mvn_reg(tmp, tmp, LSL, 0);

            if (mode == 3)
            {
                *ptr++ = strh_offset_postindex(dest, tmp, 2);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = strh_offset(dest, tmp, 0);
            break;
        case 1:
            if (mode == 4)
            {
                *ptr++ = ldrb_offset_preindex(dest, tmp, (opcode & 7) == 7 ? -2 : -1);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldrb_offset(dest, tmp, 0);

            *ptr++ = mvn_reg(tmp, tmp, LSL, 0);

            if (mode == 3)
            {
                *ptr++ = strb_offset_postindex(dest, tmp, (opcode & 7) == 7 ? 2 : 1);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = strb_offset(dest, tmp, 0);
            break;
        }
    }

    RA_FreeARMRegister(&ptr, dest);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_count + 1));
    (*m68k_ptr) += ext_count;

    if (update_mask)
    {
        uint8_t cc = RA_ModifyCC(&ptr);

        switch(size) {
            case 4:
                *ptr++ = cmn_reg(31, test_register, LSL, 0);
                break;
            case 2:
                *ptr++ = cmn_reg(31, test_register, LSL, 16);
                break;
            case 1:
                *ptr++ = cmn_reg(31, test_register, LSL, 24);
                break;
        }

        ptr = EMIT_GetNZ00(ptr, cc, &update_mask);

        if (update_mask & SR_Z)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Z, ARM_CC_EQ);
        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
    }

    RA_FreeARMRegister(&ptr, test_register);

    return ptr;
}

uint32_t *EMIT_NEG(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_count = 0;
    uint8_t dest = 0xff;
    uint8_t size = 0;

    /* Determine the size of operation */
    switch (opcode & 0x00c0)
    {
        case 0x0000: /* Byte operation */
            size = 1;
            break;
        case 0x0040: /* Short operation */
            size = 2;
            break;
        case 0x0080: /* Long operation */
            size = 4;
            break;
    }

    /* handle clearing D register here */
    if ((opcode & 0x0038) == 0)
    {
        if (size == 4)
        {
            dest = RA_MapM68kRegister(&ptr, opcode & 7);
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);

            *ptr++ = negs_reg(dest, dest, LSL, 0);
        }
        else
        {
            uint8_t tmp = RA_AllocARMRegister(&ptr);

            /* Fetch m68k register for write */
            dest = RA_MapM68kRegister(&ptr, opcode & 7);

            /* Mark register dirty */
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);

            switch(size)
            {
                case 2:
                    if (update_mask == 0) {
                        *ptr++ = negs_reg(tmp, dest, LSL, 0);
                        *ptr++ = bfxil(dest, tmp, 0, 16);
                    }
                    else {
                        *ptr++ = negs_reg(tmp, dest, LSL, 16);
                        *ptr++ = bfxil(dest, tmp, 16, 16);
                    }
                    break;
                
                case 1:
                    if (update_mask == 0) {
                        *ptr++ = negs_reg(tmp, dest, LSL, 0);
                        *ptr++ = bfxil(dest, tmp, 0, 8);
                    }
                    else {
                        *ptr++ = negs_reg(tmp, dest, LSL, 24);
                        *ptr++ = bfxil(dest, tmp, 24, 8);
                    }
                    break;
            }

            RA_FreeARMRegister(&ptr, tmp);
        }
    }
    else
    {
        uint8_t tmp = RA_AllocARMRegister(&ptr);
        uint8_t mode = (opcode & 0x0038) >> 3;

        /* Load effective address */
        if (mode == 4 || mode == 3)
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 0, NULL);
        else
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 1, NULL);

        /* Fetch data into temporary register, perform add, store it back */
        switch (size)
        {
        case 4:
            if (mode == 4)
            {
                *ptr++ = ldr_offset_preindex(dest, tmp, -4);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldr_offset(dest, tmp, 0);

            *ptr++ = negs_reg(tmp, tmp, LSL, 0);

            if (mode == 3)
            {
                *ptr++ = str_offset_postindex(dest, tmp, 4);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = str_offset(dest, tmp, 0);
            break;
        case 2:
            if (mode == 4)
            {
                *ptr++ = ldrh_offset_preindex(dest, tmp, -2);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldrh_offset(dest, tmp, 0);

            if (update_mask == 0) {
                *ptr++ = neg_reg(tmp, tmp, LSL, 0);
            }
            else {
                *ptr++ = negs_reg(tmp, tmp, LSL, 16);
                *ptr++ = lsr(tmp, tmp, 16);
            }

            if (mode == 3)
            {
                *ptr++ = strh_offset_postindex(dest, tmp, 2);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = strh_offset(dest, tmp, 0);
            break;
        case 1:
            if (mode == 4)
            {
                *ptr++ = ldrb_offset_preindex(dest, tmp, (opcode & 7) == 7 ? -2 : -1);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldrb_offset(dest, tmp, 0);

            if (update_mask == 0) {
                *ptr++ = neg_reg(tmp, tmp, LSL, 0);
            }
            else {
                *ptr++ = negs_reg(tmp, tmp, LSL, 24);
                *ptr++ = lsr(tmp, tmp, 24);
            }

            if (mode == 3)
            {
                *ptr++ = strb_offset_postindex(dest, tmp, (opcode & 7) == 7 ? 2 : 1);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = strb_offset(dest, tmp, 0);
            break;
        }

        RA_FreeARMRegister(&ptr, tmp);
    }

    RA_FreeARMRegister(&ptr, dest);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_count + 1));
    (*m68k_ptr) += ext_count;

    if (update_mask)
    {
        uint8_t cc = RA_ModifyCC(&ptr);
        if (update_mask & SR_X)
            ptr = EMIT_GetNZnCVX(ptr, cc, &update_mask);
        else
            ptr = EMIT_GetNZnCV(ptr, cc, &update_mask);

        if (update_mask & SR_Z)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Z, ARM_CC_EQ);
        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
        if (update_mask & SR_V)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Valt, ARM_CC_VS);
        if (update_mask & (SR_X | SR_C)) {
            if ((update_mask & (SR_X | SR_C)) == SR_X)
                ptr = EMIT_SetFlagsConditional(ptr, cc, SR_X, ARM_CC_NE);
            else if ((update_mask & (SR_X | SR_C)) == SR_C)
                ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Calt, ARM_CC_NE);
            else
                ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Calt | SR_X, ARM_CC_NE);
        }
    }

    return ptr;
}

// BROKEN!!!!
uint32_t *EMIT_NEGX(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_count = 0;
    uint8_t dest = 0xff;
    uint8_t size = 0;

    /* Determine the size of operation */
    switch (opcode & 0x00c0)
    {
        case 0x0000: /* Byte operation */
            size = 1;
            break;
        case 0x0040: /* Short operation */
            size = 2;
            break;
        case 0x0080: /* Long operation */
            size = 4;
            break;
    }

    uint8_t cc = RA_GetCC(&ptr);
    if (size == 4) {
        uint8_t tmp = RA_AllocARMRegister(&ptr);

        *ptr++ = mvn_reg(tmp, cc, ROR, 7);
        *ptr++ = set_nzcv(tmp);

        RA_FreeARMRegister(&ptr, tmp);
    } else {
        *ptr++ = tst_immed(cc, 1, 31 & (32 - SRB_X));
    }

    if ((opcode & 0x0038) == 0)
    {
        if (size == 4)
        {
            dest = RA_MapM68kRegister(&ptr, opcode & 7);
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);

            *ptr++ = ngcs(dest, dest);
        }
        else
        {
            uint8_t tmp = RA_AllocARMRegister(&ptr);

            /* Fetch m68k register for write */
            dest = RA_MapM68kRegister(&ptr, opcode & 7);

            /* Mark register dirty */
            RA_SetDirtyM68kRegister(&ptr, opcode & 7);

            switch(size)
            {
                case 2:
                    *ptr++ = and_immed(tmp, dest, 16, 0);   // Take lower 16 bits of destination
                    *ptr++ = neg_reg(tmp, tmp, LSL, 0);     // negate
                    *ptr++ = b_cc(A64_CC_EQ, 2);            // Skip if X not set
                    *ptr++ = sub_immed(tmp, tmp, 1);

                    if (update_mask & SR_XVC) {

                        ptr = EMIT_ClearFlags(ptr, cc, SR_XVC);

                        uint8_t tmp_2 = RA_AllocARMRegister(&ptr);

                        *ptr++ = and_reg(tmp_2, tmp, dest, LSL, 0);

                        if (update_mask & SR_V)
                        {
                            *ptr++ = tbz(tmp_2, 15, 2);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Valt));
                        }
                        
                        if ((update_mask & SR_XC) == SR_XC)
                        {
                            *ptr++ = tbz(tmp, 16, 3);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                        }
                        else if ((update_mask & SR_XC) == SR_C)
                        {
                            *ptr++ = tbz(tmp, 16, 2);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                        }
                        else if ((update_mask & SR_XC) == SR_X)
                        {
                            *ptr++ = tbz(tmp, 16, 2);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                        }


                        update_mask &= ~SR_XVC;             // Don't nag anymore with the flags

                        RA_FreeARMRegister(&ptr, tmp_2);
                    }

                    if (update_mask & SR_NZ) {
                        *ptr++ = adds_reg(31, 31, tmp, LSL, 16);
                    }

                    *ptr++ = bfxil(dest, tmp, 0, 16);       // Insert result
                    break;
                case 1:
                    *ptr++ = and_immed(tmp, dest, 8, 0);    // Take lower 8 bits of destination
                    *ptr++ = neg_reg(tmp, tmp, LSL, 0);     // negate
                    *ptr++ = b_cc(A64_CC_EQ, 2);            // Skip if X not set
                    *ptr++ = sub_immed(tmp, tmp, 1);

                    if (update_mask & SR_XVC) {
                        ptr = EMIT_ClearFlags(ptr, cc, SR_XVC);

                        uint8_t tmp_2 = RA_AllocARMRegister(&ptr);

                        *ptr++ = and_reg(tmp_2, tmp, dest, LSL, 0);  // C at position 8 in tmp, V at position 7 in tmp2

                        if (update_mask & SR_V)
                        {
                            *ptr++ = tbz(tmp_2, 7, 2);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Valt));
                        }
                        
                        if ((update_mask & SR_XC) == SR_XC)
                        {
                            *ptr++ = tbz(tmp, 8, 3);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                        }
                        else if ((update_mask & SR_XC) == SR_C)
                        {
                            *ptr++ = tbz(tmp, 8, 2);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                        }
                        else if ((update_mask & SR_XC) == SR_X)
                        {
                            *ptr++ = tbz(tmp, 8, 2);
                            *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                        }

                        update_mask &= ~SR_XVC;             // Don't nag anymore with the flags

                        RA_FreeARMRegister(&ptr, tmp_2);
                    }

                    if (update_mask & SR_NZ) {
                        *ptr++ = adds_reg(31, 31, tmp, LSL, 24);
                    }

                    *ptr++ = bfxil(dest, tmp, 0, 8);
                    break;
            }

            RA_FreeARMRegister(&ptr, tmp);
        }
    }
    else
    {
        uint8_t src = RA_AllocARMRegister(&ptr);
        uint8_t tmp = RA_AllocARMRegister(&ptr);
        uint8_t mode = (opcode & 0x0038) >> 3;

        /* Load effective address */
        if (mode == 4 || mode == 3)
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 0, NULL);
        else
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 1, NULL);

        /* Fetch data into temporary register, perform add, store it back */
        switch (size)
        {
        case 4:
            if (mode == 4)
            {
                *ptr++ = ldr_offset_preindex(dest, src, -4);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldr_offset(dest, src, 0);

            *ptr++ = ngcs(tmp, src);

            if (mode == 3)
            {
                *ptr++ = str_offset_postindex(dest, tmp, 4);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = str_offset(dest, tmp, 0);
            break;
        case 2:
            if (mode == 4)
            {
                *ptr++ = ldrh_offset_preindex(dest, src, -2);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldrh_offset(dest, src, 0);

            *ptr++ = neg_reg(tmp, src, LSL, 0);     // negate
            *ptr++ = b_cc(A64_CC_EQ, 2);            // Skip if X not set
            *ptr++ = sub_immed(tmp, tmp, 1);

            if (update_mask & SR_XVC) {
                ptr = EMIT_ClearFlags(ptr, cc, SR_XVC);
                uint8_t tmp_2 = RA_AllocARMRegister(&ptr);

                *ptr++ = and_reg(tmp_2, tmp, src, LSL, 0);

                if (update_mask & SR_V)
                {
                    *ptr++ = tbz(tmp_2, 15, 2);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Valt));
                }
                
                if ((update_mask & SR_XC) == SR_XC)
                {
                    *ptr++ = tbz(tmp, 16, 3);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                }
                else if ((update_mask & SR_XC) == SR_C)
                {
                    *ptr++ = tbz(tmp, 16, 2);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                }
                else if ((update_mask & SR_XC) == SR_X)
                {
                    *ptr++ = tbz(tmp, 16, 2);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                }

                update_mask &= ~SR_XVC;             // Don't nag anymore with the flags

                RA_FreeARMRegister(&ptr, tmp_2);
            }

            if (update_mask & SR_NZ) {
                *ptr++ = adds_reg(31, 31, tmp, LSL, 16);
            }

            if (mode == 3)
            {
                *ptr++ = strh_offset_postindex(dest, tmp, 2);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = strh_offset(dest, tmp, 0);
            break;
        case 1:
            if (mode == 4)
            {
                *ptr++ = ldrb_offset_preindex(dest, src, (opcode & 7) == 7 ? -2 : -1);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = ldrb_offset(dest, src, 0);

            *ptr++ = neg_reg(tmp, src, LSL, 0);     // negate
            *ptr++ = b_cc(A64_CC_EQ, 2);            // Skip if X not set
            *ptr++ = sub_immed(tmp, tmp, 1);

            if (update_mask & SR_XVC) {
                ptr = EMIT_ClearFlags(ptr, cc, SR_XVC);

                uint8_t tmp_2 = RA_AllocARMRegister(&ptr);

                *ptr++ = and_reg(tmp_2, tmp, src, LSL, 0);

                if (update_mask & SR_V)
                {
                    *ptr++ = tbz(tmp_2, 7, 2);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Valt));
                }
                
                if ((update_mask & SR_XC) == SR_XC)
                {
                    *ptr++ = tbz(tmp, 8, 3);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                }
                else if ((update_mask & SR_XC) == SR_C)
                {
                    *ptr++ = tbz(tmp, 8, 2);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_Calt));
                }
                else if ((update_mask & SR_XC) == SR_X)
                {
                    *ptr++ = tbz(tmp, 8, 2);
                    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_X));
                }
                update_mask &= ~SR_XVC;             // Don't nag anymore with the flags

                RA_FreeARMRegister(&ptr, tmp_2);
            }

            if (update_mask & SR_NZ) {
                *ptr++ = adds_reg(31, 31, tmp, LSL, 24);
            }

            if (mode == 3)
            {
                *ptr++ = strb_offset_postindex(dest, tmp, (opcode & 7) == 7 ? 2 : 1);
                RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
            }
            else
                *ptr++ = strb_offset(dest, tmp, 0);
            break;
        }

        RA_FreeARMRegister(&ptr, src);
        RA_FreeARMRegister(&ptr, tmp);
    }

    RA_FreeARMRegister(&ptr, dest);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_count + 1));
    (*m68k_ptr) += ext_count;

    if (update_mask)
    {
        uint8_t cc = RA_ModifyCC(&ptr);
        uint8_t tmp = RA_AllocARMRegister(&ptr);

        if (update_mask & SR_Z) {
            *ptr++ = b_cc(A64_CC_EQ, 2);
            *ptr++ = bic_immed(cc, cc, 1, 31 & (32 - SRB_Z));
            update_mask &= ~SR_Z;
        }

        if (update_mask) {
            uint8_t alt_mask = update_mask;
            if ((alt_mask & 3) != 0 && (alt_mask & 3) < 3)
                alt_mask ^= 3;

            *ptr++ = mov_immed_u16(tmp, alt_mask, 0);
            *ptr++ = bic_reg(cc, cc, tmp, LSL, 0);
        }

        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
        
        if (update_mask & SR_V)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Valt, ARM_CC_VS);
        if (update_mask & (SR_X | SR_C)) {
            if ((update_mask & (SR_X | SR_C)) == SR_X)
                ptr = EMIT_SetFlagsConditional(ptr, cc, SR_X, ARM_CC_CC);
            else if ((update_mask & (SR_X | SR_C)) == SR_C)
                ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Calt, ARM_CC_CC);
            else
                ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Calt | SR_X, ARM_CC_CC);
        }
        
        RA_FreeARMRegister(&ptr, tmp);

    }

    return ptr;
}

uint32_t *EMIT_TST(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_count = 0;
    uint8_t immed = RA_AllocARMRegister(&ptr);
    uint8_t dest = 0xff;
    uint8_t size = 0;

    /* Load immediate into the register */
    switch (opcode & 0x00c0)
    {
        case 0x0000:    /* Byte operation */
            size = 1;
            break;
        case 0x0040:    /* Short operation */
            size = 2;
            break;
        case 0x0080:    /* Long operation */
            size = 4;
            break;
    }

    /* handle adding to register here */
    if ((opcode & 0x0038) == 0)
    {
        /* Fetch m68k register */
        dest = RA_MapM68kRegister(&ptr, opcode & 7);

        /* Perform add operation */
        switch (size)
        {
            case 4:
                *ptr++ = cmn_reg(31, dest, LSL, 0);
                break;
            case 2:
                *ptr++ = cmn_reg(31, dest, LSL, 16);
                break;
            case 1:
                *ptr++ = cmn_reg(31, dest, LSL, 24);
                break;
        }
    }
    else
    {
        /* Load effective address */
        ptr = EMIT_LoadFromEffectiveAddress(ptr, size, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 1, NULL);

        /* Fetch data into temporary register, perform add, store it back */
        switch (size)
        {
            case 4:
                *ptr++ = cmn_reg(31, dest, LSL, 0);
                break;
            case 2:
                *ptr++ = cmn_reg(31, dest, LSL, 16);
                break;
            case 1:
                *ptr++ = cmn_reg(31, dest, LSL, 24);
                break;
        }
    }

    RA_FreeARMRegister(&ptr, immed);
    RA_FreeARMRegister(&ptr, dest);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_count + 1));
    (*m68k_ptr) += ext_count;

    if (update_mask)
    {
        uint8_t cc = RA_ModifyCC(&ptr);
        ptr = EMIT_GetNZ00(ptr, cc, &update_mask);

        if (update_mask & SR_Z)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Z, ARM_CC_EQ);
        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
    }
    return ptr;
}

uint32_t *EMIT_TAS(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_count = 0;
    uint8_t dest = 0xff;
    uint8_t mode = (opcode & 0x0038) >> 3;
    uint8_t tmpreg = RA_AllocARMRegister(&ptr);
    uint8_t tmpresult = RA_AllocARMRegister(&ptr);
    uint8_t tmpstate = RA_AllocARMRegister(&ptr);

    /* handle TAS on register, just make a copy and then orr 0x80 on register */
    if ((opcode & 0x0038) == 0)
    {
        /* Fetch m68k register */
        dest = RA_MapM68kRegister(&ptr, opcode & 7);
        RA_SetDirtyM68kRegister(&ptr, opcode & 7);

        *ptr++ = mov_reg(tmpresult, dest);
        *ptr++ = orr_immed(dest, dest, 1, 25);
    }
    else
    {
        /* Load effective address */
        if (mode == 4 || mode == 3)
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 0, NULL);
        else
            ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, *m68k_ptr, &ext_count, 1, NULL);

        if (mode == 4)
        {
            *ptr++ = sub_immed(dest, dest, (opcode & 7) == 7 ? 2 : 1);
            RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
        }

        *ptr++ = ldxrb(dest, tmpresult);
        *ptr++ = orr_immed(tmpreg, tmpresult, 1, 25);
        *ptr++ = stxrb(dest, tmpreg, tmpstate);
        *ptr++ = cmp_reg(31, tmpstate, LSL, 0);
        *ptr++ = b_cc(A64_CC_NE, -4);

        if (mode == 3)
        {
            *ptr++ = add_immed(dest, dest, (opcode & 7) == 7 ? 2 : 1);
            RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
        }
    }

    RA_FreeARMRegister(&ptr, dest);
    RA_FreeARMRegister(&ptr, tmpreg);
    RA_FreeARMRegister(&ptr, tmpstate);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_count + 1));
    (*m68k_ptr) += ext_count;

    if (update_mask)
    {
        *ptr++ = cmn_reg(31, tmpresult, LSL, 24);
        uint8_t cc = RA_ModifyCC(&ptr);
        ptr = EMIT_GetNZ00(ptr, cc, &update_mask);

        if (update_mask & SR_Z)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Z, ARM_CC_EQ);
        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
    }

    RA_FreeARMRegister(&ptr, tmpresult);

    return ptr;
}

static uint32_t *EMIT_MOVEfromSR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;

    uint8_t cc = RA_ModifyCC(&ptr);
    uint8_t ext_words = 0;
    uint32_t *tmpptr;

    ptr = EMIT_FlushPC(ptr);

    /* Test if supervisor mode is active */
    *ptr++ = ands_immed(31, cc, 1, 32 - SRB_S);
    tmpptr = ptr;
    *ptr++ = b_cc(A64_CC_EQ, 23);

    uint8_t tmp_cc = RA_AllocARMRegister(&ptr);

    *ptr++ = mov_reg(tmp_cc, cc);
    *ptr++ = rbit(0, cc);
    *ptr++ = bfxil(tmp_cc, 0, 30, 2);

    ptr = EMIT_StoreToEffectiveAddress(ptr, 2, &tmp_cc, opcode & 0x3f, *m68k_ptr, &ext_words, 0);

    RA_FreeARMRegister(&ptr, tmp_cc);

    *tmpptr = b_cc(A64_CC_EQ, 2 + ptr - tmpptr);

    *ptr++ = add_immed(REG_PC, REG_PC, 2 * (ext_words + 1));
    tmpptr = ptr;
    *ptr++ = b_cc(A64_CC_AL, 0);

    /* No supervisor. Update USP, generate exception */
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);
    
    *tmpptr = b_cc(A64_CC_AL, ptr - tmpptr);
    *ptr++ = (uint32_t)(uintptr_t)tmpptr;
    *ptr++ = 1;
    *ptr++ = 0;
    *ptr++ = INSN_TO_LE(0xfffffffe);

    (*m68k_ptr) += ext_words;

    return ptr;
}

static uint32_t *EMIT_MOVEfromCCR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;

    uint8_t cc = RA_GetCC(&ptr);
    uint8_t ext_words = 0;

    if (opcode & 0x38)
    {
        uint8_t tmp = RA_AllocARMRegister(&ptr);

        *ptr++ = mov_reg(tmp, cc);
        *ptr++ = rbit(0, cc);
        *ptr++ = bic_immed(tmp, tmp, 11, 27);
        *ptr++ = bfxil(tmp, 0, 30, 2);
        ptr = EMIT_StoreToEffectiveAddress(ptr, 2, &tmp, opcode & 0x3f, *m68k_ptr, &ext_words, 0);

        RA_FreeARMRegister(&ptr, tmp);
    }
    else
    {
        /* Fetch m68k register */
        uint8_t dest = RA_MapM68kRegister(&ptr, opcode & 7);

        /* Mark register dirty */
        RA_SetDirtyM68kRegister(&ptr, opcode & 7);

        *ptr++ = bfi(dest, cc, 0, 5);
        *ptr++ = rbit(0, cc);
        *ptr++ = bic_immed(dest, dest, 11, 27);
        *ptr++ = bfxil(dest, 0, 30, 2);
    }

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_words + 1));
    (*m68k_ptr) += ext_words;

    return ptr;
}

static uint32_t *EMIT_MOVEtoSR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t cc = RA_ModifyCC(&ptr);
    uint8_t ext_words = 0;
    uint8_t src = 0xff;
    uint8_t orig = 1; //RA_AllocARMRegister(&ptr);
    uint8_t changed = 2; //RA_AllocARMRegister(&ptr);
    uint8_t sp = RA_MapM68kRegister(&ptr, 15);
    uint32_t *tmpptr;

    RA_SetDirtyM68kRegister(&ptr, 15);

    ptr = EMIT_FlushPC(ptr);

    /* If supervisor is not active, put an exception here */
    tmpptr = ptr;
    ptr++;
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);

    *tmpptr = tbnz(cc, SRB_S, 1 + ptr - tmpptr);
    tmpptr = ptr;
    ptr++;

    *ptr++ = mov_reg(orig, cc);
    *ptr++ = mov_immed_u16(changed, 0xf71f, 0);
    
    ptr = EMIT_LoadFromEffectiveAddress(ptr, 2, &src, opcode & 0x3f, *m68k_ptr, &ext_words, 1, NULL);
    if ((opcode & 0x38) == 0) /* Dn direct into SR */
    {
        uint8_t src_mod = RA_AllocARMRegister(&ptr);
        *ptr++ = mov_reg(src_mod, src);
        src = src_mod;
    }
    *ptr++ = rbit(0, src);
    *ptr++ = bfxil(src, 0, 30, 2);

    cc = RA_ModifyCC(&ptr);

    *ptr++ = and_reg(cc, changed, src, LSL, 0);
    *ptr++ = eor_reg(changed, orig, cc, LSL, 0);

    /* If neither S nor M changed, go further */
    *ptr++ = ands_immed(31, changed, 2, 32 - SRB_M);
    *ptr++ = b_cc(A64_CC_EQ, 12);

    /* S or M changed. First of all, store stack pointer to either ISP or MSP */
    *ptr++ = tbz(orig, SRB_M, 3);
    *ptr++ = mov_reg_to_simd(31, TS_S, 3, sp);  // Save to MSP
    *ptr++ = b(2);
    *ptr++ = mov_reg_to_simd(31, TS_S, 2, sp);  // Save to ISP

    /* Check if changing mode to user */
    *ptr++ = tbz(changed, SRB_S, 3);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 1);
    *ptr++ = b(5);
    *ptr++ = tbz(cc, SRB_M, 3);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 3);  // Load MSP
    *ptr++ = b(2);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 2);  // Load ISP

    /* Advance PC */
    *ptr++ = add_immed(REG_PC, REG_PC, 2 * (ext_words + 1));

    // Check if IPL is less than 6. If yes, enable ARM interrupts
    *ptr++ = and_immed(changed, cc, 3, 32 - SRB_IPL);
    *ptr++ = cmp_immed(changed, 5 << SRB_IPL);
    *ptr++ = b_cc(A64_CC_GT, 3);
    *ptr++ = msr_imm(3, 7, 7); // Enable interrupts
    *ptr++ = b(2);
    *ptr++ = msr_imm(3, 6, 7); // Mask interrupts

    *tmpptr = b(ptr - tmpptr);

    *ptr++ = INSN_TO_LE(0xffffffff);

    RA_FreeARMRegister(&ptr, src);
    RA_FreeARMRegister(&ptr, orig);
    RA_FreeARMRegister(&ptr, changed);

    (*m68k_ptr) += ext_words;

    return ptr;
}

static uint32_t *EMIT_MOVEtoCCR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;

    uint8_t ext_words = 0;
    uint8_t src = 0xff;
    uint8_t cc = RA_ModifyCC(&ptr);

    ptr = EMIT_LoadFromEffectiveAddress(ptr, 2, &src, opcode & 0x3f, *m68k_ptr, &ext_words, 1, NULL);
    
    if ((opcode & 0x38) == 0) /* Dn direct */
    {
        uint8_t src_mod = RA_AllocARMRegister(&ptr);
        *ptr++ = mov_reg(src_mod, src);
        src = src_mod;
    }
    *ptr++ = rbit(0, src);
    *ptr++ = bfxil(src, 0, 30, 2);
    *ptr++ = bfi(cc, src, 0, 5);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_words + 1));

    RA_FreeARMRegister(&ptr, src);
    
    (*m68k_ptr) += ext_words;

    return ptr;
}

static uint32_t *EMIT_EXT(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    uint8_t update_mask;
    uint8_t reg = RA_MapM68kRegister(&ptr, opcode & 7);
    uint8_t tmp = reg;
    RA_SetDirtyM68kRegister(&ptr, opcode & 7);
    uint8_t mode = (opcode >> 6) & 7;

    /* 
        If current instruction is byte-to-word and subsequent is word-to-long on the same register,
        then combine both to extb.l 
    */

    if ((mode == 2) && (opcode ^ cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0])) == 0x40) {
        (*m68k_ptr)++;
        mode = 7;
        (*insn_consumed)++;
        ptr = EMIT_AdvancePC(ptr, 2);
    } 

    /* Get update mask at this point first, otherwise the mask would suggest that flag change is not necessary */
    update_mask = M68K_GetSRMask(*m68k_ptr - 1);

    switch (mode)
    {
        case 2: /* Byte to Word */
            tmp = RA_AllocARMRegister(&ptr);

            *ptr++ = sxtb(tmp, reg);
            *ptr++ = bfi(reg, tmp, 0, 16);
            break;
        case 3: /* Word to Long */
            *ptr++ = sxth(reg, reg);
            break;
        case 7: /* Byte to Long */
            *ptr++ = sxtb(reg, reg);
            break;
    }

    ptr = EMIT_AdvancePC(ptr, 2);

    if (update_mask)
    {
        *ptr++ = cmp_reg(tmp, 31, LSL, 0);

        uint8_t cc = RA_ModifyCC(&ptr);
        ptr = EMIT_GetNZ00(ptr, cc, &update_mask);

        if (update_mask & SR_Z)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Z, ARM_CC_EQ);
        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
    }
    if (tmp != reg)
        RA_FreeARMRegister(&ptr, tmp);

    return ptr;
}

static uint32_t *EMIT_LINK32(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t sp;
    uint8_t displ;
    uint8_t reg;
    int32_t offset = (cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]) << 16) | cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[1]);

    displ = RA_AllocARMRegister(&ptr);
    *ptr++ = movw_immed_u16(displ, offset & 0xffff);
    *ptr++ = movt_immed_u16(displ, (offset >> 16) & 0xffff);
    sp = RA_MapM68kRegister(&ptr, 15);
    if (8 + (opcode & 7) == 15) {
        reg = RA_CopyFromM68kRegister(&ptr, 8 + (opcode & 7));
        *ptr++ = sub_immed(reg, reg, 4);
    }
    else {
        reg = RA_MapM68kRegister(&ptr, 8 + (opcode & 7));
    }
    *ptr++ = str_offset_preindex(sp, reg, -4);  /* SP = SP - 4; An -> (SP) */
    *ptr++ = mov_reg(reg, sp);
    RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));

    *ptr++ = add_reg(sp, sp, displ, LSL, 0);

    RA_SetDirtyM68kRegister(&ptr, 15);

    (*m68k_ptr)+=2;

    ptr = EMIT_AdvancePC(ptr, 6);
    RA_FreeARMRegister(&ptr, displ);
    RA_FreeARMRegister(&ptr, reg);
    return ptr;
}

static uint32_t *EMIT_LINK16(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t sp;
    uint8_t displ;
    uint8_t reg;
    int16_t offset = cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]);

    displ = RA_AllocARMRegister(&ptr);

    sp = RA_MapM68kRegister(&ptr, 15);
    if (8 + (opcode & 7) == 15) {
        reg = RA_CopyFromM68kRegister(&ptr, 8 + (opcode & 7));
        *ptr++ = sub_immed(reg, reg, 4);
    }
    else {
        reg = RA_MapM68kRegister(&ptr, 8 + (opcode & 7));
    }
    *ptr++ = str_offset_preindex(sp, reg, -4);  /* SP = SP - 4; An -> (SP) */
    *ptr++ = mov_reg(reg, sp);
    RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));

    if (offset > 0 && offset < 4096)
    {
        *ptr++ = add_immed(sp, sp, offset);
    }
    else if (offset < 0 && offset > -4096)
    {
        *ptr++ = sub_immed(sp, sp, -offset);
    }
    else if (offset != 0)
    {
        *ptr++ = mov_immed_u16(displ, offset, 0);
        if (offset < 0)
            *ptr++ = movk_immed_u16(displ, 0xffff, 1);
        *ptr++ = add_reg(sp, sp, displ, LSL, 0);
    }
    
    RA_SetDirtyM68kRegister(&ptr, 15);

    (*m68k_ptr)++;

    ptr = EMIT_AdvancePC(ptr, 4);
    RA_FreeARMRegister(&ptr, displ);
    RA_FreeARMRegister(&ptr, reg);

    return ptr;
}

static uint32_t *EMIT_SWAP(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t reg = RA_MapM68kRegister(&ptr, opcode & 7);
    RA_SetDirtyM68kRegister(&ptr, opcode & 7);

    *ptr++ = ror(reg, reg, 16);

    ptr = EMIT_AdvancePC(ptr, 2);

    if (update_mask)
    {
        *ptr++ = cmn_reg(31, reg, LSL, 0);
        uint8_t cc = RA_ModifyCC(&ptr);
        ptr = EMIT_GetNZ00(ptr, cc, &update_mask);

        if (update_mask & SR_Z)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_Z, ARM_CC_EQ);
        if (update_mask & SR_N)
            ptr = EMIT_SetFlagsConditional(ptr, cc, SR_N, ARM_CC_MI);
    }

    return ptr;
}

static uint32_t *EMIT_ILLEGAL(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    /* Illegal generates exception. Always */
    ptr = EMIT_FlushPC(ptr);
    ptr = EMIT_Exception(ptr, VECTOR_ILLEGAL_INSTRUCTION, 0);
    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_BKPT(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    /* Illegal generates exception. Always */
    ptr = EMIT_AdvancePC(ptr, 2);
    ptr = EMIT_FlushPC(ptr);
    ptr = EMIT_Exception(ptr, VECTOR_ILLEGAL_INSTRUCTION, 0);
    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_TRAP(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)m68k_ptr;

    ptr = EMIT_AdvancePC(ptr, 2);
    ptr = EMIT_FlushPC(ptr);

    ptr = EMIT_Exception(ptr, VECTOR_INT_TRAP(opcode & 15), 0);

    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_UNLK(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t sp;
    uint8_t reg;

    (void)m68k_ptr;

    sp = RA_MapM68kRegisterForWrite(&ptr, 15);
    reg = RA_MapM68kRegister(&ptr, 8 + (opcode & 7));

    *ptr++ = mov_reg(sp, reg);
    *ptr++ = ldr_offset_postindex(sp, reg, 4);

    RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
    RA_SetDirtyM68kRegister(&ptr, 15);

    ptr = EMIT_AdvancePC(ptr, 2);

    return ptr;
}

static uint32_t *EMIT_RESET(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    uint32_t *tmp;
    uint32_t *tmp2;
    ptr = EMIT_FlushPC(ptr);
    uint8_t cc = RA_ModifyCC(&ptr);

    /* Test if supervisor mode is active */
    *ptr++ = ands_immed(31, cc, 1, 32 - SRB_S);

    /* Branch to exception if not in supervisor */
    *ptr++ = b_cc(A64_CC_EQ, 3);
    *ptr++ = add_immed(REG_PC, REG_PC, 2);
    tmp = ptr;
    *ptr++ = b_cc(A64_CC_AL, 10);

    /* No supervisor. Update USP, generate exception */
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);

    tmp2 = ptr;
    *ptr++ = b_cc(A64_CC_AL, 0);

    *tmp = b_cc(A64_CC_AL, ptr - tmp);

#ifdef PISTORM

    void do_reset();

    union {
        uint64_t u64;
        uint16_t u16[4];
    } u;

    u.u64 = (uintptr_t)do_reset;

    *ptr++ = stp64_preindex(31, 0, 1, -256);
    for (int i=2; i < 30; i += 2)
        *ptr++ = stp64(31, i, i+1, i*8);
    *ptr++ = str64_offset(31, 30, 240);

    *ptr++ = mov64_immed_u16(1, u.u16[3], 0);
    *ptr++ = movk64_immed_u16(1, u.u16[2], 1);
    *ptr++ = movk64_immed_u16(1, u.u16[1], 2);
    *ptr++ = movk64_immed_u16(1, u.u16[0], 3);

    *ptr++ = blr(1);

    for (int i=2; i < 30; i += 2)
        *ptr++ = ldp64(31, i, i+1, i*8);
    *ptr++ = ldr64_offset(31, 30, 240);
    *ptr++ = ldp64_postindex(31, 0, 1, 256);

#endif

    *tmp2 = b_cc(A64_CC_AL, ptr - tmp2);

    *ptr++ = (uint32_t)(uintptr_t)tmp2;
    *ptr++ = 1;
    *ptr++ = 0;
    *ptr++ = INSN_TO_LE(0xfffffffe);
    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_NOP(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    *ptr++ = dsb_sy();
    ptr = EMIT_AdvancePC(ptr, 2);
    ptr = EMIT_FlushPC(ptr);

    return ptr;
}

static uint32_t *EMIT_STOP(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;

    uint32_t *tmpptr;
    uint16_t new_sr = cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]) & 0xf71f;
    uint8_t changed = RA_AllocARMRegister(&ptr);
    uint8_t orig = RA_AllocARMRegister(&ptr);
    uint8_t cc = RA_ModifyCC(&ptr);
    uint8_t sp = RA_MapM68kRegister(&ptr, 15);

    RA_SetDirtyM68kRegister(&ptr, 15);

    ptr = EMIT_FlushPC(ptr);

    /* Swap C and V in new SR */
    if ((new_sr & 3) != 0 && (new_sr & 3) < 3)
    {
        new_sr ^= 3;
    }

    /* If supervisor is not active, put an exception here */
    tmpptr = ptr;
    ptr++;
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);
    *tmpptr = tbnz(cc, SRB_S, 1 + ptr - tmpptr);
    tmpptr = ptr;
    ptr++;

    cc = RA_ModifyCC(&ptr);

    /* Put new value into SR, check what has changed */
    *ptr++ = mov_reg(orig, cc);
    *ptr++ = mov_immed_u16(cc, new_sr, 0);
    *ptr++ = eor_reg(changed, orig, cc, LSL, 0);

    /* Perform eventual stack switch */

    /* If neither S nor M changed, go further */
    *ptr++ = ands_immed(31, changed, 2, 32 - SRB_M);
    *ptr++ = b_cc(A64_CC_EQ, 12);

    /* S or M changed. First of all, store stack pointer to either ISP or MSP */
    *ptr++ = tbz(orig, SRB_M, 3);
    *ptr++ = mov_reg_to_simd(31, TS_S, 3, sp);  // Save to MSP
    *ptr++ = b(2);
    *ptr++ = mov_reg_to_simd(31, TS_S, 2, sp);  // Save to ISP

    /* Check if changing mode to user */
    *ptr++ = tbz(changed, SRB_S, 3);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 1);
    *ptr++ = b(5);
    *ptr++ = tbz(cc, SRB_M, 3);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 3);  // Load MSP
    *ptr++ = b(2);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 2);  // Load ISP

    /* Now do what stop does - wait for interrupt */
    *ptr++ = add_immed(REG_PC, REG_PC, 4);

    // Check if IPL is less than 6. If yes, enable ARM interrupts
    *ptr++ = and_immed(changed, cc, 3, 32 - SRB_IPL);
    *ptr++ = cmp_immed(changed, 5 << SRB_IPL);
    *ptr++ = b_cc(A64_CC_GT, 3);
    *ptr++ = msr_imm(3, 7, 7); // Enable interrupts
    *ptr++ = b(2);
    *ptr++ = msr_imm(3, 6, 7); // Mask interrupts

#ifndef PISTORM
    /* Non pistorm machines wait for interrupt only */
    *ptr++ = wfi();
#else
    uint8_t tmpreg = RA_AllocARMRegister(&ptr);
    uint8_t ctx = RA_GetCTX(&ptr);
    uint32_t *start, *end;

    // Don't wait for event if IRQ is already pending
    *ptr++ = ldr_offset(ctx, tmpreg, __builtin_offsetof(struct M68KState, INT));
    *ptr++ = cbnz(tmpreg, 4);

    start = ptr;

    /* PiStorm waits for event and checks INT - aggregate of ~IPL0 and ARM */
    *ptr++ = wfe();
    *ptr++ = ldr_offset(ctx, tmpreg, __builtin_offsetof(struct M68KState, INT));
    end = ptr;
    *ptr++ = cbz(tmpreg, start - end);

    RA_FreeARMRegister(&ptr, tmpreg);
#endif

    *tmpptr = b(ptr - tmpptr);

    *ptr++ = INSN_TO_LE(0xffffffff);

    RA_FreeARMRegister(&ptr, changed);
    RA_FreeARMRegister(&ptr, orig);

    (*m68k_ptr) += 1;

    return ptr;
}

static uint32_t *EMIT_RTE(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    uint8_t tmp = 1; //RA_AllocARMRegister(&ptr);
    uint8_t sp = RA_MapM68kRegister(&ptr, 15);
    uint8_t cc = RA_ModifyCC(&ptr);
    uint8_t changed = 2; //RA_AllocARMRegister(&ptr);
    uint8_t orig = 3; //RA_AllocARMRegister(&ptr);
    uint32_t *tmpptr;
    uint32_t *branch_privilege;
    uint32_t *branch_format;

    RA_SetDirtyM68kRegister(&ptr, 15);

    ptr = EMIT_FlushPC(ptr);

    // First check if supervisor mode
    /* If supervisor is not active, put an exception here */
    branch_privilege = ptr;
    ptr++;
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);
    *branch_privilege = tbnz(cc, SRB_S, 1 + ptr - branch_privilege);
    branch_privilege = ptr;
    ptr++;

    // Now check frame format
    *ptr++ = ldrh_offset(sp, tmp, 6);
    *ptr++ = lsr(tmp, tmp, 12);

    // Is format valid?
    *ptr++ = cmp_immed(tmp, 2);
    tmpptr = ptr;
    // Forat 2 is supported, go further
    *ptr++ = b_cc(A64_CC_EQ, 0);
    *ptr++ = cmp_immed(tmp, 0);
    *ptr++ = b_cc(A64_CC_EQ, 0);

    ptr = EMIT_Exception(ptr, VECTOR_FORMAT_ERROR, 0);

    branch_format = ptr;
    *ptr++ = 0;

    // Patch both jumps to here
    *tmpptr = b_cc(A64_CC_EQ, ptr - tmpptr);
    tmpptr += 2;
    *tmpptr = b_cc(A64_CC_EQ, ptr - tmpptr);

    /* Fetch sr from stack */
    *ptr++ = ldrh_offset_postindex(sp, changed, 2);
    /* Reverse C and V */
    *ptr++ = rbit(orig, changed);
    *ptr++ = bfxil(changed, orig, 30, 2);
    /* Fetch PC from stack, advance sp so that format word is skipped */
    *ptr++ = ldr_offset_postindex(sp, REG_PC, 6);

    /* In case of format 2, skip subsequent longword on stack */
    *ptr++ = cmp_immed(tmp, 2);
    *ptr++ = b_cc(A64_CC_NE, 2);
    *ptr++ = add_immed(sp, sp, 4);

    cc = RA_ModifyCC(&ptr);

    /* Use two EORs to generate changed mask and update SR */
    *ptr++ = mov_reg(orig, cc);
    *ptr++ = eor_reg(changed, changed, cc, LSL, 0);
    *ptr++ = eor_reg(cc, changed, cc, LSL, 0);       

    /* Now since stack is cleaned up, perform eventual stack switch */
    /* If neither S nor M changed, go further */
    *ptr++ = ands_immed(31, changed, 2, 32 - SRB_M);
    *ptr++ = b_cc(A64_CC_EQ, 12);

    /* S or M changed. First of all, store stack pointer to either ISP or MSP */
    *ptr++ = tbz(orig, SRB_M, 3);
    *ptr++ = mov_reg_to_simd(31, TS_S, 3, sp);  // Save to MSP
    *ptr++ = b(2);
    *ptr++ = mov_reg_to_simd(31, TS_S, 2, sp);  // Save to ISP

    /* Check if changing mode to user */
    *ptr++ = tbz(changed, SRB_S, 3);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 1);
    *ptr++ = b(5);
    *ptr++ = tbz(cc, SRB_M, 3);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 3);  // Load MSP
    *ptr++ = b(2);
    *ptr++ = mov_simd_to_reg(sp, 31, TS_S, 2);  // Load ISP

    // Check if IPL is less than 6. If yes, enable ARM interrupts
    *ptr++ = and_immed(changed, cc, 3, 32 - SRB_IPL);
    *ptr++ = cmp_immed(changed, 5 << SRB_IPL);
    *ptr++ = b_cc(A64_CC_GT, 3);
    *ptr++ = msr_imm(3, 7, 7); // Enable interrupts
    *ptr++ = b(2);
    *ptr++ = msr_imm(3, 6, 7); // Mask interrupts

    *branch_privilege = b(ptr - branch_privilege);
    *branch_format = b(ptr - branch_format);

    // Instruction always breaks translation
    *ptr++ = INSN_TO_LE(0xffffffff);

    RA_FreeARMRegister(&ptr, tmp);
    RA_FreeARMRegister(&ptr, changed);
    RA_FreeARMRegister(&ptr, orig);

    return ptr;
}

static uint32_t *EMIT_RTD(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    uint8_t tmp = RA_AllocARMRegister(&ptr);
    uint8_t tmp2 = RA_AllocARMRegister(&ptr);
    uint8_t sp = RA_MapM68kRegister(&ptr, 15);
    int16_t addend = cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]);

    /* Fetch return address from stack */
    *ptr++ = ldr_offset_postindex(sp, tmp2, 4);

    if (addend > -4096 && addend < 4096)
    {
        if (addend < 0)
        {
            *ptr++ = sub_immed(sp, sp, -addend);
        }
        else
        {
            *ptr++ = add_immed(sp, sp, addend);
        }
    }
    else
    {
        if (addend < 0)
        {
            *ptr++ = movn_immed_u16(tmp, -addend - 1, 0);
        }
        else
        {
            *ptr++ = mov_immed_u16(tmp, addend, 0);
        }
        *ptr++ = add_reg(sp, sp, tmp, LSL, 0);
    }

    ptr = EMIT_ResetOffsetPC(ptr);
    *ptr++ = mov_reg(REG_PC, tmp2);
    RA_SetDirtyM68kRegister(&ptr, 15);
    *ptr++ = INSN_TO_LE(0xffffffff);
    RA_FreeARMRegister(&ptr, tmp);
    RA_FreeARMRegister(&ptr, tmp2);

    return ptr;
}

static uint32_t *EMIT_RTS(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;

    uint8_t sp = RA_MapM68kRegister(&ptr, 15);

    /* Fetch return address from stack */
    *ptr++ = ldr_offset_postindex(sp, REG_PC, 4);
    ptr = EMIT_ResetOffsetPC(ptr);
    RA_SetDirtyM68kRegister(&ptr, 15);

    /*
        If Return Stack is not empty, the branch was inlined. Pop instruction counter here,
        and go back to inlining the code
    */
    uint16_t *ret_addr = M68K_PopReturnAddress(NULL);
    if (ret_addr != (uint16_t *)0xffffffff)
    {
        /* 
            If return stack is used, make sure that the code below is at the address we were expecting
            This must not be the case - it would be sufficient if code has modified the return address on the stack
        */
        uint8_t reg = RA_AllocARMRegister(&ptr);
        uint32_t *tmp;
        uint32_t ret = (uint32_t)(uintptr_t)ret_addr;
        *ptr++ = mov_immed_u16(reg, ret & 0xffff, 0);
        *ptr++ = movk_immed_u16(reg, ret >> 16, 1);
        *ptr++ = cmp_reg(reg, REG_PC, LSL, 0);
        tmp = ptr;
        *ptr++ = b_cc(ARM_CC_EQ, 0);

        *m68k_ptr = ret_addr;

        RA_FreeARMRegister(&ptr, reg);

        *tmp = b_cc(ARM_CC_EQ, ptr - tmp);
        *ptr++ = (uint32_t)(uintptr_t)tmp;
        *ptr++ = 1;
        *ptr++ = 0;
        *ptr++ = INSN_TO_LE(0xfffffffe);
    }
    else
        *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_TRAPV(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;

    uint8_t cc = RA_GetCC(&ptr);
    uint32_t *tmpptr;
    ptr = EMIT_AdvancePC(ptr, 2);
    ptr = EMIT_FlushPC(ptr);

    *ptr++ = ands_immed(31, cc, 1, 32 - SRB_Valt);
    tmpptr = ptr;
    *ptr++ = b_cc(A64_CC_EQ, 0);
    
    ptr = EMIT_Exception(ptr, VECTOR_TRAPcc, 2, (uint32_t)(uintptr_t)(*m68k_ptr - 1));

    *tmpptr = b_cc(A64_CC_EQ, ptr - tmpptr);
    *ptr++ = (uint32_t)(uintptr_t)tmpptr;
    *ptr++ = 1;
    *ptr++ = 0;
    *ptr++ = INSN_TO_LE(0xfffffffe);

    return ptr;
}

static uint32_t *EMIT_RTR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)opcode;
    (void)m68k_ptr;

    uint8_t tmp = RA_AllocARMRegister(&ptr);
    uint8_t mask = RA_AllocARMRegister(&ptr);
    uint8_t sp = RA_MapM68kRegister(&ptr, 15);

    /* Fetch status byte from stack */
    *ptr++ = ldrh_offset_postindex(sp, tmp, 2);
    /* Reverse C and V */
    *ptr++ = rbit(0, tmp);
    *ptr++ = bfxil(tmp, 0, 30, 2);
    /* Insert XNZCV into SR */
    uint8_t cc = RA_ModifyCC(&ptr);
    *ptr++ = bfi(cc, tmp, 0, 5);

    /* Fetch return address from stack */
    *ptr++ = ldr_offset_postindex(sp, REG_PC, 4);
    ptr = EMIT_ResetOffsetPC(ptr);
    RA_SetDirtyM68kRegister(&ptr, 15);
    *ptr++ = INSN_TO_LE(0xffffffff);
    RA_FreeARMRegister(&ptr, mask);
    RA_FreeARMRegister(&ptr, tmp);

    return ptr;
}

static uint32_t *EMIT_MOVEC(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;

    uint16_t opcode2 = cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]);
    uint8_t dr = opcode & 1;
    uint8_t reg = RA_MapM68kRegister(&ptr, opcode2 >> 12);
    uint8_t ctx = RA_GetCTX(&ptr);
    uint8_t cc = RA_ModifyCC(&ptr);
    uint8_t tmp = 0xff;
    uint8_t sp = 0xff;
    uint32_t *tmpptr;
    int illegal = 0;
    extern uint32_t debug_range_min;
    extern uint32_t debug_range_max;
    extern int disasm;
    extern int debug;
    union {
        uint16_t u16[4];
        uint64_t u64;
    } u;

    (*m68k_ptr) += 1;
    ptr = EMIT_FlushPC(ptr);

    /* If supervisor is not active, put an exception here */
    tmpptr = ptr;
    ptr++;
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);
    *tmpptr = tbnz(cc, SRB_S, 1 + ptr - tmpptr);
    tmpptr = ptr;
    ptr++;

    if (dr)
    {
        switch (opcode2 & 0xfff)
        {
            case 0x000: // SFC
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = and_immed(tmp, reg, 3, 0);
                *ptr++ = strb_offset(ctx, tmp, __builtin_offsetof(struct M68KState, SFC));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x001: // DFC
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = and_immed(tmp, reg, 3, 0);
                *ptr++ = strb_offset(ctx, tmp, __builtin_offsetof(struct M68KState, DFC));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x800: // USP
                *ptr++ = mov_reg_to_simd(31, TS_S, 1, reg);
                break;
            case 0x801: // VBR
                *ptr++ = str_offset(ctx, reg, __builtin_offsetof(struct M68KState, VBR));
                break;
            case 0x002: // CACR
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = bic_immed(tmp, reg, 15, 0);
                *ptr++ = bic_immed(tmp, tmp, 15, 16);
                *ptr++ = mov_reg_to_simd(31, TS_S, 0, tmp);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x803: // MSP
                sp = RA_MapM68kRegister(&ptr, 15);
                RA_SetDirtyM68kRegister(&ptr, 15);
                *ptr++ = tbz(cc, SRB_M, 2);
                *ptr++ = mov_reg(sp, reg);
                *ptr++ = mov_reg_to_simd(31, TS_S, 3, reg);
                break;
            case 0x804: // ISP
                sp = RA_MapM68kRegister(&ptr, 15);
                RA_SetDirtyM68kRegister(&ptr, 15);
                *ptr++ = tbnz(cc, SRB_M, 2);
                *ptr++ = mov_reg(sp, reg);
                *ptr++ = mov_reg_to_simd(31, TS_S, 2, reg);
                break;
            case 0x0ea: /* JITSCFTHRESH - Maximal number of JIT units for "soft" cache flush */
                *ptr++ = str_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_SOFTFLUSH_THRESH));
                break;
            case 0x0eb: /* JITCTRL - JIT control register */
                *ptr++ = str_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CONTROL));
                break;
            case 0x0ed: /* DBGCTRL */
            {
                uint8_t tmp2 = RA_AllocARMRegister(&ptr);
                tmp = RA_AllocARMRegister(&ptr);
                u.u64 = (uintptr_t)&debug;
                *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                *ptr++ = ubfx(tmp2, reg, 0, 2);
                *ptr++ = str_offset(tmp, tmp2, 0);
                
                if (_abs((intptr_t)&debug - (intptr_t)&disasm) < 255)
                {
                    int delta = (uintptr_t)&disasm - (uintptr_t)&debug;
                    *ptr++ = ubfx(tmp2, reg, 2, 1);
                    *ptr++ = stur_offset(tmp, tmp2, delta);
                }
                else
                {
                    u.u64 = (uintptr_t)&disasm;
                    *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                    *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                    *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                    *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                    *ptr++ = ubfx(tmp2, reg, 2, 1);
                    *ptr++ = str_offset(tmp, tmp2, 0);
                }
                RA_FreeARMRegister(&ptr, tmp);
                RA_FreeARMRegister(&ptr, tmp2);
                break;
            }
            case 0x0ee: /* DBGADDRLO */
                tmp = RA_AllocARMRegister(&ptr);
                u.u64 = (uintptr_t)&debug_range_min;
                *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                *ptr++ = str_offset(tmp, reg, 0);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0ef: /* DBGADDRHI */
                tmp = RA_AllocARMRegister(&ptr);
                u.u64 = (uintptr_t)&debug_range_max;
                *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                *ptr++ = str_offset(tmp, reg, 0);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x1e0: /* JITCTRL2 - JIT second control register */
                *ptr++ = str_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CONTROL2));
                break;
            case 0x003: // TCR - write bits 15, 14, read all zeros for now
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = bic_immed(tmp, reg, 30, 16);
                *ptr++ = bic_immed(tmp, tmp, 1, 32 - 15); // Clear E bit, do not allow turning on MMU
                *ptr++ = strh_offset(ctx, tmp, __builtin_offsetof(struct M68KState, TCR));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x004: // ITT0
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = movn_immed_u16(tmp, 0x1c9b, 0);
                *ptr++ = and_reg(tmp, tmp, reg, LSL, 0);
                *ptr++ = str_offset(ctx, tmp, __builtin_offsetof(struct M68KState, ITT0));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x005: // ITT1
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = movn_immed_u16(tmp, 0x1c9b, 0);
                *ptr++ = and_reg(tmp, tmp, reg, LSL, 0);
                *ptr++ = str_offset(ctx, tmp, __builtin_offsetof(struct M68KState, ITT1));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x006: // DTT0
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = movn_immed_u16(tmp, 0x1c9b, 0);
                *ptr++ = and_reg(tmp, tmp, reg, LSL, 0);
                *ptr++ = str_offset(ctx, tmp, __builtin_offsetof(struct M68KState, DTT0));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x007: // DTT1
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = movn_immed_u16(tmp, 0x1c9b, 0);
                *ptr++ = and_reg(tmp, tmp, reg, LSL, 0);
                *ptr++ = str_offset(ctx, tmp, __builtin_offsetof(struct M68KState, DTT1));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x805: // MMUSR
                *ptr++ = str_offset(ctx, reg, __builtin_offsetof(struct M68KState, MMUSR));
                break;
            case 0x806: // URP
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = bic_immed(tmp, reg, 9, 0);
                *ptr++ = str_offset(ctx, tmp, __builtin_offsetof(struct M68KState, URP));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x807: // SRP
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = bic_immed(tmp, reg, 9, 0);
                *ptr++ = str_offset(ctx, tmp, __builtin_offsetof(struct M68KState, SRP));
                RA_FreeARMRegister(&ptr, tmp);
                break;
            default:
                ptr = EMIT_Exception(ptr, VECTOR_ILLEGAL_INSTRUCTION, 0);
                illegal = 1;
                break;
        }
    }
    else
    {
        switch (opcode2 & 0xfff)
        {
            case 0x000: // SFC
                *ptr++ = ldrb_offset(ctx, reg, __builtin_offsetof(struct M68KState, SFC));
                break;
            case 0x001: // DFC
                *ptr++ = ldrb_offset(ctx, reg, __builtin_offsetof(struct M68KState, DFC));
                break;
            case 0x800: // USP
                *ptr++ = mov_simd_to_reg(reg, 31, TS_S, 1);
                break;
            case 0x801: // VBR
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, VBR));
                break;
            case 0x002: // CACR
                *ptr++ = mov_simd_to_reg(reg, 31, TS_S, 0);
                break;
            case 0x803: // MSP
                sp = RA_MapM68kRegister(&ptr, 15);
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = tst_immed(cc, 1, 32 - SRB_M);
                *ptr++ = mov_simd_to_reg(tmp, 31, TS_S, 3);
                *ptr++ = csel(reg, sp, tmp, A64_CC_NE);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x804: // ISP
                sp = RA_MapM68kRegister(&ptr, 15);
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = tst_immed(cc, 1, 32 - SRB_M);
                *ptr++ = mov_simd_to_reg(tmp, 31, TS_S, 2);
                *ptr++ = csel(reg, sp, tmp, A64_CC_EQ);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0e0: /* CNTFRQ - speed of counter clock in Hz */
                *ptr++ = mrs(reg, 3, 3, 14, 0, 0);
                break;
            case 0x0e1: /* CNTVALLO - lower 32 bits of the counter */
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = mrs(tmp, 3, 3, 14, 0, 1);
                *ptr++ = mov_reg(reg, tmp);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0e2: /* CNTVALHI - higher 32 bits of the counter */
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = mrs(tmp, 3, 3, 14, 0, 1);
                *ptr++ = lsr64(reg, tmp, 32);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0e3: /* INSNCNTLO - lower 32 bits of m68k instruction counter */
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = mov_simd_to_reg(tmp, 30, TS_D, 0);
                *ptr++ = add64_immed(tmp, tmp, insn_count & 0xfff);
                if (insn_count & 0xfff000)
                    *ptr++ = add64_immed_lsl12(tmp, tmp, insn_count >> 12);
                *ptr++ = mov_reg(reg, tmp);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0e4: /* INSNCNTHI - higher 32 bits of m68k instruction counter */
                tmp = RA_AllocARMRegister(&ptr);
                *ptr++ = mov_simd_to_reg(tmp, 30, TS_D, 0);
                *ptr++ = add64_immed(tmp, tmp, insn_count & 0xfff);
                if (insn_count & 0xfff000)
                    *ptr++ = add64_immed_lsl12(tmp, tmp, insn_count >> 12);
                *ptr++ = lsr64(reg, tmp, 32);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0e5: /* ARMCNTLO - lower 32 bits of ARM instruction counter */
                {
                    uint8_t hosttmp = RA_AllocARMRegister(&ptr);
                    tmp = RA_AllocARMRegister(&ptr);
                    *ptr++ = mov_simd_to_reg(hosttmp, 28, TS_D, 0);
                    *ptr++ = mrs(tmp, 3, 3, 9, 13, 0);
                    *ptr++ = sub64_reg(tmp, tmp, hosttmp, LSL, 0);
                    *ptr++ = mov_reg(reg, tmp);
                    RA_FreeARMRegister(&ptr, tmp);
                    RA_FreeARMRegister(&ptr, hosttmp);
                }
                break;
            case 0x0e6: /* ARMCNTHI - higher 32 bits of ARM instruction counter */
                {
                    uint8_t hosttmp = RA_AllocARMRegister(&ptr);
                    tmp = RA_AllocARMRegister(&ptr);
                    *ptr++ = mov_simd_to_reg(hosttmp, 28, TS_D, 0);
                    *ptr++ = mrs(tmp, 3, 3, 9, 13, 0);
                    *ptr++ = sub64_reg(tmp, tmp, hosttmp, LSL, 0);
                    *ptr++ = lsr64(reg, tmp, 32);
                    RA_FreeARMRegister(&ptr, tmp);
                    RA_FreeARMRegister(&ptr, hosttmp);
                }
                break;
            case 0x0e7: /* JITSIZE - size of JIT cache, in bytes */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CACHE_TOTAL));
                break;
            case 0x0e8: /* JITFREE - free space in JIT cache, in bytes */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CACHE_FREE));
                break;
            case 0x0e9: /* JITCOUNT - Number of JIT units in cache */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_UNIT_COUNT));
                break;
            case 0x0ea: /* JITSCFTHRESH - Maximal number of JIT units for "soft" cache flush */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_SOFTFLUSH_THRESH));
                break;
            case 0x0eb: /* JITCTRL - JIT control register */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CONTROL));
                break;
            case 0x0ec: /* JITCMISS */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CACHE_MISS));
                break;
            case 0x0ed: /* DBGCTRL */
            {
                uint8_t tmp2 = RA_AllocARMRegister(&ptr);
                tmp = RA_AllocARMRegister(&ptr);
                u.u64 = (uintptr_t)&debug;
                *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);

                *ptr++ = ldr_offset(tmp, tmp2, 0);
                *ptr++ = ubfx(reg, tmp2, 0, 2);

                if (_abs((intptr_t)&debug - (intptr_t)&disasm) < 255)
                {
                    int delta = (uintptr_t)&disasm - (uintptr_t)&debug;
                    *ptr++ = ldur_offset(tmp, tmp2, delta);
                }
                else
                {
                    u.u64 = (uintptr_t)&disasm;
                    *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                    *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                    *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                    *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                    *ptr++ = ldr_offset(tmp, tmp2, 0);
                }
                *ptr++ = cbz(tmp2, 2);
                *ptr++ = orr_immed(reg, reg, 1, 30);

                RA_FreeARMRegister(&ptr, tmp);
                RA_FreeARMRegister(&ptr, tmp2);
                break;
            }
            case 0x0ee: /* DBGADDRLO */
                tmp = RA_AllocARMRegister(&ptr);
                u.u64 = (uintptr_t)&debug_range_min;
                *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                *ptr++ = ldr_offset(tmp, reg, 0);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x0ef: /* DBGADDRHI */
                tmp = RA_AllocARMRegister(&ptr);
                u.u64 = (uintptr_t)&debug_range_max;
                *ptr++ = mov64_immed_u16(tmp, u.u16[3], 0);
                *ptr++ = movk64_immed_u16(tmp, u.u16[2], 1);
                *ptr++ = movk64_immed_u16(tmp, u.u16[1], 2);
                *ptr++ = movk64_immed_u16(tmp, u.u16[0], 3);
                *ptr++ = ldr_offset(tmp, reg, 0);
                RA_FreeARMRegister(&ptr, tmp);
                break;
            case 0x1e0: /* JITCTRL2 - JIT second control register */
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, JIT_CONTROL2));
                break;
            case 0x003: // TCR - write bits 15, 14, read all zeros for now
                *ptr++ = ldrh_offset(ctx, reg, __builtin_offsetof(struct M68KState, TCR));
                break;
            case 0x004: // ITT0
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, ITT0));
                break;
            case 0x005: // ITT1
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, ITT1));
                break;
            case 0x006: // DTT0
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, DTT0));
                break;
            case 0x007: // DTT1
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, DTT1));
                break;
            case 0x805: // MMUSR
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, MMUSR));
                break;
            case 0x806: // URP
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, URP));
                break;
            case 0x807: // SRP
                *ptr++ = ldr_offset(ctx, reg, __builtin_offsetof(struct M68KState, SRP));
                break;
            default:
                ptr = EMIT_Exception(ptr, VECTOR_ILLEGAL_INSTRUCTION, 0);
                illegal = 1;
                break;
        }
        RA_SetDirtyM68kRegister(&ptr, opcode2 >> 12);
    }

    if (!illegal) {
        *ptr++ = add_immed(REG_PC, REG_PC, 4);
    }

    *tmpptr = b(ptr - tmpptr);

    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_MOVEUSP(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    (void)m68k_ptr;

    uint32_t *tmp;
    uint8_t cc = RA_ModifyCC(&ptr);
    uint8_t an = RA_MapM68kRegister(&ptr, 8 + (opcode & 7));

    ptr = EMIT_FlushPC(ptr);

    /* If supervisor is not active, put an exception here */
    tmp = ptr;
    ptr++;
    ptr = EMIT_Exception(ptr, VECTOR_PRIVILEGE_VIOLATION, 0);
    *tmp = tbnz(cc, SRB_S, 1 + ptr - tmp);
    tmp = ptr;
    ptr++;

    if (opcode & 8)
    {
        *ptr++ = mov_simd_to_reg(an, 31, TS_S, 1);
        RA_SetDirtyM68kRegister(&ptr, 8 + (opcode & 7));
    }
    else
    {
        *ptr++ = mov_reg_to_simd(31, TS_S, 1, an);
    }

    *ptr++ = add_immed(REG_PC, REG_PC, 2);

    *tmp = b(ptr - tmp);

    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_JSR(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t ext_words = 0;
    uint8_t ea = 0xff;
    uint8_t sp = 0xff;

    sp = RA_MapM68kRegister(&ptr, 15);
    ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &ea, opcode & 0x3f, (*m68k_ptr), &ext_words, 1, NULL);
    ptr = EMIT_AdvancePC(ptr, 2 * (ext_words + 1));
    ptr = EMIT_FlushPC(ptr);
    *ptr++ = str_offset_preindex(sp, REG_PC, -4);
    RA_SetDirtyM68kRegister(&ptr, 15);
    ptr = EMIT_ResetOffsetPC(ptr);
    *ptr++ = mov_reg(REG_PC, ea);
    (*m68k_ptr) += ext_words;
    RA_FreeARMRegister(&ptr, ea);
    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_JMP(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t ext_words = 0;
    uint8_t ea = REG_PC;

    ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &ea, opcode & 0x3f, (*m68k_ptr), &ext_words, 0, NULL);
    ptr = EMIT_ResetOffsetPC(ptr);
    (*m68k_ptr) += ext_words;
    RA_FreeARMRegister(&ptr, ea);
    *ptr++ = INSN_TO_LE(0xffffffff);

    return ptr;
}

static uint32_t *EMIT_NBCD(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;

    uint8_t update_mask = M68K_GetSRMask(*m68k_ptr - 1);
    uint8_t ext_words = 0;

    uint8_t ea = -1;
    uint8_t cc = RA_ModifyCC(&ptr);

    uint8_t hi = RA_AllocARMRegister(&ptr);
    uint8_t lo = RA_AllocARMRegister(&ptr);

    /* Dn mode */
    if ((opcode & 0x38) == 0)
    {
        uint8_t dn = RA_MapM68kRegister(&ptr, opcode & 7);

        *ptr++ = and_immed(lo, dn, 4, 0);
        *ptr++ = and_immed(hi, dn, 4, 28);
    }
    else
    {
        uint8_t t = RA_AllocARMRegister(&ptr);

        /* Load EA */
        ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &ea, opcode & 0x3f, *m68k_ptr, &ext_words, 0, NULL);

        /* -(An) mode? Decrease EA */
        if ((opcode & 0x38) == 0x20)
        {
            if ((opcode & 7) == 7) {
                *ptr++ = ldrb_offset_preindex(ea, t, -2);
            }
            else {
                *ptr++ = ldrb_offset_preindex(ea, t, -1);
            }
        }
        else {
            *ptr++ = ldrb_offset(ea, t, 0);
        }

        *ptr++ = and_immed(lo, t, 4, 0);
        *ptr++ = and_immed(hi, t, 4, 28);

        RA_FreeARMRegister(&ptr, t);
    }

    uint8_t tmp = RA_AllocARMRegister(&ptr);
    uint8_t result = RA_AllocARMRegister(&ptr);

    // Negate both loaded nibbles
    *ptr++ = sub_reg(lo, 31, lo, LSL, 0);
    *ptr++ = sub_reg(hi, 31, hi, LSL, 0);

    // If X was set decrement lower nibble
    *ptr++ = tbz(cc, SRB_X, 2);
    *ptr++ = sub_immed(lo, lo, 1);

    // Fix overflow in lower nibble
    *ptr++ = cmp_immed(lo, 10);
    *ptr++ = b_cc(A64_CC_CC, 2);
    *ptr++ = sub_immed(lo, lo, 6);

    *ptr++ = add_reg(result, lo, hi, LSL, 0);

    // Fix overflow in higher nibble
    *ptr++ = and_immed(tmp, result, 5, 28);
    *ptr++ = cmp_immed(tmp, 0xa0);
    *ptr++ = b_cc(A64_CC_CC, 2);
    *ptr++ = sub_immed(result, result, 0x60);

    if (update_mask & SR_XC) {
        
        cc = RA_ModifyCC(&ptr);

        switch (update_mask & SR_XC)
        {
            case SR_C:
                *ptr++ = bic_immed(cc, cc, 1, 32 - SRB_Calt);
                *ptr++ = orr_immed(tmp, cc, 1, 32 - SRB_Calt);
                break;
            case SR_X:
                *ptr++ = bic_immed(cc, cc, 1, 32 - SRB_X);
                *ptr++ = orr_immed(tmp, cc, 1, 32 - SRB_X);
                break;
            default:
                *ptr++ = mov_immed_u16(tmp, SR_XCalt, 0);
                *ptr++ = bic_reg(cc, cc, tmp, LSL, 0);
                *ptr++ = orr_reg(tmp, cc, tmp, LSL, 0);
                break;
        }

        *ptr++ = csel(cc, tmp, cc, A64_CC_CS);
    }

    if (update_mask & SR_Z) {
        cc = RA_ModifyCC(&ptr);

        *ptr++ = ands_immed(31, result, 8, 0);
        *ptr++ = bic_immed(tmp, cc, 1, 32 - SRB_Z);
        *ptr++ = csel(cc, tmp, cc, A64_CC_NE);
    }

    /* Dn mode */
    if ((opcode & 0x38) == 0)
    {
        uint8_t dn = RA_MapM68kRegister(&ptr, opcode & 7);

        RA_SetDirtyM68kRegister(&ptr, opcode & 7);

        *ptr++ = bfi(dn, result, 0, 8);
    }
    else
    {
        /* (An)+ mode? Increase EA */
        if ((opcode & 0x38) == 0x18)
        {
            if ((opcode & 7) == 7) {
                *ptr++ = strb_offset_postindex(ea, result, 2);
            }
            else {
                *ptr++ = strb_offset_postindex(ea, result, 1);
            }
        }
        else {
            *ptr++ = strb_offset(ea, result, 0);
        }
    }

    RA_FreeARMRegister(&ptr, tmp);
    RA_FreeARMRegister(&ptr, hi);
    RA_FreeARMRegister(&ptr, lo);
    RA_FreeARMRegister(&ptr, result);
    RA_FreeARMRegister(&ptr, ea);

    (*m68k_ptr) += ext_words;
    ptr = EMIT_AdvancePC(ptr, 2*(ext_words + 1));

    return ptr;
}

static uint32_t *EMIT_PEA(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t sp = 0xff;
    uint8_t ea = 0xff;
    uint8_t ext_words = 0;

    ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &ea, opcode & 0x3f, (*m68k_ptr), &ext_words, 1, NULL);
    (*m68k_ptr) += ext_words;

    sp = RA_MapM68kRegister(&ptr, 15);
    RA_SetDirtyM68kRegister(&ptr, 15);

    *ptr++ = str_offset_preindex(sp, ea, -4);

    RA_FreeARMRegister(&ptr, sp);
    RA_FreeARMRegister(&ptr, ea);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_words + 1));

    return ptr;
}

static uint32_t *EMIT_MOVEM(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t dir = (opcode >> 10) & 1;
    uint8_t size = (opcode >> 6) & 1;
    uint16_t mask = cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]);
    uint8_t block_size = 0;
    uint8_t ext_words = 0;
    extern int debug;
    uint32_t *ptr_orig = ptr;

    (*m68k_ptr)++;

    ptr = EMIT_AdvancePC(ptr, 2);

    for (int i=0; i < 16; i++)
    {
        if (mask & (1 << i))
            block_size += size ? 4:2;
    }

    if (dir == 0)
    {
        uint8_t base = 0xff;

        ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &base, opcode & 0x3f, *m68k_ptr, &ext_words, 0, NULL);

        /* Pre-decrement mode? Decrease the base now */
        if ((opcode & 0x38) == 0x20)
        {
            uint8_t offset = 0;
            uint8_t tmp_base_reg = 0xff;

            RA_SetDirtyM68kRegister(&ptr, (opcode & 7) + 8);

            /* Check if base register is on the list */
            if (mask & (0x8000 >> ((opcode & 7) + 8)))
            {
                tmp_base_reg = RA_AllocARMRegister(&ptr);
                *ptr++ = sub_immed(tmp_base_reg, base, size ? 4 : 2);
            }

            /* In pre-decrement the register order is reversed */
            uint8_t rt1 = 0xff;

            for (int i=0; i < 16; i++)
            {
                if (mask & (0x8000 >> i))
                {
                    uint8_t reg = (i == ((opcode & 7) + 8) ? tmp_base_reg : RA_MapM68kRegister(&ptr, i));

                    if (size) {
                        if (rt1 == 0xff)
                            rt1 = reg;
                        else {
                            if (offset == 0)
                                *ptr++ = stp_preindex(base, rt1, reg, -block_size);
                            else
                                *ptr++ = stp(base, rt1, reg, offset);
                            offset += 8;
                            rt1 = 0xff;
                        }
                    }
                    else
                    {
                        if (offset == 0)
                            *ptr++ = strh_offset_preindex(base, reg, -block_size);
                        else
                            *ptr++ = strh_offset(base, reg, offset);

                        offset += 2;
                    }
                }
            }
            if (rt1 != 0xff) {
                if (offset == 0)
                    *ptr++ = str_offset_preindex(base, rt1, -block_size);
                else
                    *ptr++ = str_offset(base, rt1, offset);
            }

            RA_FreeARMRegister(&ptr, tmp_base_reg);
        }
        else
        {
            uint8_t offset = 0;
            uint8_t rt1 = 0xff;

            for (int i=0; i < 16; i++)
            {
                if (mask & (1 << i))
                {
                    uint8_t reg = RA_MapM68kRegister(&ptr, i);
                    if (size) {
                        if (rt1 == 0xff)
                            rt1 = reg;
                        else {
                            *ptr++ = stp(base, rt1, reg, offset);
                            offset += 8;
                            rt1 = 0xff;
                        }
                    }
                    else
                    {
                        *ptr++ = strh_offset(base, reg, offset);
                        offset += 2;
                    }
                }
            }
            if (rt1 != 0xff)
                *ptr++ = str_offset(base, rt1, offset);
        }

        RA_FreeARMRegister(&ptr, base);
    }
    else
    {
        uint8_t base = 0xff;
        uint8_t offset = 0;

        ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &base, opcode & 0x3f, *m68k_ptr, &ext_words, 0, NULL);

        uint8_t rt1 = 0xff;

        for (int i=0; i < 16; i++)
        {
            if (mask & (1 << i))
            {
                /* Keep base register high in LRU */
                if (((opcode & 0x38) == 0x18)) RA_MapM68kRegister(&ptr, (opcode & 7) + 8);

                uint8_t reg = RA_MapM68kRegisterForWrite(&ptr, i);
                if (size) {
                    if ((((opcode & 0x38) == 0x18) && (i == (opcode & 7) + 8))) {
                        /* If rt1 was set, flush it now and reset, skip the base register */
                        if (rt1 != 0xff) {
                            *ptr++ = ldr_offset(base, rt1, offset);
                            rt1 = 0xff;
                            offset += 4;
                        }
                        offset += 4;
                        continue;
                    }

                    if (rt1 == 0xff)
                        rt1 = reg;
                    else {
                        if (block_size == 8 && (opcode & 0x38) == 0x18)
                            *ptr++ = ldp_postindex(base, rt1, reg, 8);
                        else 
                            *ptr++ = ldp(base, rt1, reg, offset);
                        offset += 8;
                        rt1 = 0xff;
                    }
                }
                else
                {
                    if (!(((opcode & 0x38) == 0x18) && (i == (opcode & 7) + 8)))
                        *ptr++ = ldrsh_offset(base, reg, offset);
                    offset += 2;
                }
            }
        }
        if (rt1 != 0xff) {
            *ptr++ = ldr_offset(base, rt1, offset);
        }

        /* Post-increment mode? Increase the base now */
        if ((opcode & 0x38) == 0x18 && !(block_size == 8 && size))
        {
            *ptr++ = add_immed(base, base, block_size);
            RA_SetDirtyM68kRegister(&ptr, (opcode & 7) + 8);
        }

        RA_FreeARMRegister(&ptr, base);
    }

    ptr = EMIT_AdvancePC(ptr, 2*(ext_words + 1));
    (*m68k_ptr) += ext_words;

    /* No opcode was emited? At least flush PC counter now */
    if (ptr == ptr_orig)
        ptr = EMIT_FlushPC(ptr);

    return ptr;
}

static uint32_t *EMIT_LEA(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;
    uint8_t dest = 0xff;
    uint8_t ea = 0xff;
    uint8_t ext_words = 0;

    /* On AArch64 we can always map destination reg for write - it is always there! */
    dest = RA_MapM68kRegisterForWrite(&ptr, 8 + ((opcode >> 9) & 7));

    /* Mode 2, (An) in case of LEA is a special case - just move the reg to destination */
    if ((opcode & 0x38) == 0x10)
    {
        uint8_t src = RA_MapM68kRegister(&ptr, 8 + (opcode & 7));
        *ptr++ = mov_reg(dest, src);
    }
    else
        ptr = EMIT_LoadFromEffectiveAddress(ptr, 0, &dest, opcode & 0x3f, (*m68k_ptr), &ext_words, 1, NULL);

    (*m68k_ptr) += ext_words;

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_words + 1));
    RA_FreeARMRegister(&ptr, ea);

    return ptr;
}

static uint32_t *EMIT_CHK(uint32_t *ptr, uint16_t opcode, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    (void)insn_consumed;

    uint32_t opcode_address = (uint32_t)(uintptr_t)((*m68k_ptr) - 1);
    uint8_t ext_words = 0;
    uint8_t dn = RA_MapM68kRegister(&ptr, (opcode >> 9) & 7);
    uint8_t src = -1;
    uint8_t tmpreg = RA_AllocARMRegister(&ptr);

    /* word operation */
    if (opcode & 0x80)
    {
        ptr = EMIT_LoadFromEffectiveAddress(ptr, 2, &src, opcode & 0x3f, *m68k_ptr, &ext_words, 0, NULL);
        *ptr++ = lsl(src, src, 16);
    }
    else
    {
        ptr = EMIT_LoadFromEffectiveAddress(ptr, 4, &src, opcode & 0x3f, *m68k_ptr, &ext_words, 1, NULL);
    }

    uint8_t cc = RA_ModifyCC(&ptr);

    // Clear Z, V and C flags, set Z back if operand is zero
    *ptr++ = mov_immed_u16(tmpreg, SR_NCalt, 0);
    *ptr++ = bic_reg(cc, cc, tmpreg, LSL, 0);
    *ptr++ = tbz(src, 31, 2);
    *ptr++ = orr_immed(cc, cc, 1, 32 - SRB_N);

    ptr = EMIT_AdvancePC(ptr, 2 * (ext_words + 1));
    (*m68k_ptr) += ext_words;

    ptr = EMIT_FlushPC(ptr);

    /* Check if Dn < 0 */
    if (opcode & 0x80)
        *ptr++ = adds_reg(31, 31, dn, LSL, 16); 
    else
        *ptr++ = adds_reg(31, 31, dn, LSL, 0); 

    /* Jump to exception generator if negative */
    *ptr++ = b_cc(A64_CC_MI, 5);
    
    /* Check if Dn > src */
    if (opcode & 0x80)
        *ptr++ = subs_reg(31, src, dn, LSL, 16);
    else
        *ptr++ = subs_reg(31, src, dn, LSL, 0);
    
    uint32_t *tmp = ptr;
    *ptr++ = b_cc(A64_CC_GE, 0);
    *ptr++ = bic_immed(cc, cc, 1, 31 & (32 - SRB_N));
    *ptr++ = b(2);

    *ptr++ = orr_immed(cc, cc, 1, 31 & (32 - SRB_N));

    ptr = EMIT_Exception(ptr, VECTOR_CHK, 2, opcode_address);

    RA_FreeARMRegister(&ptr, src);
    RA_FreeARMRegister(&ptr, tmpreg);

    *tmp = b_cc(A64_CC_GE, ptr - tmp);
    *ptr++ = (uint32_t)(uintptr_t)tmp;
    *ptr++ = 1;
    *ptr++ = 0;
    *ptr++ = INSN_TO_LE(0xfffffffe);

    return ptr;
}

static struct OpcodeDef InsnTable[4096] = {
    [00300 ... 00307] = { { .od_EmitMulti = EMIT_MOVEfromSR }, NULL, SR_ALL, 0, 1, 0, 2 },
    [00320 ... 00347] = { { .od_EmitMulti = EMIT_MOVEfromSR }, NULL, SR_ALL, 0, 1, 0, 2 },
    [00350 ... 00371] = { { .od_EmitMulti = EMIT_MOVEfromSR }, NULL, SR_ALL, 0, 1, 1, 2 },

    [01300 ... 01307] = { { .od_EmitMulti = EMIT_MOVEfromCCR }, NULL, SR_CCR, 0, 1, 0, 2 },
    [01320 ... 01347] = { { .od_EmitMulti = EMIT_MOVEfromCCR }, NULL, SR_CCR, 0, 1, 0, 2 },
    [01350 ... 01371] = { { .od_EmitMulti = EMIT_MOVEfromCCR }, NULL, SR_CCR, 0, 1, 1, 2 },

    [03300 ... 03307] = { { .od_EmitMulti = EMIT_MOVEtoSR }, NULL, SR_S, SR_ALL, 1, 0, 2 },
    [03320 ... 03347] = { { .od_EmitMulti = EMIT_MOVEtoSR }, NULL, SR_S, SR_ALL, 1, 0, 2 },
    [03350 ... 03374] = { { .od_EmitMulti = EMIT_MOVEtoSR }, NULL, SR_S, SR_ALL, 1, 1, 2 },

    [02300 ... 02307] = { { .od_EmitMulti = EMIT_MOVEtoCCR }, NULL, 0, SR_CCR, 1, 0, 2 },
    [02320 ... 02347] = { { .od_EmitMulti = EMIT_MOVEtoCCR }, NULL, 0, SR_CCR, 1, 0, 2 },
    [02350 ... 02374] = { { .od_EmitMulti = EMIT_MOVEtoCCR }, NULL, 0, SR_CCR, 1, 1, 2 },

    [04200 ... 04207] = { { .od_EmitMulti = EMIT_EXT }, NULL, 0, SR_NZVC, 1, 0, 0 },
    [04300 ... 04307] = { { .od_EmitMulti = EMIT_EXT }, NULL, 0, SR_NZVC, 1, 0, 0 },
    [04700 ... 04707] = { { .od_EmitMulti = EMIT_EXT }, NULL, 0, SR_NZVC, 1, 0, 0 },

    [04010 ... 04017] = { { .od_EmitMulti = EMIT_LINK32 }, NULL, 0, 0, 3, 0, 0 },
    [07120 ... 07127] = { { .od_EmitMulti = EMIT_LINK16 }, NULL, 0, 0, 2, 0, 0 },

    [04100 ... 04107] = { { .od_EmitMulti = EMIT_SWAP }, NULL, 0, SR_NZVC, 1, 0, 0 },
    [0xafc]           = { { .od_EmitMulti = EMIT_ILLEGAL }, NULL, SR_CCR, 0, 1, 0, 0 },
    [0xe40 ... 0xe4f] = { { .od_EmitMulti = EMIT_TRAP }, NULL, SR_CCR, 0, 1, 0, 0 },
    [07130 ... 07137] = { { .od_EmitMulti = EMIT_UNLK }, NULL, 0, 0, 1, 0, 0 },
    [0xe70]           = { { .od_EmitMulti = EMIT_RESET }, NULL, SR_S, 0, 1, 0, 0 },
    [0xe71]           = { { .od_EmitMulti = EMIT_NOP }, NULL, 0, 0, 1, 0, 0 },
    [0xe72]           = { { .od_EmitMulti = EMIT_STOP }, NULL, SR_S, SR_ALL, 2, 0, 0 },
    [0xe73]           = { { .od_EmitMulti = EMIT_RTE }, NULL, SR_S, SR_ALL, 1, 0, 0 },
    [0xe74]           = { { .od_EmitMulti = EMIT_RTD }, NULL, 0, 0, 2, 0, 0 },
    [0xe75]           = { { .od_EmitMulti = EMIT_RTS }, NULL, 0, 0, 1, 0, 0 },
    [0xe76]           = { { .od_EmitMulti = EMIT_TRAPV }, NULL, SR_CCR, 0, 1, 0, 0 },
    [0xe77]           = { { .od_EmitMulti = EMIT_RTR }, NULL, 0, SR_CCR, 1, 0, 0 },
    [0xe7a ... 0xe7b] = { { .od_EmitMulti = EMIT_MOVEC }, NULL, SR_S, 0, 2, 0, 4 },
    [0xe60 ... 0xe6f] = { { .od_EmitMulti = EMIT_MOVEUSP }, NULL, SR_S, 0, 1, 0, 4 },
    [04110 ... 04117] = { { .od_EmitMulti = EMIT_BKPT }, NULL, SR_ALL, 0, 1, 0, 0 },      // BKPT

    [07320 ... 07327] = { { .od_EmitMulti = EMIT_JMP }, NULL, 0, 0, 1, 0, 0 },
    [07350 ... 07373] = { { .od_EmitMulti = EMIT_JMP }, NULL, 0, 0, 1, 1, 0 },

    [07220 ... 07227] = { { .od_EmitMulti = EMIT_JSR }, NULL, 0, 0, 1, 0, 0 },
    [07250 ... 07273] = { { .od_EmitMulti = EMIT_JSR }, NULL, 0, 0, 1, 1, 0 },

    [00000 ... 00007] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 0, 1 },
    [00100 ... 00107] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 0, 2 },
    [00200 ... 00207] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 0, 4 },

    [00020 ... 00047] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 0, 1 },
    [00120 ... 00147] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 0, 2 },
    [00220 ... 00247] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 0, 4 },
    
    [00050 ... 00071] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 1, 1 },
    [00150 ... 00171] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 1, 2 },
    [00250 ... 00271] = { { .od_EmitMulti = EMIT_NEGX }, NULL, SR_XZ, SR_CCR, 1, 1, 4 },

    [01000 ... 01007] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [01100 ... 01107] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 0, 2 },
    [01200 ... 01207] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 0, 4 },

    [01020 ... 01047] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [01120 ... 01147] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 0, 2 },
    [01220 ... 01247] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 0, 4 },

    [01050 ... 01071] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 1, 1 },
    [01150 ... 01171] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 1, 2 },
    [01250 ... 01271] = { { .od_EmitMulti = EMIT_CLR }, NULL, 0, SR_NZVC, 1, 1, 4 },

    [02000 ... 02007] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 0, 1 },
    [02100 ... 02107] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 0, 2 },
    [02200 ... 02207] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 0, 4 },

    [02020 ... 02047] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 0, 1 },
    [02120 ... 02147] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 0, 2 },
    [02220 ... 02247] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 0, 4 },

    [02050 ... 02071] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 1, 1 },
    [02150 ... 02171] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 1, 2 },
    [02250 ... 02271] = { { .od_EmitMulti = EMIT_NEG }, NULL, 0, SR_CCR, 1, 1, 4 },

    [03000 ... 03007] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [03100 ... 03107] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 0, 2 },
    [03200 ... 03207] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 0, 4 },

    [03020 ... 03047] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [03120 ... 03147] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 0, 2 },
    [03220 ... 03247] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 0, 4 },

    [03050 ... 03071] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 1, 1 },
    [03150 ... 03171] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 1, 2 },
    [03250 ... 03271] = { { .od_EmitMulti = EMIT_NOT }, NULL, 0, SR_NZVC, 1, 1, 4 },

    [05000 ... 05007] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [05020 ... 05047] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [05050 ... 05074] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 1, 1 },
    
    [05100 ... 05147] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 0, 2 },
    [05150 ... 05174] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 1, 2 },
    
    [05200 ... 05247] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 0, 4 },
    [05250 ... 05274] = { { .od_EmitMulti = EMIT_TST }, NULL, 0, SR_NZVC, 1, 1, 4 },

    [04000 ... 04007] = { { .od_EmitMulti = EMIT_NBCD }, NULL, SR_XZ, SR_XZC, 1, 0, 1 },
    [04020 ... 04047] = { { .od_EmitMulti = EMIT_NBCD }, NULL, SR_XZ, SR_XZC, 1, 0, 1 },
    [04050 ... 04071] = { { .od_EmitMulti = EMIT_NBCD }, NULL, SR_XZ, SR_XZC, 1, 1, 1 },

    [04120 ... 04127] = { { .od_EmitMulti = EMIT_PEA }, NULL, 0, 0, 1, 0, 4 },
    [04150 ... 04173] = { { .od_EmitMulti = EMIT_PEA }, NULL, 0, 0, 1, 1, 4 },

    [05300 ... 05307] = { { .od_EmitMulti = EMIT_TAS }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [05320 ... 05347] = { { .od_EmitMulti = EMIT_TAS }, NULL, 0, SR_NZVC, 1, 0, 1 },
    [05350 ... 05371] = { { .od_EmitMulti = EMIT_TAS }, NULL, 0, SR_NZVC, 1, 1, 1 },

    [06000 ... 06007] = { { .od_EmitMulti = EMIT_MUL_DIV_ }, NULL, 0, SR_NZVC, 2, 0, 4 },
    [06020 ... 06047] = { { .od_EmitMulti = EMIT_MUL_DIV_ }, NULL, 0, SR_NZVC, 2, 0, 4 },
    [06050 ... 06074] = { { .od_EmitMulti = EMIT_MUL_DIV_ }, NULL, 0, SR_NZVC, 2, 1, 4 },
    [06100 ... 06107] = { { .od_EmitMulti = EMIT_MUL_DIV_ }, NULL, 0, SR_NZVC, 2, 0, 4 },
    [06120 ... 06147] = { { .od_EmitMulti = EMIT_MUL_DIV_ }, NULL, 0, SR_NZVC, 2, 0, 4 },
    [06150 ... 06174] = { { .od_EmitMulti = EMIT_MUL_DIV_ }, NULL, 0, SR_NZVC, 2, 1, 4 },

    [04220 ... 04227] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 0, 2 },
    [04320 ... 04327] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 0, 4 },
    [04240 ... 04247] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 0, 2 },
    [04340 ... 04347] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 0, 4 },
    [04250 ... 04271] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 1, 2 },
    [04350 ... 04371] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 1, 4 },

    [06220 ... 06237] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 0, 2 },
    [06320 ... 06337] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 0, 4 },
    [06250 ... 06273] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 1, 2 },
    [06350 ... 06373] = { { .od_EmitMulti = EMIT_MOVEM }, NULL, 0, 0, 2, 1, 4 },

    [00720 ... 00727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [00750 ... 00773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [01720 ... 01727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [01750 ... 01773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [02720 ... 02727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [02750 ... 02773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [03720 ... 03727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [03750 ... 03773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [04720 ... 04727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [04750 ... 04773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [05720 ... 05727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [05750 ... 05773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [06720 ... 06727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [06750 ... 06773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },
    [07720 ... 07727] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 0, 4 },
    [07750 ... 07773] = { { .od_EmitMulti = EMIT_LEA }, NULL, 0, 0, 1, 1, 4 },

    [00600 ... 00607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [00620 ... 00647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [00650 ... 00674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [00400 ... 00407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [00420 ... 00447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [00450 ... 00474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [01600 ... 01607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [01620 ... 01647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [01650 ... 01674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [01400 ... 01407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [01420 ... 01447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [01450 ... 01474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [02600 ... 02607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [02620 ... 02647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [02650 ... 02674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [02400 ... 02407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [02420 ... 02447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [02450 ... 02474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [03600 ... 03607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [03620 ... 03647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [03650 ... 03674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [03400 ... 03407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [03420 ... 03447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [03450 ... 03474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [04600 ... 04607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [04620 ... 04647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [04650 ... 04674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [04400 ... 04407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [04420 ... 04447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [04450 ... 04474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [05600 ... 05607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [05620 ... 05647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [05650 ... 05674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [05400 ... 05407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [05420 ... 05447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [05450 ... 05474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [06600 ... 06607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [06620 ... 06647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [06650 ... 06674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [06400 ... 06407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [06420 ... 06447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [06450 ... 06474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

    [07600 ... 07607] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [07620 ... 07647] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 2 },
    [07650 ... 07674] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 2 },
    [07400 ... 07407] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [07420 ... 07447] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 0, 4 },
    [07450 ... 07474] = { { .od_EmitMulti = EMIT_CHK }, NULL, SR_CCR, SR_NZVC, 1, 1, 4 },

};

uint32_t *EMIT_line4(uint32_t *ptr, uint16_t **m68k_ptr, uint16_t *insn_consumed)
{
    uint16_t opcode = cache_read_16(ICACHE, (uintptr_t)&(*m68k_ptr)[0]);
    (*m68k_ptr)++;
    *insn_consumed = 1;

    if (InsnTable[opcode & 0xfff].od_EmitMulti) {
        ptr = InsnTable[opcode & 0xfff].od_EmitMulti(ptr, opcode, m68k_ptr, insn_consumed);
    }
    else
    {
        ptr = EMIT_FlushPC(ptr);
        ptr = EMIT_InjectDebugString(ptr, "[JIT] opcode %04x at %08x not implemented\n", opcode, *m68k_ptr - 1);
        *ptr++ = svc(0x100);
        *ptr++ = svc(0x101);
        *ptr++ = svc(0x103);
        *ptr++ = (uint32_t)(uintptr_t)(*m68k_ptr - 8);
        *ptr++ = 48;
        ptr = EMIT_Exception(ptr, VECTOR_ILLEGAL_INSTRUCTION, 0);
        *ptr++ = INSN_TO_LE(0xffffffff);
    }

    return ptr;
}

uint32_t GetSR_Line4(uint16_t opcode)
{
    /* If instruction is in the table, return what flags it needs (shifted 16 bits left) and flags it sets */
    if (InsnTable[opcode & 0xfff].od_Emit) {
        return (InsnTable[opcode & 0xfff].od_SRNeeds << 16) | InsnTable[opcode & 0xfff].od_SRSets;
    }
    /* Instruction not found, i.e. it needs all flags and sets none (ILLEGAL INSTRUCTION exception) */
    else {
        kprintf("Undefined Line4\n");
        return SR_CCR << 16;
    }
}


int M68K_GetLine4Length(uint16_t *insn_stream)
{
    uint16_t opcode = cache_read_16(ICACHE, (uintptr_t)&(*insn_stream));
    
    int length = 0;
    int need_ea = 0;
    int opsize = 0;

    if (InsnTable[opcode & 0xfff].od_Emit) {
        length = InsnTable[opcode & 0xfff].od_BaseLength;
        need_ea = InsnTable[opcode & 0xfff].od_HasEA;
        opsize = InsnTable[opcode & 0xfff].od_OpSize;
    }

    if (need_ea) {
        length += SR_GetEALength(&insn_stream[length], opcode & 0x3f, opsize);
    }

    return length;
}


#ifdef PISTORM
void do_reset()
{
    void ps_pulse_reset();

    struct Size sz = get_display_size();
    init_display(sz, NULL, NULL);

    ps_pulse_reset();
}
#endif
