/*
 * VARCem	Virtual ARchaeological Computer EMulator.
 *		An emulator of (mostly) x86-based PC systems and devices,
 *		using the ISA,EISA,VLB,MCA  and PCI system buses, roughly
 *		spanning the era between 1981 and 1995.
 *
 *		This file is part of the VARCem Project.
 *
 *		Dynamic Recompiler for Intel 32-bit systems.
 *
 * Version:	@(#)codegen_x86.c	1.0.6	2019/05/17
 *
 * Authors:	Fred N. van Kempen, <decwiz@yahoo.com>
 *		Sarah Walker, <tommowalker@tommowalker.co.uk>
 *		Miran Grca, <mgrca8@gmail.com>
 *
 *		Copyright 2018,2019 Fred N. van Kempen.
 *		Copyright 2008-2018 Sarah Walker.
 *		Copyright 2016-2018 Miran Grca.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free  Software  Foundation; either  version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is  distributed in the hope that it will be useful, but
 * WITHOUT   ANY  WARRANTY;  without  even   the  implied  warranty  of
 * MERCHANTABILITY  or FITNESS  FOR A PARTICULAR  PURPOSE. See  the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the:
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place - Suite 330
 *   Boston, MA 02111-1307
 *   USA.
 */
#if defined(i386) || defined(__i386) || defined(__i386__) || defined(_X86_) || defined(_M_IX86) || defined(_M_X64)

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../emu.h"
#include "cpu.h"
#include "../mem.h"
#include "x86.h"
#include "x86_flags.h"
#include "x86_ops.h"
#include "x87.h"

#include "386_common.h"

#include "codegen.h"
#include "codegen_ops.h"
#include "codegen_ops_x86.h"

#ifdef __linux__
#include <sys/mman.h>
#include <unistd.h>
#endif
#if defined _WIN32
#undef ERROR
#include <windows.h>
#endif

int codegen_flat_ds, codegen_flat_ss;
int mmx_ebx_ecx_loaded;
int codegen_flags_changed = 0;
int codegen_fpu_entered = 0;
int codegen_mmx_entered = 0;
int codegen_fpu_loaded_iq[8];
x86seg *op_ea_seg;
int op_ssegs;
uint32_t op_old_pc;

uint32_t recomp_page = -1;

int host_reg_mapping[NR_HOST_REGS];
int host_reg_xmm_mapping[NR_HOST_XMM_REGS];
codeblock_t *codeblock;
codeblock_t **codeblock_hash;


int block_current = 0;
static int block_num;
int block_pos;

int cpu_recomp_flushes, cpu_recomp_flushes_latched;
int cpu_recomp_evicted, cpu_recomp_evicted_latched;
int cpu_recomp_reuse, cpu_recomp_reuse_latched;
int cpu_recomp_removed, cpu_recomp_removed_latched;

uint32_t codegen_endpc;

int codegen_block_cycles;
static int codegen_block_ins;
static int codegen_block_full_ins;

static uint32_t last_op32;
static x86seg *last_ea_seg;
static int last_ssegs;

static uint32_t mem_abrt_rout;
uint32_t mem_load_addr_ea_b;
uint32_t mem_load_addr_ea_w;
uint32_t mem_load_addr_ea_l;
uint32_t mem_load_addr_ea_q;
uint32_t mem_store_addr_ea_b;
uint32_t mem_store_addr_ea_w;
uint32_t mem_store_addr_ea_l;
uint32_t mem_store_addr_ea_q;
uint32_t mem_load_addr_ea_b_no_abrt;
uint32_t mem_store_addr_ea_b_no_abrt;
uint32_t mem_load_addr_ea_w_no_abrt;
uint32_t mem_store_addr_ea_w_no_abrt;
uint32_t mem_load_addr_ea_l_no_abrt;
uint32_t mem_store_addr_ea_l_no_abrt;
uint32_t mem_check_write;
uint32_t mem_check_write_w;
uint32_t mem_check_write_l;

static uint32_t gen_MEM_LOAD_ADDR_EA_B()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];

        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(4+1);
        addbyte(0x0f); /*MOVZX EAX, B[EDX+EDI]*/
        addbyte(0xb6);
        addbyte(0x04);
        addbyte(0x3a);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmembl*/
        addlong((uint32_t)readmemb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*MOVZX EAX, AL*/
        addbyte(0xb6);
        addbyte(0xc0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/
        
        return addr;
}

static uint32_t gen_MEM_LOAD_ADDR_EA_W()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];

        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 1*/
        addbyte(0xc7);
        addlong(1);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+4+1);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(4+1);
        addbyte(0x0f); /*MOVZX EAX, [EDX+EDI]W*/
        addbyte(0xb7);
        addbyte(0x04);
        addbyte(0x3a);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmemwl*/
        addlong((uint32_t)readmemwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*MOVZX EAX, AX*/
        addbyte(0xb7);
        addbyte(0xc0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/
        
        return addr;
}

static uint32_t gen_MEM_LOAD_ADDR_EA_L()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];

        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 3*/
        addbyte(0xc7);
        addlong(3);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+3+1);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+1);
        addbyte(0x8b); /*MOV EAX, [EDX+EDI]*/
        addbyte(0x04);
        addbyte(0x3a);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmemll*/
        addlong((uint32_t)readmemll - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/
        
        return addr;
}

static uint32_t gen_MEM_LOAD_ADDR_EA_Q()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 7*/
        addbyte(0xc7);
        addlong(7);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+3+4+1);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+4+1);
        addbyte(0x8b); /*MOV EAX, [EDX+EDI]*/
        addbyte(0x04);
        addbyte(0x3a);
        addbyte(0x8b); /*MOV EDX, [EDX+EDI+4]*/
        addbyte(0x54);
        addbyte(0x3a);
        addbyte(4);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmemql*/
        addlong((uint32_t)readmemql - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/
        
        return addr;
}

static uint32_t gen_MEM_STORE_ADDR_EA_B()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EBX, ESI*/
        addbyte(0xf3);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xc0 | (REG_ESI << 3) | REG_EDI);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+1);
        addbyte(0x88); /*MOV [EDI+ESI],CL*/
        addbyte(0x04 | (REG_ECX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0xe8); /*CALL writememb386l*/
        addlong((uint32_t)writememb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 12*/
        addbyte(0xc4);
        addbyte(12);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/

        return addr;
}

static uint32_t gen_MEM_STORE_ADDR_EA_W()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EBX, ESI*/
        addbyte(0xf3);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xf7);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 1*/
        addbyte(0xc7);
        addlong(1);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+4+1);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(4+1);
        addbyte(0x66); /*MOV [EDI+ESI],CX*/
        addbyte(0x89);
        addbyte(0x04 | (REG_CX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0xe8); /*CALL writememwl*/
        addlong((uint32_t)writememwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 12*/
        addbyte(0xc4);
        addbyte(12);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/

        return addr;
}

static uint32_t gen_MEM_STORE_ADDR_EA_L()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EBX, ESI*/
        addbyte(0xf3);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xf7);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 3*/
        addbyte(0xc7);
        addlong(3);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+3+1);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+1);
        addbyte(0x89); /*MOV [EDI+ESI],ECX*/
        addbyte(0x04 | (REG_ECX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0xe8); /*CALL writememll*/
        addlong((uint32_t)writememll - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 12*/
        addbyte(0xc4);
        addbyte(12);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/

        return addr;
}

static uint32_t gen_MEM_STORE_ADDR_EA_Q()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = EBX/ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EDX, ESI*/
        addbyte(0xf2);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xf7);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 7*/
        addbyte(0xc7);
        addlong(7);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+3+4+1);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+4+1);
        addbyte(0x89); /*MOV [EDI+ESI],EBX*/
        addbyte(0x04 | (REG_EBX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0x89); /*MOV 4[EDI+ESI],EBX*/
        addbyte(0x44 | (REG_ECX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(4);
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x52); /*PUSH EDX*/
        addbyte(0xe8); /*CALL writememql*/
        addlong((uint32_t)writememql - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 16*/
        addbyte(0xc4);
        addbyte(16);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/

        return addr;
}

#ifndef RELEASE_BUILD
static char gen_MEM_LOAD_ADDR_EA_B_NO_ABRT_err[] = "gen_MEM_LOAD_ADDR_EA_B_NO_ABRT aborted\n";
#endif
static uint32_t gen_MEM_LOAD_ADDR_EA_B_NO_ABRT()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];

        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(4+1);
        addbyte(0x0f); /*MOVZX ECX, B[EDX+EDI]*/
        addbyte(0xb6);
        addbyte(0x0c);
        addbyte(0x3a);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmembl*/
        addlong((uint32_t)readmemb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
#ifndef RELEASE_BUILD
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
#endif
        addbyte(0x0f); /*MOVZX ECX, AL*/
        addbyte(0xb6);
        addbyte(0xc8);
#ifndef RELEASE_BUILD
        addbyte(0x75); /*JNE mem_abrt_rout*/
        addbyte(1);
#endif
        addbyte(0xc3); /*RET*/
#ifndef RELEASE_BUILD        
        addbyte(0xc7); /*MOV [ESP], gen_MEM_LOAD_ADDR_EA_B_NO_ABRT_err*/
        addbyte(0x04);
        addbyte(0x24);
        addlong((uint32_t)gen_MEM_LOAD_ADDR_EA_B_NO_ABRT_err);
        addbyte(0xe8); /*CALL fatal*/
        addlong((uint32_t)fatal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        /*Should not return!*/
#endif        
        return addr;
}

#ifndef RELEASE_BUILD
static char gen_MEM_LOAD_ADDR_EA_W_NO_ABRT_err[] = "gen_MEM_LOAD_ADDR_EA_W_NO_ABRT aborted\n";
#endif
static uint32_t gen_MEM_LOAD_ADDR_EA_W_NO_ABRT()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];

        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 1*/
        addbyte(0xc7);
        addlong(1);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+4+1);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(4+1);
        addbyte(0x0f); /*MOVZX ECX, [EDX+EDI]W*/
        addbyte(0xb7);
        addbyte(0x0c);
        addbyte(0x3a);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmemwl*/
        addlong((uint32_t)readmemwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
#ifndef RELEASE_BUILD
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
#endif
        addbyte(0x0f); /*MOVZX ECX, AX*/
        addbyte(0xb7);
        addbyte(0xc8);
#ifndef RELEASE_BUILD
        addbyte(0x75); /*JNE mem_abrt_rout*/
        addbyte(1);
#endif
        addbyte(0xc3); /*RET*/
#ifndef RELEASE_BUILD
        addbyte(0xc7); /*MOV [ESP], gen_MEM_LOAD_ADDR_EA_W_NO_ABRT_err*/
        addbyte(0x04);
        addbyte(0x24);
        addlong((uint32_t)gen_MEM_LOAD_ADDR_EA_W_NO_ABRT_err);
        addbyte(0xe8); /*CALL fatal*/
        addlong((uint32_t)fatal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        /*Should not return!*/
#endif
        return addr;
}

#ifndef RELEASE_BUILD
static char gen_MEM_LOAD_ADDR_EA_L_NO_ABRT_err[] = "gen_MEM_LOAD_ADDR_EA_L_NO_ABRT aborted\n";
#endif
static uint32_t gen_MEM_LOAD_ADDR_EA_L_NO_ABRT()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];

        addbyte(0x89); /*MOV ESI, EDX*/
        addbyte(0xd6);
        addbyte(0x01); /*ADDL EDX, EAX*/
        addbyte(0xc2);
        addbyte(0x89); /*MOV EDI, EDX*/
        addbyte(0xd7);
        addbyte(0xc1); /*SHR EDX, 12*/
        addbyte(0xea);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 3*/
        addbyte(0xc7);
        addlong(3);
        addbyte(0x8b); /*MOV EDX, readlookup2[EDX*4]*/
        addbyte(0x14);
        addbyte(0x95);
        addlong((uint32_t)readlookup2);
        addbyte(0x75); /*JE slowpath*/
        addbyte(3+2+3+1);
        addbyte(0x83); /*CMP EDX, -1*/
        addbyte(0xfa);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+1);
        addbyte(0x8b); /*MOV ECX, [EDX+EDI]*/
        addbyte(0x0c);
        addbyte(0x3a);
        addbyte(0xc3); /*RET*/

        addbyte(0x50); /*slowpath: PUSH EAX*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0xe8); /*CALL readmemll*/
        addlong((uint32_t)readmemll - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
        addbyte(0x89); /*MOV ECX, EAX*/
        addbyte(0xc1);
#ifndef RELEASE_BUILD
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x75); /*JNE mem_abrt_rout*/
        addbyte(1);
#endif
        addbyte(0xc3); /*RET*/
#ifndef RELEASE_BUILD
        addbyte(0x83); /*SUBL 4,%esp*/
        addbyte(0xEC);
        addbyte(4);
        addbyte(0xc7); /*MOV [ESP], gen_MEM_LOAD_ADDR_EA_L_NO_ABRT_err*/
        addbyte(0x04);
        addbyte(0x24);
        addlong((uint32_t)gen_MEM_LOAD_ADDR_EA_L_NO_ABRT_err);
        addbyte(0xe8); /*CALL fatal*/
        addlong((uint32_t)fatal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        /*Should not return!*/
#endif        
        return addr;
}

#ifndef RELEASE_BUILD
static char gen_MEM_STORE_ADDR_EA_B_NO_ABRT_err[] = "gen_MEM_STORE_ADDR_EA_B_NO_ABRT aborted\n";
#endif
static uint32_t gen_MEM_STORE_ADDR_EA_B_NO_ABRT()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EBX, ESI*/
        addbyte(0xf3);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xc0 | (REG_ESI << 3) | REG_EDI);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+1);
        addbyte(0x88); /*MOV [EDI+ESI],CL*/
        addbyte(0x04 | (REG_ECX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0xe8); /*CALL writememb386l*/
        addlong((uint32_t)writememb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 12*/
        addbyte(0xc4);
        addbyte(12);
#ifndef RELEASE_BUILD
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x75); /*JNE mem_abrt_rout*/
        addbyte(1);
#endif
        addbyte(0xc3); /*RET*/
#ifndef RELEASE_BUILD        
        addbyte(0xc7); /*MOV [ESP], gen_MEM_STORE_ADDR_EA_B_NO_ABRT_err*/
        addbyte(0x04);
        addbyte(0x24);
        addlong((uint32_t)gen_MEM_STORE_ADDR_EA_B_NO_ABRT_err);
        addbyte(0xe8); /*CALL fatal*/
        addlong((uint32_t)fatal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        /*Should not return!*/
#endif
        return addr;
}

#ifndef RELEASE_BUILD
static char gen_MEM_STORE_ADDR_EA_W_NO_ABRT_err[] = "gen_MEM_STORE_ADDR_EA_W_NO_ABRT aborted\n";
#endif
static uint32_t gen_MEM_STORE_ADDR_EA_W_NO_ABRT()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EBX, ESI*/
        addbyte(0xf3);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xf7);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 1*/
        addbyte(0xc7);
        addlong(1);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+4+1);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(4+1);
        addbyte(0x66); /*MOV [EDI+ESI],CX*/
        addbyte(0x89);
        addbyte(0x04 | (REG_CX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0xe8); /*CALL writememwl*/
        addlong((uint32_t)writememwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 12*/
        addbyte(0xc4);
        addbyte(12);
#ifndef RELEASE_BUILD
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x75); /*JNE mem_abrt_rout*/
        addbyte(1);
#endif
        addbyte(0xc3); /*RET*/
#ifndef RELEASE_BUILD
        addbyte(0xc7); /*MOV [ESP], gen_MEM_STORE_ADDR_EA_W_NO_ABRT_err*/
        addbyte(0x04);
        addbyte(0x24);
        addlong((uint32_t)gen_MEM_STORE_ADDR_EA_W_NO_ABRT_err);
        addbyte(0xe8); /*CALL fatal*/
        addlong((uint32_t)fatal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        /*Should not return!*/
#endif
        return addr;
}

#ifndef RELEASE_BUILD
static char gen_MEM_STORE_ADDR_EA_L_NO_ABRT_err[] = "gen_MEM_STORE_ADDR_EA_L_NO_ABRT aborted\n";
#endif
static uint32_t gen_MEM_STORE_ADDR_EA_L_NO_ABRT()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*dat = ECX, seg = ESI, addr = EAX*/
        addbyte(0x89); /*MOV EBX, ESI*/
        addbyte(0xf3);
        addbyte(0x01); /*ADDL ESI, EAX*/
        addbyte(0xc0 | (REG_EAX << 3) | REG_ESI);
        addbyte(0x89); /*MOV EDI, ESI*/
        addbyte(0xf7);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xe8 | REG_ESI);
        addbyte(12);
        addbyte(0xf7); /*TEST EDI, 3*/
        addbyte(0xc7);
        addlong(3);
        addbyte(0x8b); /*MOV ESI, readlookup2[ESI*4]*/
        addbyte(0x04 | (REG_ESI << 3));
        addbyte(0x85 | (REG_ESI << 3));
        addlong((uint32_t)writelookup2);
        addbyte(0x75); /*JNE slowpath*/
        addbyte(3+2+3+1);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xf8 | REG_ESI);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(3+1);
        addbyte(0x89); /*MOV [EDI+ESI],ECX*/
        addbyte(0x04 | (REG_ECX << 3));
        addbyte(REG_EDI | (REG_ESI << 3));
        addbyte(0xc3); /*RET*/

        addbyte(0x51); /*slowpath: PUSH ECX*/
        addbyte(0x50); /*PUSH EAX*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0xe8); /*CALL writememll*/
        addlong((uint32_t)writememll - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 12*/
        addbyte(0xc4);
        addbyte(12);
#ifndef RELEASE_BUILD
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x75); /*JNE mem_abrt_rout*/
        addbyte(1);
#endif
        addbyte(0xc3); /*RET*/
#ifndef RELEASE_BUILD
        addbyte(0xc7); /*MOV [ESP], gen_MEM_STORE_ADDR_EA_L_NO_ABRT_err*/
        addbyte(0x04);
        addbyte(0x24);
        addlong((uint32_t)gen_MEM_STORE_ADDR_EA_L_NO_ABRT_err);
        addbyte(0xe8); /*CALL fatal*/
        addlong((uint32_t)fatal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        /*Should not return!*/
#endif
        return addr;
}

static uint32_t gen_MEM_CHECK_WRITE()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*seg = ESI, addr = EAX*/
        
        addbyte(0x8d); /*LEA EDI, [EAX+ESI]*/
        addbyte(0x3c);
        addbyte(0x30);
        addbyte(0x83); /*CMP cr0, 0*/
        addbyte(0x3d);
        addlong((uint32_t)&cr0);
        addbyte(0);
        addbyte(0x78); /*JS +*/
        addbyte(1);
        addbyte(0xc3); /*RET*/
        addbyte(0xc1); /*SHR EDI, 12*/
        addbyte(0xef);
        addbyte(12);
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xfe);
        addbyte(-1);
        addbyte(0x74); /*JE slowpath*/
        addbyte(11);
        addbyte(0x83); /*CMP writelookup2[EDI*4],-1*/
        addbyte(0x3c);
        addbyte(0xbd);
        addlong((uint32_t)writelookup2);
        addbyte(-1);
        addbyte(0x74); /*JE +*/
        addbyte(1);
        addbyte(0xc3); /*RET*/
        
        /*slowpath:*/
        addbyte(0x8d); /*LEA EDI, [EAX+ESI]*/
        addbyte(0x3c);
        addbyte(0x30);
        addbyte(0x6a); /*PUSH 1*/
        addbyte(1);
        addbyte(0x57); /*PUSH EDI*/
        addbyte(0xe8); /*CALL mmutranslatereal*/
        addlong((uint32_t)mmutranslatereal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x83); /*ADD ESP, 8*/
        addbyte(0xc4);
        addbyte(8);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        addbyte(0xc3); /*RET*/

        return addr;
}

static uint32_t gen_MEM_CHECK_WRITE_W()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*seg = ESI, addr = EAX*/
        
        addbyte(0x8d); /*LEA EDI, [EAX+ESI]*/
        addbyte(0x3c);
        addbyte(0x30);
        addbyte(0x83); /*CMP cr0, 0*/
        addbyte(0x3d);
        addlong((uint32_t)&cr0);
        addbyte(0);
        addbyte(0x78); /*JS +*/
        addbyte(1);
        addbyte(0xc3); /*RET*/
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xfe);
        addbyte(-1);
        addbyte(0x8d); /*LEA ESI, 1[EDI]*/
        addbyte(0x77);
        addbyte(0x01);
        addbyte(0x74); /*JE slowpath*/
        addbyte(11);
        addbyte(0x89); /*MOV EAX, EDI*/
        addbyte(0xf8);
        addbyte(0xc1); /*SHR EDI, 12*/
        addbyte(0xef);
        addbyte(12);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xee);
        addbyte(12);
        addbyte(0x83); /*CMP writelookup2[EDI*4],-1*/
        addbyte(0x3c);
        addbyte(0xbd);
        addlong((uint32_t)writelookup2);
        addbyte(-1);
        addbyte(0x74); /*JE +*/
        addbyte(11);
        addbyte(0x83); /*CMP writelookup2[ESI*4],-1*/
        addbyte(0x3c);
        addbyte(0xb5);
        addlong((uint32_t)writelookup2);
        addbyte(-1);
        addbyte(0x74); /*JE +*/
        addbyte(1);
        addbyte(0xc3); /*RET*/
        
        /*slowpath:*/
        addbyte(0x89); /*MOV EDI, EAX*/
        addbyte(0xc7);
        /*slowpath_lp:*/
        addbyte(0x6a); /*PUSH 1*/
        addbyte(1);
        addbyte(0x57); /*PUSH EDI*/
        addbyte(0xe8); /*CALL mmutranslatereal*/
        addlong((uint32_t)mmutranslatereal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x5f); /*POP EDI*/
        addbyte(0x83); /*ADD ESP, 4*/
        addbyte(0xc4);
        addbyte(4);
        addbyte(0x83); /*ADD EDI, 1*/
        addbyte(0xc7);
        addbyte(1);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        /*If bits 0-11 of the address are now 0 then this crosses a page, so loop back*/
        addbyte(0xf7); /*TEST $fff, EDI*/
        addbyte(0xc7);
        addlong(0xfff);
        addbyte(0x74); /*JE slowpath_lp*/
        addbyte(-33);
        addbyte(0xc3); /*RET*/

        return addr;
}

static uint32_t gen_MEM_CHECK_WRITE_L()
{
        uint32_t addr = (uint32_t)&codeblock[block_current].data[block_pos];
        
        /*seg = ESI, addr = EAX*/
        
        addbyte(0x8d); /*LEA EDI, [EAX+ESI]*/
        addbyte(0x3c);
        addbyte(0x30);
        addbyte(0x83); /*CMP cr0, 0*/
        addbyte(0x3d);
        addlong((uint32_t)&cr0);
        addbyte(0);
        addbyte(0x78); /*JS +*/
        addbyte(1);
        addbyte(0xc3); /*RET*/
        addbyte(0x83); /*CMP ESI, -1*/
        addbyte(0xfe);
        addbyte(-1);
        addbyte(0x8d); /*LEA ESI, 3[EDI]*/
        addbyte(0x77);
        addbyte(0x03);
        addbyte(0x74); /*JE slowpath*/
        addbyte(11);
        addbyte(0x89); /*MOV EAX, EDI*/
        addbyte(0xf8);
        addbyte(0xc1); /*SHR EDI, 12*/
        addbyte(0xef);
        addbyte(12);
        addbyte(0xc1); /*SHR ESI, 12*/
        addbyte(0xee);
        addbyte(12);
        addbyte(0x83); /*CMP writelookup2[EDI*4],-1*/
        addbyte(0x3c);
        addbyte(0xbd);
        addlong((uint32_t)writelookup2);
        addbyte(-1);
        addbyte(0x74); /*JE +*/
        addbyte(11);
        addbyte(0x83); /*CMP writelookup2[ESI*4],-1*/
        addbyte(0x3c);
        addbyte(0xb5);
        addlong((uint32_t)writelookup2);
        addbyte(-1);
        addbyte(0x74); /*JE +*/
        addbyte(1);
        addbyte(0xc3); /*RET*/
        
        /*slowpath:*/
        addbyte(0x89); /*MOV EDI, EAX*/
        addbyte(0xc7);
        /*slowpath_lp:*/
        addbyte(0x6a); /*PUSH 1*/
        addbyte(1);
        addbyte(0x57); /*PUSH EDI*/
        addbyte(0xe8); /*CALL mmutranslatereal*/
        addlong((uint32_t)mmutranslatereal - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        addbyte(0x5f); /*POP EDI*/
        addbyte(0x83); /*ADD ESP, 4*/
        addbyte(0xc4);
        addbyte(4);
        addbyte(0x83); /*ADD EDI, 3*/
        addbyte(0xc7);
        addbyte(3);
        addbyte(0x80); /*CMP abrt, 0*/
        addbyte(0x7d);
        addbyte((uint8_t)cpu_state_offset(abrt));
        addbyte(0);
        addbyte(0x0f); /*JNE mem_abrt_rout*/
        addbyte(0x85);
        addlong(mem_abrt_rout - ((uint32_t)(&codeblock[block_current].data[block_pos]) + 4));
        /*If bits 2-11 of the address are now 0 then this crosses a page, so loop back*/
        addbyte(0xf7); /*TEST EDI, FFC*/
        addbyte(0xc7);
        addlong(0xffc);
        addbyte(0x74); /*JE slowpath_lp*/
        addbyte(-33);
        addbyte(0xc3); /*RET*/

        return addr;
}

void codegen_init()
{
#ifdef __linux__
	void *start;
	size_t len;
	long pagesize = sysconf(_SC_PAGESIZE);
	long pagemask = ~(pagesize - 1);
#endif
        
#ifdef _WIN32
        codeblock = (codeblock_t *)VirtualAlloc(NULL, (BLOCK_SIZE+1) * sizeof(codeblock_t), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
        codeblock = (codeblock_t *)mem_alloc((BLOCK_SIZE+1) * sizeof(codeblock_t));
#endif
        codeblock_hash = (codeblock_t **)mem_alloc(HASH_SIZE * sizeof(codeblock_t *));

        memset(codeblock, 0, (BLOCK_SIZE+1) * sizeof(codeblock_t));
        memset(codeblock_hash, 0, HASH_SIZE * sizeof(codeblock_t *));

#ifdef __linux__
	start = (void *)((long)codeblock & pagemask);
	len = (((BLOCK_SIZE+1) * sizeof(codeblock_t)) + pagesize) & pagemask;
	if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
	{
		perror("mprotect");
		exit(-1);
	}
#endif

        block_current = BLOCK_SIZE;
        block_pos = 0;
        mem_abrt_rout = (uint32_t)&codeblock[block_current].data[block_pos];        
        addbyte(0x83); /*ADDL $16+4,%esp*/
        addbyte(0xC4);
        addbyte(0x10+4);
        addbyte(0x5f); /*POP EDI*/
        addbyte(0x5e); /*POP ESI*/
        addbyte(0x5d); /*POP EBP*/
        addbyte(0x5b); /*POP EDX*/
        addbyte(0xC3); /*RET*/
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_l = (uint32_t)gen_MEM_LOAD_ADDR_EA_L();
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_w = (uint32_t)gen_MEM_LOAD_ADDR_EA_W();
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_b = (uint32_t)gen_MEM_LOAD_ADDR_EA_B();
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_q = (uint32_t)gen_MEM_LOAD_ADDR_EA_Q();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_l = (uint32_t)gen_MEM_STORE_ADDR_EA_L();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_w = (uint32_t)gen_MEM_STORE_ADDR_EA_W();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_b = (uint32_t)gen_MEM_STORE_ADDR_EA_B();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_q = (uint32_t)gen_MEM_STORE_ADDR_EA_Q();
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_b_no_abrt = (uint32_t)gen_MEM_LOAD_ADDR_EA_B_NO_ABRT();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_b_no_abrt = (uint32_t)gen_MEM_STORE_ADDR_EA_B_NO_ABRT();
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_w_no_abrt = (uint32_t)gen_MEM_LOAD_ADDR_EA_W_NO_ABRT();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_w_no_abrt = (uint32_t)gen_MEM_STORE_ADDR_EA_W_NO_ABRT();
        block_pos = (block_pos + 15) & ~15;
        mem_load_addr_ea_l_no_abrt = (uint32_t)gen_MEM_LOAD_ADDR_EA_L_NO_ABRT();
        block_pos = (block_pos + 15) & ~15;
        mem_store_addr_ea_l_no_abrt = (uint32_t)gen_MEM_STORE_ADDR_EA_L_NO_ABRT();
        block_pos = (block_pos + 15) & ~15;
        mem_check_write = (uint32_t)gen_MEM_CHECK_WRITE();
        block_pos = (block_pos + 15) & ~15;
        mem_check_write_w = (uint32_t)gen_MEM_CHECK_WRITE_W();
        block_pos = (block_pos + 15) & ~15;
        mem_check_write_l = (uint32_t)gen_MEM_CHECK_WRITE_L();
        
#ifndef _MSC_VER
        asm(
                "fstcw %0\n"
                : "=m" (cpu_state.old_npxc)
        );
#else
        __asm
        {
                fstcw cpu_state.old_npxc
        }
#endif
}

void codegen_reset()
{
        memset(codeblock, 0, BLOCK_SIZE * sizeof(codeblock_t));
        memset(codeblock_hash, 0, HASH_SIZE * sizeof(codeblock_t *));
        mem_reset_page_blocks();
}

void dump_block()
{
}

static void add_to_block_list(codeblock_t *block)
{
        codeblock_t *block_prev = pages[block->phys >> 12].block[(block->phys >> 10) & 3];

        if (!block->page_mask)
                fatal("add_to_block_list - mask = 0\n");

        if (block_prev)
        {
                block->next = block_prev;
                block_prev->prev = block;
                pages[block->phys >> 12].block[(block->phys >> 10) & 3] = block;
        }
        else
        {
                block->next = NULL;
                pages[block->phys >> 12].block[(block->phys >> 10) & 3] = block;
        }

        if (block->next)
        {
                if (!block->next->valid)
                        fatal("block->next->valid=0 %p %p %x %x\n", (void *)block->next, (void *)codeblock, block_current, block_pos);
        }
        
        if (block->page_mask2)
        {
                block_prev = pages[block->phys_2 >> 12].block_2[(block->phys_2 >> 10) & 3];

                if (block_prev)
                {
                        block->next_2 = block_prev;
                        block_prev->prev_2 = block;
                        pages[block->phys_2 >> 12].block_2[(block->phys_2 >> 10) & 3] = block;
                }
                else
                {
                        block->next_2 = NULL;
                        pages[block->phys_2 >> 12].block_2[(block->phys_2 >> 10) & 3] = block;
                }
        }
}

static void remove_from_block_list(codeblock_t *block, uint32_t pc)
{
        if (!block->page_mask)
                return;

        if (block->prev)
        {
                block->prev->next = block->next;
                if (block->next)
                        block->next->prev = block->prev;
        }
        else
        {
                pages[block->phys >> 12].block[(block->phys >> 10) & 3] = block->next;
                if (block->next)
                        block->next->prev = NULL;
                else
                        mem_flush_write_page(block->phys, 0);
        }
        if (!block->page_mask2)
        {
                if (block->prev_2 || block->next_2)
                        fatal("Invalid block_2\n");
                return;
        }

        if (block->prev_2)
        {
                block->prev_2->next_2 = block->next_2;
                if (block->next_2)
                        block->next_2->prev_2 = block->prev_2;
        }
        else
        {
                pages[block->phys_2 >> 12].block_2[(block->phys_2 >> 10) & 3] = block->next_2;
                if (block->next_2)
                        block->next_2->prev_2 = NULL;
                else
                        mem_flush_write_page(block->phys_2, 0);
        }
}

static void delete_block(codeblock_t *block)
{
        uint32_t old_pc = block->pc;

        if (block == codeblock_hash[HASH(block->phys)])
                codeblock_hash[HASH(block->phys)] = NULL;

        if (!block->valid)
                fatal("Deleting deleted block\n");
        block->valid = 0;

        codeblock_tree_delete(block);
        remove_from_block_list(block, old_pc);
}

void codegen_check_flush(page_t *page, uint64_t mask, uint32_t phys_addr)
{
        struct codeblock_t *block = page->block[(phys_addr >> 10) & 3];

        while (block)
        {
                if (mask & block->page_mask)
                {
                        delete_block(block);
                        cpu_recomp_evicted++;
                }
                if (block == block->next)
                        fatal("Broken 1\n");
                block = block->next;
        }

        block = page->block_2[(phys_addr >> 10) & 3];
        
        while (block)
        {
                if (mask & block->page_mask2)
                {
                        delete_block(block);
                        cpu_recomp_evicted++;
                }
                if (block == block->next_2)
                        fatal("Broken 2\n");
                block = block->next_2;
        }
}

void codegen_block_init(uint32_t phys_addr)
{
        codeblock_t *block;
        page_t *page = &pages[phys_addr >> 12];
        
        if (!page->block[(phys_addr >> 10) & 3])
                mem_flush_write_page(phys_addr, cs+cpu_state.pc);

        block_current = (block_current + 1) & BLOCK_MASK;
        block = &codeblock[block_current];

        if (block->valid != 0)
        {
                delete_block(block);
                cpu_recomp_reuse++;
        }
        block_num = HASH(phys_addr);
        codeblock_hash[block_num] = &codeblock[block_current];

	block->valid = 1;
        block->ins = 0;
        block->pc = cs + cpu_state.pc;
        block->_cs = cs;
        block->pnt = block_current;
        block->phys = phys_addr;
        block->dirty_mask = &page->dirty_mask[(phys_addr >> PAGE_MASK_INDEX_SHIFT) & PAGE_MASK_INDEX_MASK];
        block->dirty_mask2 = NULL;
        block->next = block->prev = NULL;
        block->next_2 = block->prev_2 = NULL;
        block->page_mask = 0;
        block->flags = CODEBLOCK_STATIC_TOP;
        block->status = cpu_cur_status;
        
        block->was_recompiled = 0;

        recomp_page = block->phys & ~0xfff;
        
        codeblock_tree_add(block);
}

void codegen_block_start_recompile(codeblock_t *block)
{
        page_t *page = &pages[block->phys >> 12];
        
        if (!page->block[(block->phys >> 10) & 3])
                mem_flush_write_page(block->phys, cs+cpu_state.pc);

        block_num = HASH(block->phys);
        block_current = block->pnt;

        if (block->pc != cs + cpu_state.pc || block->was_recompiled)
                fatal("Recompile to used block!\n");

        block->status = cpu_cur_status;

        block_pos = BLOCK_GPF_OFFSET;
        addbyte(0xc7); /*MOV [ESP],0*/
        addbyte(0x04);
        addbyte(0x24);
        addlong(0);
        addbyte(0xc7); /*MOV [ESP+4],0*/
        addbyte(0x44);
        addbyte(0x24);
        addbyte(0x04);
        addlong(0);
        addbyte(0xe8); /*CALL x86gpf*/
        addlong((uint32_t)x86gpf - (uint32_t)(&codeblock[block_current].data[block_pos + 4]));
        block_pos = BLOCK_EXIT_OFFSET; /*Exit code*/
        addbyte(0x83); /*ADDL $16,%esp*/
        addbyte(0xC4);
        addbyte(0x10);
        addbyte(0x5f); /*POP EDI*/
        addbyte(0x5e); /*POP ESI*/
        addbyte(0x5d); /*POP EBP*/
        addbyte(0x5b); /*POP EDX*/
        addbyte(0xC3); /*RET*/
        cpu_block_end = 0;
        block_pos = 0; /*Entry code*/
        addbyte(0x53); /*PUSH EBX*/
        addbyte(0x55); /*PUSH EBP*/
        addbyte(0x56); /*PUSH ESI*/
        addbyte(0x57); /*PUSH EDI*/
        addbyte(0x83); /*SUBL $16,%esp*/
        addbyte(0xEC);
        addbyte(0x10);
        addbyte(0xBD); /*MOVL EBP, &cpu_state*/
        addlong(((uintptr_t)&cpu_state) + 128);

        last_op32 = -1;
        last_ea_seg = NULL;
        last_ssegs = -1;
        
        codegen_block_cycles = 0;
        codegen_timing_block_start();
        
        codegen_block_ins = 0;
        codegen_block_full_ins = 0;

        recomp_page = block->phys & ~0xfff;
        
        codegen_flags_changed = 0;
        codegen_fpu_entered = 0;
        codegen_mmx_entered = 0;

        codegen_fpu_loaded_iq[0] = codegen_fpu_loaded_iq[1] = codegen_fpu_loaded_iq[2] = codegen_fpu_loaded_iq[3] =
        codegen_fpu_loaded_iq[4] = codegen_fpu_loaded_iq[5] = codegen_fpu_loaded_iq[6] = codegen_fpu_loaded_iq[7] = 0;
        
        _ds.checked = _es.checked = _fs.checked = _gs.checked = (cr0 & 1) ? 0 : 1;

        block->TOP = cpu_state.TOP;
        block->was_recompiled = 1;

        codegen_flat_ds = !(cpu_cur_status & CPU_STATUS_NOTFLATDS);
        codegen_flat_ss = !(cpu_cur_status & CPU_STATUS_NOTFLATSS);       
}

void codegen_block_remove()
{
        codeblock_t *block = &codeblock[block_current];

        delete_block(block);
        cpu_recomp_removed++;

        recomp_page = -1;
}

void codegen_block_generate_end_mask()
{
        codeblock_t *block = &codeblock[block_current];
        uint32_t start_pc;
        uint32_t end_pc;

        block->endpc = codegen_endpc;

        block->page_mask = 0;
        start_pc = (block->pc & 0x3ff) & ~15;
        if ((block->pc ^ block->endpc) & ~0x3ff)
                end_pc = 0x3ff & ~15;
        else
                end_pc = (block->endpc & 0x3ff) & ~15;
        if (end_pc < start_pc)
                end_pc = 0x3ff;
        start_pc >>= PAGE_MASK_SHIFT;
        end_pc >>= PAGE_MASK_SHIFT;
        
        for (; start_pc <= end_pc; start_pc++)
        {                
                block->page_mask |= ((uint64_t)1 << start_pc);
        }
        
        pages[block->phys >> 12].code_present_mask[(block->phys >> 10) & 3] |= block->page_mask;

        block->phys_2 = -1;
        block->page_mask2 = 0;
        block->next_2 = block->prev_2 = NULL;
        if ((block->pc ^ block->endpc) & ~0x3ff)
        {
                block->phys_2 = get_phys_noabrt(block->endpc);
                if (block->phys_2 != -1)
                {
                        page_t *page_2 = &pages[block->phys_2 >> 12];

                        start_pc = 0;
                        end_pc = (block->endpc & 0x3ff) >> PAGE_MASK_SHIFT;
                        for (; start_pc <= end_pc; start_pc++)
                                block->page_mask2 |= ((uint64_t)1 << start_pc);
                        page_2->code_present_mask[(block->phys_2 >> 10) & 3] |= block->page_mask2;
                
                        if (!pages[block->phys_2 >> 12].block_2[(block->phys_2 >> 10) & 3])
                                mem_flush_write_page(block->phys_2, block->endpc);

                        if (!block->page_mask2)
                                fatal("!page_mask2\n");
                        if (block->next_2)
                        {
                                if (!block->next_2->valid)
                                        fatal("block->next_2->valid=0 %p\n", (void *)block->next_2);
                        }

                        block->dirty_mask2 = &page_2->dirty_mask[(block->phys_2 >> PAGE_MASK_INDEX_SHIFT) & PAGE_MASK_INDEX_MASK];
                }
        }

        recomp_page = -1;
}

void codegen_block_end()
{
        codeblock_t *block = &codeblock[block_current];

        codegen_block_generate_end_mask();
        add_to_block_list(block);
}

void codegen_block_end_recompile(codeblock_t *block)
{
        codegen_timing_block_end();

        if (codegen_block_cycles)
        {
                addbyte(0x81); /*SUB $codegen_block_cycles, cyclcs*/
                addbyte(0x6d);
                addbyte((uint8_t)cpu_state_offset(_cycles));
                addlong(codegen_block_cycles);
        }
        if (codegen_block_ins)
        {
                addbyte(0x81); /*ADD $codegen_block_ins,ins*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(cpu_recomp_ins));
                addlong(codegen_block_ins);
        }
        addbyte(0x83); /*ADDL $16,%esp*/
        addbyte(0xC4);
        addbyte(0x10);
        addbyte(0x5f); /*POP EDI*/
        addbyte(0x5e); /*POP ESI*/
        addbyte(0x5d); /*POP EBP*/
        addbyte(0x5b); /*POP EDX*/
        addbyte(0xC3); /*RET*/
        
        if (block_pos > BLOCK_GPF_OFFSET)
                fatal("Over limit!\n");

        remove_from_block_list(block, block->pc);
        block->next = block->prev = NULL;
        block->next_2 = block->prev_2 = NULL;
        codegen_block_generate_end_mask();
        add_to_block_list(block);

        if (!(block->flags & CODEBLOCK_HAS_FPU))
                block->flags &= ~CODEBLOCK_STATIC_TOP;
}

void codegen_flush()
{
        return;
}

static int opcode_modrm[256] =
{
        1, 1, 1, 1,  0, 0, 0, 0,  1, 1, 1, 1,  0, 0, 0, 0,  /*00*/
        1, 1, 1, 1,  0, 0, 0, 0,  1, 1, 1, 1,  0, 0, 0, 0,  /*10*/
        1, 1, 1, 1,  0, 0, 0, 0,  1, 1, 1, 1,  0, 0, 0, 0,  /*20*/
        1, 1, 1, 1,  0, 0, 0, 0,  1, 1, 1, 1,  0, 0, 0, 0,  /*30*/

        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*40*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*50*/
        0, 0, 1, 1,  0, 0, 0, 0,  0, 1, 0, 1,  0, 0, 0, 0,  /*60*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*70*/

        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  /*80*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*90*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*a0*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*b0*/

        1, 1, 0, 0,  1, 1, 1, 1,  0, 0, 0, 0,  0, 0, 0, 0,  /*c0*/
        1, 1, 1, 1,  0, 0, 0, 0,  1, 1, 1, 1,  1, 1, 1, 1,  /*d0*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  /*e0*/
        0, 0, 0, 0,  0, 0, 1, 1,  0, 0, 0, 0,  0, 0, 1, 1,  /*f0*/
};
int opcode_0f_modrm[256] =
{
        1, 1, 1, 1,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /*00*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /*10*/
        1, 1, 1, 1,  1, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /*20*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /*30*/

        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1, /*40*/
        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /*50*/
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  0, 0, 1, 1, /*60*/
        0, 1, 1, 1,  1, 1, 1, 0,  0, 0, 0, 0,  0, 0, 1, 1, /*70*/

        0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0, /*80*/
        1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1, /*90*/
        0, 0, 0, 1,  1, 1, 0, 0,  0, 0, 0, 1,  1, 1, 1, 1, /*a0*/
        1, 1, 1, 1,  1, 1, 1, 1,  0, 0, 1, 1,  1, 1, 1, 1, /*b0*/

        1, 1, 0, 0,  0, 0, 0, 1,  0, 0, 0, 0,  0, 0, 0, 0, /*c0*/
        0, 1, 1, 1,  0, 1, 0, 0,  1, 1, 0, 1,  1, 1, 0, 1, /*d0*/
        0, 1, 1, 0,  0, 1, 0, 0,  1, 1, 0, 1,  1, 1, 0, 1, /*e0*/
        0, 1, 1, 1,  0, 1, 0, 0,  1, 1, 1, 0,  1, 1, 1, 0  /*f0*/
};
        
void codegen_debug()
{
}

static x86seg *codegen_generate_ea_16_long(x86seg *op_ea_seg, uint32_t fetchdat, int op_ssegs, uint32_t *op_pc)
{
        if (!cpu_mod && cpu_rm == 6) 
        { 
                addbyte(0xC7); /*MOVL $0,(ssegs)*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(eaaddr));
                addlong((fetchdat >> 8) & 0xffff);
                (*op_pc) += 2;
        }
        else
        {
                switch (cpu_mod)
                {
                        case 0:
                        addbyte(0xa1); /*MOVL *mod1add[0][cpu_rm], %eax*/
                        addlong((uint32_t)mod1add[0][cpu_rm]);
                        addbyte(0x03); /*ADDL *mod1add[1][cpu_rm], %eax*/
                        addbyte(0x05);
                        addlong((uint32_t)mod1add[1][cpu_rm]);
                        break;
                        case 1:
                        addbyte(0xb8); /*MOVL ,%eax*/
                        addlong((uint32_t)(int8_t)(rmdat >> 8));
                        addbyte(0x03); /*ADDL *mod1add[0][cpu_rm], %eax*/
                        addbyte(0x05);
                        addlong((uint32_t)mod1add[0][cpu_rm]);
                        addbyte(0x03); /*ADDL *mod1add[1][cpu_rm], %eax*/
                        addbyte(0x05);
                        addlong((uint32_t)mod1add[1][cpu_rm]);
                        (*op_pc)++;
                        break;
                        case 2:
                        addbyte(0xb8); /*MOVL ,%eax*/
                        addlong((fetchdat >> 8) & 0xffff);
                        addbyte(0x03); /*ADDL *mod1add[0][cpu_rm], %eax*/
                        addbyte(0x05);
                        addlong((uint32_t)mod1add[0][cpu_rm]);
                        addbyte(0x03); /*ADDL *mod1add[1][cpu_rm], %eax*/
                        addbyte(0x05);
                        addlong((uint32_t)mod1add[1][cpu_rm]);
                        (*op_pc) += 2;
                        break;
                }
                addbyte(0x25); /*ANDL $0xffff, %eax*/
                addlong(0xffff);
                addbyte(0xa3);
                addlong((uint32_t)&cpu_state.eaaddr);

                if (mod1seg[cpu_rm] == &ss && !op_ssegs)
                        op_ea_seg = &_ss;
        }
        return op_ea_seg;
}

static x86seg *codegen_generate_ea_32_long(x86seg *op_ea_seg, uint32_t fetchdat, int op_ssegs, uint32_t *op_pc, int stack_offset)
{
        uint32_t new_eaaddr;

        if (cpu_rm == 4)
        {
                uint8_t sib = fetchdat >> 8;
                (*op_pc)++;
                
                switch (cpu_mod)
                {
                        case 0:
                        if ((sib & 7) == 5)
                        {
                                new_eaaddr = fastreadl(cs + (*op_pc) + 1);
                                addbyte(0xb8); /*MOVL ,%eax*/
                                addlong(new_eaaddr);
                                (*op_pc) += 4;
                        }
                        else
                        {
                                addbyte(0x8b); /*MOVL regs[sib&7].l, %eax*/
                                addbyte(0x45);
                                addbyte((uint8_t)cpu_state_offset(regs[sib & 7].l));
                        }
                        break;
                        case 1: 
                        new_eaaddr = (uint32_t)(int8_t)((fetchdat >> 16) & 0xff);
                        addbyte(0xb8); /*MOVL new_eaaddr, %eax*/
                        addlong(new_eaaddr);
                        addbyte(0x03); /*ADDL regs[sib&7].l, %eax*/
                        addbyte(0x45);
                        addbyte((uint8_t)cpu_state_offset(regs[sib & 7].l));
                        (*op_pc)++;
                        break;
                        case 2:
                        new_eaaddr = fastreadl(cs + (*op_pc) + 1);
                        addbyte(0xb8); /*MOVL new_eaaddr, %eax*/
                        addlong(new_eaaddr);
                        addbyte(0x03); /*ADDL regs[sib&7].l, %eax*/
                        addbyte(0x45);
                        addbyte((uint8_t)cpu_state_offset(regs[sib & 7].l));
                        (*op_pc) += 4;
                        break;
                }
                if (stack_offset && (sib & 7) == 4 && (cpu_mod || (sib & 7) != 5)) /*ESP*/
                {
                        addbyte(0x05);
                        addlong(stack_offset);
                }
                if (((sib & 7) == 4 || (cpu_mod && (sib & 7) == 5)) && !op_ssegs)
                        op_ea_seg = &_ss;
                if (((sib >> 3) & 7) != 4)
                {
                        switch (sib >> 6)
                        {
                                case 0:
                                addbyte(0x03); /*ADDL regs[sib&7].l, %eax*/
                                addbyte(0x45);
                                addbyte((uint8_t)cpu_state_offset(regs[(sib >> 3) & 7].l));
                                break;
                                case 1:
                                addbyte(0x8B); addbyte(0x5D); addbyte((uint8_t)cpu_state_offset(regs[(sib >> 3) & 7].l)); /*MOVL armregs[RD],%ebx*/
                                addbyte(0x01); addbyte(0xD8); /*ADDL %ebx,%eax*/
                                addbyte(0x01); addbyte(0xD8); /*ADDL %ebx,%eax*/
                                break;
                                case 2:
                                addbyte(0x8B); addbyte(0x5D); addbyte((uint8_t)cpu_state_offset(regs[(sib >> 3) & 7].l)); /*MOVL armregs[RD],%ebx*/
                                addbyte(0xC1); addbyte(0xE3); addbyte(2); /*SHL $2,%ebx*/
                                addbyte(0x01); addbyte(0xD8); /*ADDL %ebx,%eax*/
                                break;
                                case 3:
                                addbyte(0x8B); addbyte(0x5D); addbyte((uint8_t)cpu_state_offset(regs[(sib >> 3) & 7].l)); /*MOVL armregs[RD],%ebx*/
                                addbyte(0xC1); addbyte(0xE3); addbyte(3); /*SHL $2,%ebx*/
                                addbyte(0x01); addbyte(0xD8); /*ADDL %ebx,%eax*/
                                break;
                        }
                }
                addbyte(0xa3);
                addlong((uint32_t)&cpu_state.eaaddr);
        }
        else
        {
                if (!cpu_mod && cpu_rm == 5)
                {                
                        new_eaaddr = fastreadl(cs + (*op_pc) + 1);
                        addbyte(0xC7); /*MOVL $new_eaaddr,(eaaddr)*/
                        addbyte(0x45);
                        addbyte((uint8_t)cpu_state_offset(eaaddr));
                        addlong(new_eaaddr);
                        (*op_pc) += 4;
                        return op_ea_seg;
                }
                addbyte(0x8b); /*MOVL regs[sib&7].l, %eax*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(regs[cpu_rm].l));
                cpu_state.eaaddr = cpu_state.regs[cpu_rm].l;
                if (cpu_mod) 
                {
                        if (cpu_rm == 5 && !op_ssegs)
                                op_ea_seg = &_ss;
                        if (cpu_mod == 1) 
                        {
                                addbyte(0x05);
                                addlong((uint32_t)(int8_t)(fetchdat >> 8)); 
                                (*op_pc)++; 
                        }
                        else          
                        {
                                new_eaaddr = fastreadl(cs + (*op_pc) + 1);
                                addbyte(0x05);
                                addlong(new_eaaddr); 
                                (*op_pc) += 4;
                        }
                }
                addbyte(0xa3);
                addlong((uint32_t)&cpu_state.eaaddr);
        }
        return op_ea_seg;
}

void codegen_generate_call(uint8_t opcode, OpFn op, uint32_t fetchdat, uint32_t new_pc, uint32_t old_pc)
{
        codeblock_t *block = &codeblock[block_current];
        uint32_t op_32 = use32;
        uint32_t op_pc = new_pc;
        const OpFn *op_table = x86_dynarec_opcodes;
        RecompOpFn *recomp_op_table = recomp_opcodes;
        int opcode_shift = 0;
        int opcode_mask = 0x3ff;
        int over = 0;
        int pc_off = 0;
        int test_modrm = 1;
        int c;
        
        op_ea_seg = &_ds;
        op_ssegs = 0;
        op_old_pc = old_pc;
        
        for (c = 0; c < NR_HOST_REGS; c++)
                host_reg_mapping[c] = -1;
        mmx_ebx_ecx_loaded = 0;
        for (c = 0; c < NR_HOST_XMM_REGS; c++)
                host_reg_xmm_mapping[c] = -1;
        
        codegen_timing_start();

        while (!over)
        {
                switch (opcode)
                {
                        case 0x0f:
                        op_table = x86_dynarec_opcodes_0f;
                        recomp_op_table = recomp_opcodes_0f;
                        over = 1;
                        break;
                        
                        case 0x26: /*ES:*/
                        op_ea_seg = &_es;
                        op_ssegs = 1;
                        break;
                        case 0x2e: /*CS:*/
                        op_ea_seg = &_cs;
                        op_ssegs = 1;
                        break;
                        case 0x36: /*SS:*/
                        op_ea_seg = &_ss;
                        op_ssegs = 1;
                        break;
                        case 0x3e: /*DS:*/
                        op_ea_seg = &_ds;
                        op_ssegs = 1;
                        break;
                        case 0x64: /*FS:*/
                        op_ea_seg = &_fs;
                        op_ssegs = 1;
                        break;
                        case 0x65: /*GS:*/
                        op_ea_seg = &_gs;
                        op_ssegs = 1;
                        break;
                        
                        case 0x66: /*Data size select*/
                        op_32 = ((use32 & 0x100) ^ 0x100) | (op_32 & 0x200);
                        break;
                        case 0x67: /*Address size select*/
                        op_32 = ((use32 & 0x200) ^ 0x200) | (op_32 & 0x100);
                        break;
                        
                        case 0xd8:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_d8_a32 : x86_dynarec_opcodes_d8_a16;
                        recomp_op_table = recomp_opcodes_d8;
                        opcode_shift = 3;
                        opcode_mask = 0x1f;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xd9:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_d9_a32 : x86_dynarec_opcodes_d9_a16;
                        recomp_op_table = recomp_opcodes_d9;
                        opcode_mask = 0xff;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xda:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_da_a32 : x86_dynarec_opcodes_da_a16;
                        recomp_op_table = recomp_opcodes_da;
                        opcode_mask = 0xff;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xdb:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_db_a32 : x86_dynarec_opcodes_db_a16;
                        recomp_op_table = recomp_opcodes_db;
                        opcode_mask = 0xff;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xdc:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_dc_a32 : x86_dynarec_opcodes_dc_a16;
                        recomp_op_table = recomp_opcodes_dc;
                        opcode_shift = 3;
                        opcode_mask = 0x1f;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xdd:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_dd_a32 : x86_dynarec_opcodes_dd_a16;
                        recomp_op_table = recomp_opcodes_dd;
                        opcode_mask = 0xff;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xde:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_de_a32 : x86_dynarec_opcodes_de_a16;
                        recomp_op_table = recomp_opcodes_de;
                        opcode_mask = 0xff;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        case 0xdf:
                        op_table = (op_32 & 0x200) ? x86_dynarec_opcodes_df_a32 : x86_dynarec_opcodes_df_a16;
                        recomp_op_table = recomp_opcodes_df;
                        opcode_mask = 0xff;
                        over = 1;
                        pc_off = -1;
                        test_modrm = 0;
                        block->flags |= CODEBLOCK_HAS_FPU;
                        break;
                        
                        case 0xf0: /*LOCK*/
                        break;
                        
                        case 0xf2: /*REPNE*/
                        op_table = x86_dynarec_opcodes_REPNE;
                        recomp_op_table = recomp_opcodes_REPNE;
                        break;
                        case 0xf3: /*REPE*/
                        op_table = x86_dynarec_opcodes_REPE;
                        recomp_op_table = recomp_opcodes_REPE;
                        break;

                        default:
                        goto generate_call;
                }
                fetchdat = fastreadl(cs + op_pc);
                codegen_timing_prefix(opcode, fetchdat);
                if (cpu_state.abrt)
                        return;
                opcode = fetchdat & 0xff;
                if (!pc_off)
                        fetchdat >>= 8;
                
                op_pc++;
        }
        
generate_call:
        codegen_timing_opcode(opcode, fetchdat, op_32);

        if ((op_table == x86_dynarec_opcodes &&
              ((opcode & 0xf0) == 0x70 || (opcode & 0xfc) == 0xe0 || opcode == 0xc2 ||
              (opcode & 0xfe) == 0xca || (opcode & 0xfc) == 0xcc || (opcode & 0xfc) == 0xe8 ||
              (opcode == 0xff && ((fetchdat & 0x38) >= 0x10 && (fetchdat & 0x38) < 0x30)))) ||
            (op_table == x86_dynarec_opcodes_0f && ((opcode & 0xf0) == 0x80)))
        {
                /*Opcode is likely to cause block to exit, update cycle count*/
                if (codegen_block_cycles)
                {
                        addbyte(0x81); /*SUB $codegen_block_cycles, cyclcs*/
                        addbyte(0x6d);
                        addbyte((uint8_t)cpu_state_offset(_cycles));
                        addlong(codegen_block_cycles);
                        codegen_block_cycles = 0;
                }
                if (codegen_block_ins)
                {
                        addbyte(0x81); /*ADD $codegen_block_ins,ins*/
                        addbyte(0x45);
                        addbyte((uint8_t)cpu_state_offset(cpu_recomp_ins));
                        addlong(codegen_block_ins);
                        codegen_block_ins = 0;
                }
#if 0
                if (codegen_block_full_ins)
                {
                        addbyte(0x81); /*ADD $codegen_block_ins,ins*/
                        addbyte(0x05);
                        addlong((uint32_t)&cpu_recomp_full_ins);
                        addlong(codegen_block_full_ins);
                        codegen_block_full_ins = 0;
                }
#endif
        }

        if ((op_table == x86_dynarec_opcodes_REPNE || op_table == x86_dynarec_opcodes_REPE) && !op_table[opcode | op_32])
        {
                op_table = x86_dynarec_opcodes;
                recomp_op_table = recomp_opcodes;
        }

        if (recomp_op_table && recomp_op_table[(opcode | op_32) & 0x1ff])
        {
                uint32_t new_pc = recomp_op_table[(opcode | op_32) & 0x1ff](opcode, fetchdat, op_32, op_pc, block);
                if (new_pc)
                {
                        if (new_pc != -1)
                                STORE_IMM_ADDR_L((uintptr_t)&cpu_state.pc, new_pc);

                        codegen_block_ins++;
                        block->ins++;
                        codegen_block_full_ins++;
                        codegen_endpc = (cs + cpu_state.pc) + 8;

                        return;
                }
        }
        
        op = op_table[((opcode >> opcode_shift) | op_32) & opcode_mask];
        if (op_ssegs != last_ssegs)
        {
                last_ssegs = op_ssegs;

                addbyte(0xC6); /*MOVB [ssegs],op_ssegs*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(ssegs));
                addbyte(op_pc + pc_off);
        }

        if (!test_modrm ||
                (op_table == x86_dynarec_opcodes && opcode_modrm[opcode]) ||
                (op_table == x86_dynarec_opcodes_0f && opcode_0f_modrm[opcode]))
        {
                int stack_offset = 0;
                
                if (op_table == x86_dynarec_opcodes && opcode == 0x8f) /*POP*/
                        stack_offset = (op_32 & 0x100) ? 4 : 2;

                cpu_mod = (fetchdat >> 6) & 3;
                cpu_reg = (fetchdat >> 3) & 7;
                cpu_rm = fetchdat & 7;

                addbyte(0xC7); /*MOVL $rm | mod | reg,(rm_mod_reg_data)*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(rm_data.rm_mod_reg_data));
                addlong(cpu_rm | (cpu_mod << 8) | (cpu_reg << 16));

                op_pc += pc_off;
                if (cpu_mod != 3 && !(op_32 & 0x200))
                        op_ea_seg = codegen_generate_ea_16_long(op_ea_seg, fetchdat, op_ssegs, &op_pc);
                if (cpu_mod != 3 &&  (op_32 & 0x200))
                        op_ea_seg = codegen_generate_ea_32_long(op_ea_seg, fetchdat, op_ssegs, &op_pc, stack_offset);
                op_pc -= pc_off;
        }

        if (op_ea_seg != last_ea_seg)
        {
                last_ea_seg = op_ea_seg;
                addbyte(0xC7); /*MOVL $&_ds,(ea_seg)*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(ea_seg));
                addlong((uint32_t)op_ea_seg);
        }

        addbyte(0xC7); /*MOVL pc,new_pc*/
        addbyte(0x45);
        addbyte((uint8_t)cpu_state_offset(pc));
        addlong(op_pc + pc_off);

        addbyte(0xC7); /*MOVL $old_pc,(oldpc)*/
        addbyte(0x45);
        addbyte((uint8_t)cpu_state_offset(oldpc));
        addlong(old_pc);

        if (op_32 != last_op32)
        {
                last_op32 = op_32;
                addbyte(0xC7); /*MOVL $use32,(op32)*/
                addbyte(0x45);
                addbyte((uint8_t)cpu_state_offset(op32));
                addlong(op_32);
        }

        addbyte(0xC7); /*MOVL $fetchdat,(%esp)*/
        addbyte(0x04);
        addbyte(0x24);
        addlong(fetchdat);
  
        addbyte(0xE8); /*CALL*/
        addlong(((uint8_t *)op - (uint8_t *)(&block->data[block_pos + 4])));

        codegen_block_ins++;
        
        block->ins++;

        addbyte(0x09); /*OR %eax, %eax*/
        addbyte(0xc0);
        addbyte(0x0F); addbyte(0x85); /*JNZ 0*/
        addlong((uint32_t)&block->data[BLOCK_EXIT_OFFSET] - (uint32_t)(&block->data[block_pos + 4]));

        codegen_endpc = (cs + cpu_state.pc) + 8;
}

#endif
