Bool ParseAsmImm(CCompCtrl *cc, CAsmArg *arg)
{
	if (arg->imm_or_off_present)
		LexExcept(cc, "Already one immediate at ");
	arg->imm_or_off_present = TRUE;
	arg->num.local_asm_undef_hash = NULL;
	arg->num.global_asm_undef_hash = NULL;
	cc->asm_undef_hash = NULL;
	cc->abs_counts = 0;
	cc->flags &= ~(CCF_UNRESOLVED + CCF_LOCAL);
	if (!IsLexExpression2Bin(cc, &arg->num.machine_code))
		LexSkipEol(cc);
	else
	{
		if (cc->abs_counts.externs)
			LexExcept(cc, "Extern Not Allowed at ");
		if (cc->flags & CCF_UNRESOLVED)
		{
			if (cc->flags & CCF_LOCAL)
				arg->num.local_asm_undef_hash = cc->asm_undef_hash;
			else
				arg->num.global_asm_undef_hash = cc->asm_undef_hash;

			cc->asm_undef_hash = NULL;
		} 
		else
		{
			arg->num.i = Call(arg->num.machine_code);
			arg->num.global_asm_undef_hash = cc->asm_undef_hash;
			cc->asm_undef_hash = NULL;
			Free(arg->num.machine_code);
			arg->num.machine_code = NULL;
		}
	}
	return TRUE;
}

U0 ParseAsmArg(CCompCtrl *cc, CAsmArg *arg, Bool rel)
{
	CHashGeneric *tmph, *tmph1;
	CHashReg	 *tmpr;

	MemSet(arg, 0, sizeof(CAsmArg));
	arg->seg  = REG_NONE;
	arg->reg1 = REG_NONE;
	arg->reg2 = REG_NONE;
	arg->scale = 1;
	while (TRUE)
	{
		if (cc->token == TK_IDENT)
		{
			if (tmph = cc->hash_entry)
			{
				if (tmph->type & HTG_TYPE_MASK == HTT_REG)
				{
					tmpr = tmph;
					arg->reg1_type = tmpr->reg_type;
					switch (tmpr->reg_type)
					{
						start:
							case REGT_R8:
								arg->size = 1;
								break;

							case REGT_R16:
								arg->size = 2;
								break;

							case REGT_R32:
								arg->size = 4;
								break;

							case REGT_R64:
							case REGT_FSTACK:
							case REGT_MM:
								arg->size = 8;
								break;

							case REGT_XMM:
								arg->size = 16;
						end:
							arg->reg1 = tmpr->reg_num;
							Lex(cc);
							return;

						case REGT_SEG:
							arg->seg = tmpr->reg_num;
							if (Lex(cc) != ':')
							{
								arg->just_seg = TRUE;
								return;
							}
							else
								Lex(cc); //skip ":"
							break;
					}
				}
				else
				{
					if ((tmph->type & HTG_TYPE_MASK == HTT_CLASS || tmph->type & HTG_TYPE_MASK == HTT_INTERNAL_TYPE) &&
							(tmph1 = HashFind(cc->cur_str, cmp.asm_hash, HTT_ASM_KEYWORD)))
						tmph = tmph1;
					if (tmph->type & HTG_TYPE_MASK == HTT_ASM_KEYWORD)
					{
						switch (tmph->user_data0)
						{
							case AKW_I8:
							case AKW_U8:
								arg->size = 1;
								break;

							case AKW_I16:
							case AKW_U16:
								arg->size = 2;
								break;

							case AKW_I32:
							case AKW_U32:
								arg->size = 4;
								break;

							case AKW_I64:
							case AKW_U64:
								arg->size = 8;
								break;

							default:
								LexExcept(cc, "syntax error at ");
						}
						Lex(cc); //skip keyword
					}
					else
						goto pa_asm_direct_imm;
				}
			}
			else
			{
pa_asm_direct_imm:
				ParseAsmImm(cc, arg);
				arg->num.abs_counts = cc->abs_counts;
				if (arg->size <= 1 && !rel && arg->num.abs_counts & 1)
				{
					if (cc->aotc->seg_size == 16)
						arg->size = 2;
					else
						arg->size = 4;
				}
				if (cc->token != '[')
					return;
			}
		}
		else if (cc->token == '[')
		{
			arg->indirect = TRUE;
			Lex(cc); // skip [
			while (cc->token && cc->token != ']')
			{
				if (cc->token == TK_IDENT)
				{
					if (tmph = cc->hash_entry)
					{
						if (tmph->type & HTG_TYPE_MASK == HTT_REG && REGT_R16 <= tmph(CHashReg *)->reg_type <= REGT_R64)
						{
							tmpr = tmph;
							arg->reg2_type = tmpr->reg_type;
							if (arg->reg1 == REG_NONE)
							{
								if (tmpr->reg_num & 7 == REG_RSP)
								{
									arg->reg1 = 4;
									arg->reg2 = tmpr->reg_num;
								}
								else
									arg->reg1 = tmpr->reg_num;
							}
							else
								arg->reg2 = tmpr->reg_num;
							Lex(cc);
						}
						else
							goto pa_asm_indirect_imm;
					}
					else
						goto pa_asm_indirect_imm;
				}
				else if (cc->token == '*')
				{
					Lex(cc);
					if (cc->token != TK_I64)
						LexExcept(cc, "Expecting scale factor at ");
					arg->scale = cc->cur_i64;
					Lex(cc); //skip scale
					if (arg->reg2 != REG_NONE)
					{
						SwapI64(&arg->reg1, &arg->reg2);
						SwapI64(&arg->reg1_type, &arg->reg2_type);
					}
				}
				else if (cc->token == '+')
				{
					Lex(cc); //skip '+'
				}
				else
				{
pa_asm_indirect_imm:
					ParseAsmImm(cc, arg);
					arg->num.abs_counts = cc->abs_counts;
				}
			}
			if (cc->token != ']')
				LexExcept(cc, "Missing ']' at ");
			Lex(cc); //skip ]
			return;
		}
		else
			goto pa_asm_direct_imm;
	}
}

I64 AsmMakeArgMask(CCompCtrl *cc, CAsmArg *arg)
{
	CAOTCtrl	*aotc = cc->aotc;
	I64			 res;

	if (arg->just_seg)
	{
		switch (arg->seg)
		{
			case 0:
				res = 1 << ARGT_ES | 1 << ARGT_SREG;
				break;

			case 1:
				res = 1 << ARGT_CS | 1 << ARGT_SREG;
				break;

			case 2:
				res = 1 << ARGT_SS | 1 << ARGT_SREG;
				break;

			case 3:
				res = 1 << ARGT_DS | 1 << ARGT_SREG;
				break;

			case 4:
				res = 1 << ARGT_FS | 1 << ARGT_SREG;
				break;

			case 5:
				res = 1 << ARGT_GS | 1 << ARGT_SREG;
				break;
		}
		goto mm_done;
	}
	if (arg->reg1_type == REGT_FSTACK)
	{
		if (arg->reg1)
			res = 1 << ARGT_STI;
		else
			res = 1 << ARGT_ST0 | 1 << ARGT_STI;
		goto mm_done;
	}
	res = cmp.size_arg_mask[arg->size];
	if (aotc->seg_size == 64)
		res &= 0xFF0FFFFFFF;

	if (arg->reg1 != REG_NONE && arg->imm_or_off_present && !arg->num.i &&
			!arg->num.global_asm_undef_hash && !arg->num.local_asm_undef_hash)
		arg->imm_or_off_present = FALSE;	//Zero displacement

	if (arg->reg2 != REG_NONE || arg->scale != 1)
	{
		res &= 0x0000FF0000;
		goto mm_done;
	}

	if (arg->indirect)
	{
		if (arg->imm_or_off_present)
			res &= 0x00FFFF0000;
		else
			res &= 0x000FFF0000;
	}
	else
	{
		if (arg->imm_or_off_present)
			res &= 0x000F000FFE;
		else
			res &= 0x3F0FFFF000;
	}
	if (arg->seg != REG_NONE)
		res &= 0x00FFFF0000;
	if (arg->reg1 == REG_NONE)
	{
		if (arg->indirect)
			res &= 0x00FFFF0000;
		else if (arg->num.i < 0)
		{
			if (arg->num.i >= I8_MIN)
				res &= 0x8FE;
			else if (arg->num.i >= I16_MIN)
				res &= 0x8EE;
			else if (arg->num.i >= I32_MIN)
				res &= 0x8CE;
			else
				res &= 0x88E;
		}
		else
		{
			if (arg->num.i <= I8_MAX)
				res &= 0xFFE;
			else if (arg->num.i <= U8_MAX)
				res &= 0xFEE;
			else if (arg->num.i <= I16_MAX)
				res &= 0xEEE;
			else if (arg->num.i <= U16_MAX)
				res &= 0xECE;
			else if (arg->num.i <= I32_MAX)
				res &= 0xCCE;
			else if (arg->num.i <= U32_MAX)
				res &= 0xC8E;
			else
				res &= 0x88E;
		}
	}
	else
	{
		res &= 0x3F00FFF000;
		if (!arg->indirect) //M8-M64
			res &= 0xFFFF0FFFFF;
	}
	switch (arg->reg1)
	{
		case REG_RAX:
			res &= ~0x3000000000;
			break;

		case REG_RCX:
			res &= ~0x2F00000000;
			break;

		case REG_RDX:
			res &= ~0x1F00000000;
			break;

		default:
			res &= ~0x3F00000000;
	}
mm_done:
	return res;
}

Bool AsmStoreNum(CCompCtrl *cc, CAsmNum2 *num2, I64 count, Bool U8_avail)
{
	CAOTCtrl	*aotc = cc->aotc;
	I64			 i;
	CAOTAbsAddr	*tmpa;

	if (!num2->imm_flag)
		num2->num.i -= num2->rel;
	for (i = 0; i < count; i++)
	{
		if (num2->U8_count == 1)
		{
			if (num2->num.local_asm_undef_hash || num2->num.global_asm_undef_hash)
				AsmUnresolvedAdd(cc, num2->num.machine_code, IET_REL_I8 + num2->imm_flag, aotc->rip, num2->rel,
					num2->num.local_asm_undef_hash, num2->num.global_asm_undef_hash,
					cc->lex_include_stack->line_num, U8_avail);
			else if (!num2->imm_flag && !(I8_MIN <= num2->num.i <= I8_MAX))
				LexExcept(cc,"Branch out of range at ");
			if (num2->imm_flag)
			{
				if (num2->num.abs_counts.abs_addres & 1)
				{
					tmpa = CAlloc(sizeof(CAOTAbsAddr));
					tmpa->next = aotc->abss;
					aotc->abss = tmpa;
					tmpa->rip = aotc->rip;
					tmpa->type = AAT_ADD_U8;
				}
			}
			else
			{
				if (num2->num.abs_counts.c_addres & 1)
				{
					tmpa = CAlloc(sizeof(CAOTAbsAddr));
					tmpa->next = aotc->abss;
					aotc->abss = tmpa;
					tmpa->rip = aotc->rip;
					tmpa->type = AAT_SUB_U8;
				}
			}
			AOTStoreCodeU8(cc, num2->num.i);
		}
		else
		{
			if (num2->U8_count == 2)
			{
				if (num2->num.local_asm_undef_hash || num2->num.global_asm_undef_hash)
					AsmUnresolvedAdd(cc, num2->num.machine_code, IET_REL_I16 + num2->imm_flag, aotc->rip, num2->rel,
						num2->num.local_asm_undef_hash, num2->num.global_asm_undef_hash,
						cc->lex_include_stack->line_num, U8_avail);
				else if (!num2->imm_flag && !(I16_MIN <= num2->num.i <= I16_MAX))
					LexExcept(cc,"Branch out of range at ");
				if (num2->imm_flag)
				{
					if (num2->num.abs_counts.abs_addres & 1)
					{
						tmpa = CAlloc(sizeof(CAOTAbsAddr));
						tmpa->next = aotc->abss;
						aotc->abss = tmpa;
						tmpa->rip = aotc->rip;
						tmpa->type = AAT_ADD_U16;
					}
				}
				else
				{
					if (num2->num.abs_counts.c_addres & 1)
					{
						tmpa = CAlloc(sizeof(CAOTAbsAddr));
						tmpa->next = aotc->abss;
						aotc->abss = tmpa;
						tmpa->rip = aotc->rip;
						tmpa->type = AAT_SUB_U16;
					}
				}
				AOTStoreCodeU8(cc, num2->num.i.u8[0]);
				AOTStoreCodeU8(cc, num2->num.i.u8[1]);
			}
			else if (num2->U8_count == 4)
			{
				if (num2->num.local_asm_undef_hash || num2->num.global_asm_undef_hash)
					AsmUnresolvedAdd(cc, num2->num.machine_code, IET_REL_I32 + num2->imm_flag, aotc->rip, num2->rel,
						num2->num.local_asm_undef_hash, num2->num.global_asm_undef_hash,
						cc->lex_include_stack->line_num, U8_avail);
				else if (!num2->imm_flag && !(I32_MIN <= num2->num.i <= I32_MAX))
					LexExcept(cc,"Branch out of range at ");
				if (num2->imm_flag)
				{
					if (num2->num.abs_counts.abs_addres & 1)
					{
						tmpa = CAlloc(sizeof(CAOTAbsAddr));
						tmpa->next = aotc->abss;
						aotc->abss = tmpa;
						tmpa->rip = aotc->rip;
						tmpa->type = AAT_ADD_U32;
					}
				}
				else
				{
					if (num2->num.abs_counts.c_addres & 1)
					{
						tmpa = CAlloc(sizeof(CAOTAbsAddr));
						tmpa->next = aotc->abss;
						aotc->abss = tmpa;
						tmpa->rip = aotc->rip;
						tmpa->type = AAT_SUB_U32;
					}
				}
				AOTStoreCodeU32(cc, num2->num.i);
			}
			else if (num2->U8_count == 8)
			{
				if (num2->num.local_asm_undef_hash || num2->num.global_asm_undef_hash)
					AsmUnresolvedAdd(cc, num2->num.machine_code, IET_REL_I64 + num2->imm_flag, aotc->rip, num2->rel,
						num2->num.local_asm_undef_hash, num2->num.global_asm_undef_hash,
						cc->lex_include_stack->line_num, U8_avail);
				if (num2->imm_flag)
				{
					if (num2->num.abs_counts.abs_addres & 1)
					{
						tmpa = CAlloc(sizeof(CAOTAbsAddr));
						tmpa->next = aotc->abss;
						aotc->abss = tmpa;
						tmpa->rip = aotc->rip;
						tmpa->type = AAT_ADD_U64;
					}
				}
				else
				{
					if (num2->num.abs_counts.c_addres & 1)
					{
						tmpa = CAlloc(sizeof(CAOTAbsAddr));
						tmpa->next = aotc->abss;
						aotc->abss = tmpa;
						tmpa->rip = aotc->rip;
						tmpa->type = AAT_SUB_U64;
					}
				}
				AOTStoreCodeU64(cc, num2->num.i);
			}
			if (U8_avail && !num2->num.local_asm_undef_hash && !num2->num.global_asm_undef_hash &&
				!num2->imm_flag && -124 <= num2->num.i <= 123)
			{
				LexWarn(cc, "could use I8 displacement at ");
				return FALSE;
			}
		}
	}

	return TRUE;
}

U8 asm_seg_prefixes[6] = {0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65};

Bool ParseAsmInst(CCompCtrl *cc, CHashOpcode *tmpo, I64 argcount)
{
	CAOTCtrl	*aotc = cc->aotc;
	I64			 i, j, arg1, arg2, om, seg, arg1mask, arg2mask;
	CAsmArg		*tmpa1, *tmpa2;
	Bool		 ModrM_complete, U8_avail = FALSE, found_second_possible = FALSE;
	CInst		*tmpins;
	CAsmIns		 cur, best;

	best.U8_count = 255;
	if (argcount > 0)
		arg1mask = AsmMakeArgMask(cc, &aotc->arg1);
	else
		arg1mask = 1;
	if (argcount > 1)
		arg2mask = AsmMakeArgMask(cc, &aotc->arg2);
	else
		arg2mask = 1;
	for (i = 0; i < tmpo->inst_entry_count; i++)
	{
		tmpins = &tmpo->ins[i];
		if (tmpins->arg1 == ARGT_REL8 || tmpins->arg2 == ARGT_REL8)
			U8_avail = TRUE;
		if (Bt(&arg1mask, tmpins->arg1) && Bt(&arg2mask, tmpins->arg2) &&
					(!(tmpins->flags & IEF_NOT_IN_64_BIT) || aotc->seg_size != 64))
		{
			MemSet(&cur, 0, sizeof(CAsmIns));
			cur.tmpins = tmpins;
			ModrM_complete = FALSE;
			cur.is_default = ToBool(tmpins->flags & IEF_DEFAULT);
			if (aotc->seg_size == 64)
			{
				if (tmpins->flags & IEF_48_REX)
					cur.REX = 0x48;
				else
					cur.REX = 0x40;
			}
			cur.disp.imm_flag = TRUE;
			cur.imm.imm_flag = TRUE;
			om = tmpins->opcode_modifier;
			arg1 = tmpins->arg1;
			arg2 = tmpins->arg2;
			tmpa1 = &aotc->arg1;
			tmpa2 = &aotc->arg2;
			cur.last_opcode_U8 = tmpins->opcode[tmpins->opcode_count - 1];

			if (tmpins->slash_val < 8)
			{
				cur.ModrM |= tmpins->slash_val << 3;
				cur.has_ModrM = TRUE;
			}

			if (aotc->seg_size == 16 && tmpins->flags & IEF_OP_SIZE32 || aotc->seg_size != 16 && tmpins->flags & IEF_OP_SIZE16)
				cur.has_operand_prefix = TRUE;

			if (om == OM_IB)
				cur.imm.U8_count = 1;
			else if (om == OM_IW)
				cur.imm.U8_count = 2;
			else if (om == OM_ID)
				cur.imm.U8_count = 4;

			if (om == OM_CB)
			{
				cur.imm.U8_count = 1;
				cur.imm.imm_flag = FALSE;
			}
			else if (om == OM_CW)
			{
				cur.imm.U8_count = 2;
				cur.imm.imm_flag = FALSE;
			}
			else if (om==OM_CD)
			{
				cur.imm.U8_count = 4;
				cur.imm.imm_flag = FALSE;
			}

			if (argcount == 1)
			{
				if (best.U8_count != 255 && !found_second_possible && !best.is_default)
				{
					found_second_possible = TRUE;
					if (!aotc->arg1.size)
						PrintWarn("no size specified at %s,%04d\n",
									cc->lex_include_stack->full_name,
									cc->lex_include_stack->line_num - 1);
				}
				if (tmpins->flags & IEF_PLUS_OPCODE)
				{
					if (tmpins->slash_val == SV_R_REG)
					{
						cur.last_opcode_U8 |= tmpa1->reg1 & 7;
						if (tmpa1->reg1 & 15 > 7)
							cur.REX |= 1;
						if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
							cur.has_REX = TRUE;
					}
					else
					{//SV_I_REG
						if (tmpa1->reg1_type == REGT_FSTACK)
							cur.last_opcode_U8 += tmpa1->reg1;
					}
				}
				if (arg1 == ARGT_R64 || arg1 == ARGT_RM64 || arg1 == ARGT_M64)
					cur.REX |= 8;
				if (ARGT_RM8 <= arg1 <= ARGT_RM64 || ARGT_M8 <= arg1 <= ARGT_M64)
				{
					if (aotc->seg_size == 16)
						cur.has_addr_prefix = TRUE;

					cur.has_ModrM = TRUE;
					if (tmpa1->imm_or_off_present && tmpa1->indirect && tmpa1->reg1 == REG_NONE)
					{
						cur.ModrM = cur.ModrM + 5;
						MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
						cur.disp.U8_count = 4;
						if (aotc->seg_size == 64)
							cur.disp.imm_flag = FALSE;
					}
					else
					{
						if (tmpa1->reg2 == REG_NONE && tmpa1->scale == 1)
						{
							cur.ModrM |= tmpa1->reg1 & 7;
							if (tmpa1->reg1 & 15 > 7)
								cur.REX |= 1;
							if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
						}
						else
						{
							cur.ModrM |= 4;
							cur.has_SIB = TRUE;
							if (tmpa1->scale == 1)
								cur.SIB = 0;
							else if (tmpa1->scale == 2)
								cur.SIB = 0x40;
							else if (tmpa1->scale == 4)
								cur.SIB = 0x80;
							else if (tmpa1->scale == 8)
								cur.SIB = 0xC0;
							if (tmpa1->reg2 == REG_NONE)
							{
								ModrM_complete = TRUE;
								cur.SIB |= (tmpa1->reg1 & 7) << 3 + REG_RBP;
								if (tmpa1->reg1 & 15 > 7)
									cur.REX |= 2;
								if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
									cur.has_REX = TRUE;
								MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
								cur.disp.U8_count = 4;
							}
							else
							{
								cur.SIB |= (tmpa1->reg1 & 7) << 3 + tmpa1->reg2 & 7;
								if (tmpa1->reg1 & 15 > 7)
									cur.REX |= 2;
								if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
									cur.has_REX = TRUE;
								if (tmpa1->reg2 & 15 > 7)
									cur.REX |= 1;
								if (tmpa1->reg2 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
									cur.has_REX = TRUE;
								if (tmpa1->reg2&7 == REG_RBP && !tmpa1->imm_or_off_present && tmpa1->indirect)
								{
									cur.ModrM |= 0x40;
									cur.disp.U8_count = 1;
									ModrM_complete = TRUE;
								}
							}
						}
						if (!ModrM_complete)
						{
							if (tmpa1->imm_or_off_present)
							{
								MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
								if (!cur.disp.num.machine_code && I8_MIN <= cur.disp.num.i <= I8_MAX)
								{
									cur.ModrM |= 0x40;
									cur.disp.U8_count = 1;
								}
								else if (aotc->seg_size == 16)
								{
									cur.ModrM |= 0x80;
									cur.disp.U8_count = 2;
								}
								else
								{
									cur.ModrM |= 0x80;
									cur.disp.U8_count = 4;
								}
							}
							else if (!tmpa1->indirect)
							{
								cur.has_addr_prefix = FALSE;
								cur.ModrM |= 0xC0;
							}
							else
							{
								if (tmpa1->reg1 & 7 == REG_RBP)
								{
									cur.ModrM |= 0x40;
									cur.disp.U8_count = 1;
								}
							}
						}
					}
				}
				else if (ARGT_REL8  <= arg1 <= ARGT_REL32 ||
						 ARGT_IMM8  <= arg1 <= ARGT_IMM64 ||
						 ARGT_UIMM8 <= arg1 <= ARGT_UIMM64)
				{
					if (arg1 == ARGT_IMM64 || arg2 == ARGT_UIMM64)
						cur.REX |= 8;
					MemCopy(&cur.imm.num, &tmpa1->num, sizeof(CAsmNum));
				}
			}
			else if (argcount == 2)
			{
				if (best.U8_count != 255 && !found_second_possible && !best.is_default)
				{
					found_second_possible = TRUE;
					if (!aotc->arg1.size && !aotc->arg2.size)
						PrintWarn("no size specified at %s,%04d\n",
									cc->lex_include_stack->full_name,
									cc->lex_include_stack->line_num - 1);
				}
				if (tmpins->flags & IEF_PLUS_OPCODE)
				{
					if (tmpins->slash_val == SV_R_REG)
					{
						if (ARGT_AL <= arg1 <= ARGT_RAX)
						{
							cur.last_opcode_U8 |= tmpa2->reg1 & 7;
							if (tmpa2->reg1 & 15 > 7)
								cur.REX |= 1;
							if (tmpa2->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
						}
						else
						{
							cur.last_opcode_U8 |= tmpa1->reg1 & 7;
							if (tmpa1->reg1 & 15 > 7)
								cur.REX |= 1;
							if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
						}
					}
					else
					{//SV_I_REG
						if (tmpa1->reg1_type == REGT_FSTACK)
							cur.last_opcode_U8 |= tmpa1->reg1;
						if (tmpa2->reg1_type == REGT_FSTACK)
							cur.last_opcode_U8 |= tmpa2->reg1;
					}
				}
				if (arg1 == ARGT_RM64 || arg2 == ARGT_RM64 ||
					arg1 == ARGT_M64  || arg2 == ARGT_M64 ||
					arg1 == ARGT_R64  || arg2 == ARGT_R64)

					cur.REX |= 8;
				if (ARGT_RM8 <= arg1 <= ARGT_RM64 ||
					ARGT_RM8 <= arg2 <= ARGT_RM64 ||
					ARGT_M8  <= arg1 <= ARGT_M64  ||
					ARGT_M8  <= arg2 <= ARGT_M64)
				{
					if (aotc->seg_size == 16)
						cur.has_addr_prefix = TRUE;
					cur.has_ModrM = TRUE;
					if (ARGT_RM8 <= arg2 <= ARGT_RM64 || ARGT_M8 <= arg2 <= ARGT_M64)
					{
						tmpa1 = &aotc->arg2;
						tmpa2 = &aotc->arg1;
					}
					if (tmpins->slash_val == SV_R_REG)
					{
						if (tmpa2->just_seg)
							cur.ModrM |= tmpa2->seg << 3;
						else
						{
							if (tmpa2->reg1 == REG_NONE)
							{
								cur.ModrM |= (tmpa1->reg1 & 7) << 3;
								if (tmpa1->reg1 & 15 > 7)
									cur.REX |= 4;
								if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
									cur.has_REX = TRUE;
							}
							else
							{
								cur.ModrM |= (tmpa2->reg1 & 7) << 3;
								if (tmpa2->reg1 & 15 >7)
									cur.REX |= 4;
								if (tmpa2->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
									cur.has_REX = TRUE;
							}
						}
					}
					if (tmpa1->reg2 == REG_NONE && tmpa1->scale == 1)
					{
						if (tmpa1->reg1 != REG_NONE)
						{
							cur.ModrM |= tmpa1->reg1 & 7;
							if (tmpa1->reg1 & 15 > 7)
								cur.REX |= 1;
							if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
						}
					}
					else
					{
						cur.ModrM |= 4;
						cur.has_SIB = TRUE;
						if (tmpa1->scale == 1)
							cur.SIB = 0;
						else if (tmpa1->scale == 2)
							cur.SIB = 0x40;
						else if (tmpa1->scale == 4)
							cur.SIB = 0x80;
						else if (tmpa1->scale == 8)
							cur.SIB = 0xC0;
						if (tmpa1->reg2 == REG_NONE)
						{
							ModrM_complete = TRUE;
							cur.SIB |= (tmpa1->reg1 & 7) << 3 + 5;
							if (tmpa1->reg1 & 15 > 7)
								cur.REX |= 2;
							if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
							MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
							cur.disp.U8_count = 4;
						}
						else
						{
							cur.SIB |= (tmpa1->reg1 & 7) << 3 + tmpa1->reg2 & 7;
							if (tmpa1->reg1 & 15 > 7)
								cur.REX |= 2;
							if (tmpa1->reg1 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
							if (tmpa1->reg2 & 15 > 7)
								cur.REX |= 1;
							if (tmpa1->reg2 >= 20) //RBPu8, RSPu8, RSIu8, RDIu8?
								cur.has_REX = TRUE;
							if (tmpa1->reg2 & 7 == REG_RBP && !tmpa1->imm_or_off_present && tmpa1->indirect)
							{
								cur.ModrM |= 0x40;
								cur.disp.U8_count = 1;
								ModrM_complete = TRUE;
							}
						}
					}
					if (!ModrM_complete)
					{
						if (tmpa1->imm_or_off_present && tmpa1->indirect && tmpa1->reg1 == REG_NONE)
						{
							cur.ModrM = cur.ModrM & 0xF8 + 5;
							MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
							cur.disp.U8_count = 4;
							if (aotc->seg_size == 64)
								cur.disp.imm_flag = FALSE;
						}
						else
						{
							if (tmpa1->imm_or_off_present)
							{
								MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
								if (!cur.disp.num.machine_code && I8_MIN <= cur.disp.num.i <= I8_MAX)
								{
									cur.ModrM |= 0x40;
									cur.disp.U8_count = 1;
								}
								else if (aotc->seg_size == 16)
								{
									cur.ModrM |= 0x80;
									cur.disp.U8_count = 2;
								}
								else
								{
									cur.ModrM |= 0x80;
									cur.disp.U8_count = 4;
								}
							}
							else if (!tmpa1->indirect)
							{
								cur.has_addr_prefix = FALSE;
								cur.ModrM |= 0xC0;
							}
							else
							{
								if (tmpa1->reg1 & 7 == REG_RBP)
								{
									cur.ModrM |= 0x40;
									cur.disp.U8_count = 1;
								}
							}
						}
					}
				}
				else if (ARGT_MOFFS8 <= arg1 <= ARGT_MOFFS64)
				{
					MemCopy(&cur.disp.num, &tmpa1->num, sizeof(CAsmNum));
					if (aotc->seg_size == 16)
						cur.disp.U8_count = 2;
					else
						cur.disp.U8_count = 4;
					cur.has_addr_prefix = FALSE;
				}
				else if (ARGT_MOFFS8 <= arg2 <= ARGT_MOFFS64)
				{
					MemCopy(&cur.disp.num, &tmpa2->num, sizeof(CAsmNum));
					if (aotc->seg_size == 16)
						cur.disp.U8_count = 2;
					else
						cur.disp.U8_count = 4;
					cur.has_addr_prefix = FALSE;
				}
				else if (ARGT_IMM8  <= arg1 <= ARGT_IMM64 ||
						 ARGT_UIMM8 <= arg1 <= ARGT_UIMM64)
				{
					MemCopy(&cur.imm.num, &tmpa1->num, sizeof(CAsmNum));
					if (arg1 == ARGT_IMM8 || arg1 == ARGT_UIMM8)
						cur.imm.U8_count = 1;
					else if (arg1 == ARGT_IMM16 || arg1 == ARGT_UIMM16)
						cur.imm.U8_count = 2;
					else if (arg1 == ARGT_IMM32 || arg1 == ARGT_UIMM32)
						cur.imm.U8_count = 4;
					else
					{
						cur.imm.U8_count = 8;
						cur.REX |= 8;
					}
				}
				if (ARGT_IMM8 <= arg2 <= ARGT_IMM64 || ARGT_UIMM8 <= arg2 <= ARGT_UIMM64)
				{
					MemCopy(&cur.imm.num, &tmpa2->num, sizeof(CAsmNum));
					if (arg2 == ARGT_IMM8 || arg2 == ARGT_UIMM8)
						cur.imm.U8_count = 1;
					else if (arg2 == ARGT_IMM16 || arg2 == ARGT_UIMM16)
						cur.imm.U8_count = 2;
					else if (arg2 == ARGT_IMM32 || arg2 == ARGT_UIMM32)
					{
						cur.imm.U8_count = 4;
						if (tmpins->flags & IEF_REX_ONLY_R8_R15 && arg2 == ARGT_UIMM32)
							cur.REX &= ~8;
					}
					else
					{
						cur.imm.U8_count = 8;
						cur.REX |= 8;
					}
				}
			}
			cur.U8_count = tmpins->opcode_count + cur.disp.U8_count + cur.imm.U8_count;
			if (cur.has_ModrM)
				cur.U8_count++;
			if (cur.has_SIB)
				cur.U8_count++;
			if (aotc->seg_size == 64 && cur.REX & 0x40 == 0x40 && (cur.REX != 0x40 || cur.has_REX) &&
					(cur.REX & 7 || !(tmpins->flags & IEF_REX_ONLY_R8_R15 ||
					tmpins->flags & IEF_REX_XOR_LIKE && tmpa1->reg1 == tmpa2->reg1 && cur.ModrM & 0xC0 == 0xC0)))
				cur.U8_count++;
			if (cur.U8_count < best.U8_count && !(tmpins->flags & IEF_DONT_SWITCH_MODES &&
					(cur.has_addr_prefix || cur.has_operand_prefix)))
				MemCopy(&best, &cur, sizeof(CAsmIns));
		}
	}
	if (best.U8_count < 255)
	{
		tmpins = best.tmpins;
		seg = REG_NONE;
		if (argcount > 1 && aotc->arg2.seg != REG_NONE && !aotc->arg2.just_seg)
			seg = aotc->arg2.seg;
		else if (argcount > 0 && aotc->arg1.seg != REG_NONE && !aotc->arg1.just_seg)
			seg = aotc->arg1.seg;
		if (seg != REG_NONE)
			AOTStoreCodeU8(cc, asm_seg_prefixes[seg]);
		if (best.has_operand_prefix)
			AOTStoreCodeU8(cc, OC_OP_SIZE_PREFIX); //Operand size override
		if (best.has_addr_prefix || aotc->seg_size == 16 && cur.has_SIB)
			AOTStoreCodeU8(cc, OC_ADDR_SIZE_PREFIX); //Operand size override
		if (aotc->seg_size == 64 && best.REX & 0x40 == 0x40 && (best.REX != 0x40 || best.has_REX) &&
				(best.REX & 7 || !(tmpins->flags & IEF_REX_ONLY_R8_R15 ||
				tmpins->flags & IEF_REX_XOR_LIKE && tmpa1->reg1 == tmpa2->reg1 && best.ModrM & 0xC0 == 0xC0)))
			AOTStoreCodeU8(cc, best.REX);
		for (j = 0; j < tmpins->opcode_count - 1; j++)
			AOTStoreCodeU8(cc, tmpins->opcode[j]);
		AOTStoreCodeU8(cc, best.last_opcode_U8);

		if (best.has_ModrM)
			AOTStoreCodeU8(cc, best.ModrM);
		if (best.has_SIB)
			AOTStoreCodeU8(cc, best.SIB);

		if (best.disp.U8_count)
		{
			best.disp.rel = aotc->rip + best.disp.U8_count + best.imm.U8_count;
			if (!AsmStoreNum(cc, &best.disp, 1, U8_avail))
				return FALSE;
		}

		if (best.imm.U8_count)
		{
			best.imm.rel = aotc->rip + best.imm.U8_count;
			if (!AsmStoreNum(cc, &best.imm, 1, U8_avail))
				return FALSE;
		}
		if (tmpins->flags & IEF_ENDING_ZERO) //ENTER inst
			AOTStoreCodeU8(cc, 0);
		return TRUE;
	}
	LexExcept(cc, "Invalid inst at ");
}

U0 ParseAsmDefine(CCompCtrl *cc, I64 U8_count)
{
	Bool		 is_dup;
	I64			 i, dup_val;
	U8			*ptr;
	CAsmNum2	 num2;

	num2.U8_count = U8_count;

	while (cc->token && cc->token != ';')
	{
		num2.num.local_asm_undef_hash = NULL;
		num2.num.global_asm_undef_hash = NULL;
		if (cc->token == TK_STR)
		{
			ptr = cc->cur_str;
			i = cc->cur_str_len - 1;
			while (i--)
				AOTStoreCodeU8(cc, *ptr++);
			Lex(cc);	//Skip Str
		}
		else
		{
			is_dup = FALSE;
			cc->abs_counts = 0;
			cc->asm_undef_hash = NULL;
			cc->flags &= ~(CCF_UNRESOLVED + CCF_LOCAL);
			if (!IsLexExpression2Bin(cc, &num2.num.machine_code))
				LexSkipEol(cc);
			else
			{
				if (cc->abs_counts.externs)
					LexExcept(cc, "Extern Not Allowed at ");
				if (cc->flags & CCF_UNRESOLVED)
				{
					if (cc->flags & CCF_LOCAL)
					{
						num2.num.local_asm_undef_hash = cc->asm_undef_hash;
						cc->asm_undef_hash = NULL;
					}
					else
					{
						num2.num.global_asm_undef_hash = cc->asm_undef_hash;
						cc->asm_undef_hash = NULL;
					}
				}
				else
				{
					i = Call(num2.num.machine_code);
					Free(num2.num.machine_code);
				}
			}
			if (cc->token == TK_IDENT && cc->hash_entry)
			{
				if (cc->hash_entry->type & HTT_ASM_KEYWORD && cc->hash_entry->user_data0 == AKW_DUP)
				{
					is_dup = TRUE;
					if (Lex(cc) != '(')
						LexExcept(cc, "Expecting '(' at ");
					Lex(cc); //skip (
					dup_val = AsmLexExpression(cc);
					if (cc->token != ')')
						LexExcept(cc, "Expecting ')' at ");
					Lex(cc); //SKIP )
				}
			}
			num2.rel = 0;
			num2.imm_flag = TRUE;
			num2.num.abs_counts = cc->abs_counts;
			if (is_dup)
			{
				if (num2.num.local_asm_undef_hash || num2.num.global_asm_undef_hash)
					LexExcept(cc, "Undefined DUP count at ");
				num2.num.i = dup_val;
				AsmStoreNum(cc, &num2, i, FALSE);
			}
			else
			{
				num2.num.i = i;
				AsmStoreNum(cc, &num2, 1, FALSE);
			}
		}
		if (cc->token == ',')
			Lex(cc);
	}
	if (cc->token != ';')
		LexExcept(cc, "Missing ';' at");
	Lex(cc);
}

U0 ParseBinFile(CCompCtrl *cc)
{
	I64 i, size;
	U8 *buf, *st;

	if (cc->token != TK_STR)
		LexExcept(cc, "Expecting string at ");
	st = ExtDefault(cc->cur_str, "BIN");
	buf = FileRead(st, &size);
	Free(st);
	for (i = 0; i < size; i++)
		AOTStoreCodeU8(cc, buf[i]);
	if (Lex(cc) != ';')
		LexExcept(cc, "Missing ';' at");
	Lex(cc);
}

U0 ParseAsmBlk(CCompCtrl *cc,I64 comp_flags)
{
	CAOTCtrl *aotc = cc->aotc;
	I64 i, j, k, argcount, old_flags = cc->flags & CCF_ASM_EXPRESSIONS;
	CHashOpcode *tmpo;
	CHashExport *tmpex;
	U8 *next_last_label;
	CCodeMisc *g_lb;

	aotc->seg_size = 64;
	cc->flags |= CCF_ASM_EXPRESSIONS;
	if (!(comp_flags & CMPF_ONE_ASM_INS))
	{
		if (cc->token != '{')
			LexExcept(cc, "Expecting '{' at ");
		Lex(cc);
	}
	while (cc->token && cc->token != '}')
	{
		AsmLineList(cc);
		if (cc->token == TK_IDENT && cc->hash_entry)
		{
			if (cc->hash_entry->type & HTT_ASM_KEYWORD)
			{
				i = cc->hash_entry->user_data0;
				Lex(cc); //skip keyword
				switch (i)
				{
					case AKW_IMPORT:
						while (cc->token && cc->token != ';')
						{
							if (cc->token != TK_IDENT)
								LexExcept(cc, "Expecting identifier at ");
							else
							{
								tmpex = NULL;
								tmpex = CAlloc(sizeof(CHashExport));
								tmpex->str = cc->cur_str;
								cc->cur_str = 0;
								tmpex->type = HTT_EXPORT_SYS_SYM | HTF_UNRESOLVED;
								HashAdd(tmpex, cc->htc.global_hash_table);
								tmpex->type |= HTF_IMPORT;
								if (Lex(cc) == ',')
									Lex(cc); //skip ','
							}
						}
						if (cc->token != ';')
							LexExcept(cc, "Missing ';' at");
						Lex(cc); //skip ';';
						break;

					case AKW_ORG:
						if (cc->htc.local_var_list)
							LexExcept(cc, "ORG not allowed in fun asm blk ");
						if (aotc->org != INVALID_PTR)
							LexExcept(cc, "Just one org allowed ");
						if (aotc->rip)
							LexExcept(cc, "ORG must be at beginning ");
						aotc->org = AsmLexExpression(cc);
						break;

					case AKW_ALIGN:
						if (cc->htc.local_var_list)
							LexExcept(cc, "ALIGN not allowed in fun asm blk ");
						i = AsmLexExpression(cc);
						j = Bsf(i);
						if (!i || j != Bsr(i))
							LexExcept(cc, "ALIGN must be power of two at ");
						if (!(cc->flags & CCF_AOT_COMPILE) && i > 8)
							LexExcept(cc, "In JIT mode, max ALIGN is 8 ");
						if (j > aotc->max_align_bits)
							aotc->max_align_bits = j;
						i = CeilU64(aotc->rip, i);
						if (cc->token != ',')
							LexExcept(cc, "Expecting ',' at ");
						Lex(cc);
						k = AsmLexExpression(cc);
						for (j = aotc->rip; j < i; j++)
							AOTStoreCodeU8(cc, k);
						break;

					case AKW_DU8:
						ParseAsmDefine(cc, 1);
						break;

					case AKW_DU16:
						ParseAsmDefine(cc, 2);
						break;

					case AKW_DU32:
						ParseAsmDefine(cc, 4);
						break;

					case AKW_DU64:
						ParseAsmDefine(cc, 8);
						break;

					case AKW_BINFILE:
						ParseBinFile(cc);
						break;

					case AKW_LIST:
						aotc->list = TRUE;
						break;

					case AKW_NOLIST:
						aotc->list = FALSE;
						break;

					case AKW_USE16:
						aotc->seg_size = 16;
						break;

					case AKW_USE32:
						aotc->seg_size = 32;
						break;

					case AKW_USE64:
						aotc->seg_size = 64;
						break;

					default:
						LexExcept(cc, "Syntax error at ");
				}
			}
			else if (cc->hash_entry->type & HTT_OPCODE)
			{
				tmpo = cc->hash_entry;
				Lex(cc); //skip opcode
				argcount = 0;
				if (tmpo->ins[0].arg1)
				{
					argcount++;
					if (ARGT_REL8 <= tmpo->ins[0].arg1 <= ARGT_REL32)
						ParseAsmArg(cc, &aotc->arg1, TRUE);
					else
						ParseAsmArg(cc, &aotc->arg1, FALSE);
					if (tmpo->ins[0].arg2)
					{
						argcount++;
						if (cc->token != ',')
							LexExcept(cc, "Expecting ',' at ");
						else
						{
							Lex(cc); //skip ','
							if (ARGT_REL8 <= tmpo->ins[0].arg2 <= ARGT_REL32)
								ParseAsmArg(cc, &aotc->arg2, TRUE);
							else
								ParseAsmArg(cc, &aotc->arg2, FALSE);
						}
					}
				}
				ParseAsmInst(cc, tmpo,argcount);
			}
			else if (cc->hash_entry->type & HTT_EXPORT_SYS_SYM)
			{
				if (Btr(&cc->hash_entry->type, HTf_UNRESOLVED))
				{
					if (cc->hash_entry->type & HTF_LOCAL)
					{
						cc->hash_entry(CHashExport *)->val = aotc->rip;
						if (Lex(cc) != ':')
							LexExcept(cc, "Expecting ':' at ");
						Lex(cc);
					}
					else
					{
						if (cc->hash_entry->type & HTF_IMPORT)
							LexExcept(cc, "attempt to define import at ");
						cc->hash_entry(CHashExport *)->val = aotc->rip;
						next_last_label = cc->hash_entry->str;
						Lex(cc); //Skip cur_str
						if (cc->token != ':' && cc->token != TK_DBL_COLON)
							LexExcept(cc, "Expecting ':' at ");
						if (cc->token == TK_DBL_COLON)
						{
							cc->hash_entry->type |= HTF_EXPORT;
							HashSrcFileSet(cc, cc->hash_entry);

							AOTLocalsResolve(cc);
							aotc->last_label = next_last_label;
						}
						Lex(cc);
					}
				}
				else if (cc->hash_entry(CHashExport *)->val == aotc->rip)
				{
					Lex(cc); //Skip cur_str
					if (cc->token != ':' && cc->token != TK_DBL_COLON)
						LexExcept(cc, "Expecting ':' at ");
					Lex(cc);
				}
				else
					LexExcept(cc, "Redefinition at ");
			}
			else
				LexExcept(cc, "Syntax error at ");
		}
		else if (cc->token == TK_IDENT)
		{
			tmpex = CAlloc(sizeof(CHashExport));
			tmpex->str = cc->cur_str;
			cc->cur_str = 0;
			tmpex->type = HTT_EXPORT_SYS_SYM;
			tmpex->val = aotc->rip;
			Lex(cc);	//Skip cur_str
			if (cc->token != ':' && cc->token != TK_DBL_COLON)
				LexExcept(cc, "Expecting ':' at ");
			else
			{
				if (*tmpex->str == '@' && tmpex->str[1] == '@')
				{
					if (cc->token == TK_DBL_COLON)
						LexExcept(cc, "No local global exports at ");
					HashAdd(tmpex, cc->htc.local_hash_table);
				}
				else
					HashAdd(tmpex,cc->htc.global_hash_table);
				if (cc->htc.local_var_list)
				{//AsmBlk in fun? Also add goto-like label.
					if (!(g_lb = COCGoToLabelFind(cc, tmpex->str)))
					{
						g_lb = COCMiscNew(cc, CMT_ASM_LABEL);
						g_lb->str = StrNew(tmpex->str);
					}
					else if (g_lb->flags & CMF_DEFINED)
						LexExcept(cc, "Duplicate goto label at ");
					g_lb->type = CMT_ASM_LABEL;
					g_lb->flags |= CMF_DEFINED;
					g_lb->rip = aotc->rip;
					g_lb->use_count++; //Disable warning on unused labels.
					ICAdd(cc, IC_LABEL, g_lb, 0);
				}
				if (cc->token == TK_DBL_COLON)
				{
					tmpex->type |= HTF_EXPORT;
					HashSrcFileSet(cc, tmpex);

					AOTLocalsResolve(cc);
					aotc->last_label = tmpex->str;
				}
				Lex(cc);
			}
		}
		else if (cc->token == ';')
			Lex(cc);
		else
			LexExcept(cc, "Syntax error at ");
		if (comp_flags & CMPF_ONE_ASM_INS && (cc->token != TK_IDENT || !(tmpo=cc->hash_entry) ||
				!(tmpo->type & (HTT_OPCODE | HTT_ASM_KEYWORD))))
			break;
	}
	AOTLocalsResolve(cc);
	aotc->list = FALSE;
	cc->flags &= cc->flags & ~CCF_ASM_EXPRESSIONS | old_flags;
}
