'' code generation for x86, GNU assembler (GAS/Intel arch)
''
'' chng: sep/2004 written [v1ctor]
''  	 mar/2005 longint support added [v1ctor]
''  	 may/2008 SSE/SSE2 instructions [Bryan Stoeberl]
''       may/2008 boolean support added [jeffm]

#include once "fb.bi"
#include once "fbint.bi"
#include once "reg.bi"
#include once "ir.bi"
#include once "rtl.bi"
#include once "emit.bi"
#include once "emitdbg.bi"
#include once "hash.bi"
#include once "symb.bi"
#include once "emit-private.bi"
#include once "ir-private.bi"

declare sub hDeclVariable _
	( _
		byval s as FBSYMBOL ptr _
	)

declare function _getSectionString _
	( _
		byval section as integer, _
		byval priority as integer _
	) as const zstring ptr

declare sub _setSection _
	( _
		byval section as integer, _
		byval priority as integer _
	)

declare function _getTypeString( byval dtype as integer ) as const zstring ptr

''globals

	'' same order as EMITREG_ENUM
	dim shared rnameTB(0 to EMIT_MAXRTABLES-1, 0 to EMIT_MAXRNAMES-1) as zstring * 7+1 => _
	{ _
		{    "dl",   "di",   "si",   "cl",   "bl",   "al",   "bp",   "sp" }, _
		{    "dx",   "di",   "si",   "cx",   "bx",   "ax",   "bp",   "sp" }, _
		{   "edx",  "edi",  "esi",  "ecx",  "ebx",  "eax",  "ebp",  "esp" }, _
		{ "st(0)","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)" } _
	}

	'' same order as FB_DATATYPE
	dim shared dtypeTB(0 to FB_DATATYPES-1) as EMITDATATYPE => _
	{ _
		( 0, "void ptr"  ), _ '' void
		( 0, "byte ptr"  ), _ '' boolean
		( 0, "byte ptr"  ), _ '' byte
		( 0, "byte ptr"  ), _ '' ubyte
		( 0, "byte ptr"  ), _ '' char
		( 1, "word ptr"  ), _ '' short
		( 1, "word ptr"  ), _ '' ushort
		( 1, "word ptr"  ), _ '' wchar
		( 2, "dword ptr" ), _ '' int
		( 2, "dword ptr" ), _ '' uint
		( 2, "dword ptr" ), _ '' enum
		( 2, "dword ptr" ), _ '' long
		( 2, "dword ptr" ), _ '' ulong
		( 2, "qword ptr" ), _ '' longint
		( 2, "qword ptr" ), _ '' ulongint
		( 3, "dword ptr" ), _ '' single
		( 3, "qword ptr" ), _ '' double
		( 0, ""          ), _ '' string
		( 0, "byte ptr"  ), _ '' fix-len string
		( 2, "dword ptr" ), _ '' struct
		( 0, ""          ), _ '' namespace
		( 2, "dword ptr" ), _ '' function
		( 0, "byte ptr"  ), _ '' fwd-ref
		( 2, "dword ptr" ), _ '' pointer
		( 3, "xmmword ptr" ) _ '' 128-bit
	}

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' helper functions
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

#if __FB_DEBUG__
function emitDumpRegName( byval dtype as integer, byval reg as integer ) as string
	function = *hGetRegName( dtype, reg )
end function
#endif

'':::::
function hIsRegFree _
	( _
		byval dclass as integer, _
		byval reg as integer _
	) as integer static

	'' if EBX, EDI or ESI and if they weren't ever used, return false,
	'' because hCreateFrame didn't preserve them
	if( dclass = FB_DATACLASS_INTEGER ) then
		select case reg
		case EMIT_REG_EBX, EMIT_REG_ESI, EMIT_REG_EDI
			if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, reg ) = FALSE ) then
				return FALSE
			end if
		end select
	end if

	assert( dclass < EMIT_REGCLASSES )

	'' assume it will be trashed
	EMIT_REGSETUSED( dclass, reg )

	function = REG_ISFREE( emit.curnode->regFreeTB(dclass), reg )

end function

'' This will always find a reg that is not used by the given vreg,
'' because a single vreg can only use 2 regs at most (longints),
'' and x86 has more regs than that.
'' Free regs are preferred; however, if all regs are used, the returned reg
'' won't be free.
function hFindRegNotInVreg _
	( _
		byval vreg as IRVREG ptr, _
		byval noSIDI as integer = FALSE _
	) as integer

	dim as integer r = any, reg = any, reg2 = any, regs = any

	function = INVALID '' shouldn't ever happen, getMaxRegs() should be > 0

	reg = INVALID

	select case vreg->typ
	case IR_VREGTYPE_REG
		reg = vreg->reg
	case IR_VREGTYPE_IDX, IR_VREGTYPE_PTR
		if( vreg->vidx <> NULL ) then
			if( vreg->vidx->typ = IR_VREGTYPE_REG ) then
				reg = vreg->vidx->reg
			end if
		end if
	end select

	'' longint..
	reg2 = INVALID
	if( vreg->vaux <> NULL ) then
		if( vreg->vaux->typ = IR_VREGTYPE_REG ) then
			reg2 = vreg->vaux->reg
		end if
	end if

	regs = emit.regTB(FB_DATACLASS_INTEGER)->getMaxRegs( emit.regTB(FB_DATACLASS_INTEGER) )

	if( reg2 = INVALID ) then
		if( noSIDI = FALSE ) then
			for r as integer = regs-1 to 0 step -1
				if( r <> reg ) then
					function = r
					if( hIsRegFree( FB_DATACLASS_INTEGER, r ) ) then
						exit for
					end if
				end if
			next
		'' SI/DI as byte..
		else
			for r as integer = regs-1 to 0 step -1
				if( r <> reg ) then
					if( r <> EMIT_REG_ESI ) then
						if( r <> EMIT_REG_EDI ) then
							function = r
							if( hIsRegFree( FB_DATACLASS_INTEGER, r ) ) then
								exit for
							end if
						end if
					end if
				end if
			next
		end if
	'' longints..
	else
		if( noSIDI = FALSE ) then
			for r as integer = regs-1 to 0 step -1
				if( (r <> reg) and (r <> reg2) ) then
					function = r
					if( hIsRegFree( FB_DATACLASS_INTEGER, r ) ) then
						exit for
					end if
				end if
			next
		'' SI/DI as byte..
		else
			for r = regs-1 to 0 step -1
				if( (r <> reg) and (r <> reg2) ) then
					if( r <> EMIT_REG_ESI ) then
						if( r <> EMIT_REG_EDI ) then
							function = r
							if( hIsRegFree( FB_DATACLASS_INTEGER, r ) ) then
								exit for
							end if
						end if
					end if
				end if
			next
		end if
	end if

end function

'' Returns a free reg or INVALID if there are no free regs
function hFindFreeReg( byval dclass as integer ) as integer
	function = INVALID

	for r as integer = emit.regTB(dclass)->getMaxRegs( emit.regTB(dclass) )-1 to 0 step -1
		if( hIsRegFree( dclass, r ) ) then
			return r
		end if
	next
end function

'':::::
function hIsRegInVreg _
	( _
		byval vreg as IRVREG ptr, _
		byval reg as integer _
	) as integer static

	function = FALSE

	select case vreg->typ
	case IR_VREGTYPE_REG
		if( vreg->reg = reg ) then
			return TRUE
		end if

	case IR_VREGTYPE_IDX, IR_VREGTYPE_PTR
		if( vreg->vidx <> NULL ) then
			if( vreg->vidx->typ = IR_VREGTYPE_REG ) then
				if( vreg->vidx->reg = reg ) then
					return TRUE
				end if
			end if
		end if
	end select

	'' longints..
	if( vreg->vaux <> NULL ) then
		if( vreg->vaux->typ = IR_VREGTYPE_REG ) then
			if( vreg->vaux->reg = reg ) then
				return TRUE
			end if
		end if
	end if

end function

'':::::
function hGetRegName _
	( _
		byval dtype as integer, _
		byval reg as integer _
	) as zstring ptr

	if( reg = INVALID ) then
		function = NULL
	else
		dim as integer tb = dtypeTB(typeGet( dtype )).rnametb

		function = @rnameTB(tb, reg)
	end if

end function

'':::::
private function hGetIdxName _
	( _
		byval vreg as IRVREG ptr _
	) as zstring ptr static

    static as zstring * FB_MAXINTNAMELEN+1+8+1+1+1+8+1 iname
    dim as FBSYMBOL ptr sym
    dim as IRVREG ptr vi
    dim as integer addone, mult
    dim as zstring ptr rname

	sym = vreg->sym
	vi  = vreg->vidx

	if( sym = NULL ) then
		'' no var or index?
		if( vi = NULL ) then
			return NULL
		end if

		iname = ""

	else
		iname = *symbGetMangledName( sym )
		if( vi <> NULL ) then
			iname += "+"
		end if
	end if

	rname = hGetRegName( vi->dtype, vi->reg )

	iname += *rname

    if( vi <> NULL ) then
    	mult = vreg->mult
		''
		'' For x86 ASM, a multiplier/scaling factor can be given right
		'' as part of address/indexing expression. It can be a power
		'' of two in the range 1..8, i.e. one of {1, 2, 4, 8}.
		''
		''  For example, assuming a variable at ebp-N, holding a valid array index,
		''  and the corresponding dword array at ebp-M:
		''
		''      mov eax, dword ptr [ebp-N]       ; Load array index variable from stack
		''      mov dword ptr [ebp+eax*4-M], 0   ; Store 0 into element eax of the dword array
		''
		''  instead of:
		''
		''      mov eax, dword ptr [ebp-N]
		''      imul eax, 4
		''      mov dword ptr [ebp+eax-M], 0
		''
		'' We can support {3, 5, 9} multipliers by emitting them as
		'' *2+1, *4+1, *8+1 respectively (with addone = TRUE).
		''
		'' 6 and 7 cannot be supported.
		''
		'' Besides that, since the "addone" form uses up the "offset"
		'' part from the [base + (index*mult) + offset] form, it can
		'' only be used if the offset isn't needed in combination with
		'' the base. This means the "addone" form cannot be used with
		'' things from stack (ebp-N), but only globals.
		''

		assert( (mult >= 1) and (mult <= 9) )
		assert( (mult <> 6) and (mult <> 7) )

		if( mult > 1 ) then
			addone = FALSE
			select case mult
			case 3, 5, 9
				mult -= 1
				addone = TRUE
			end select

			iname += "*"
			iname += str( mult )

			if( addone ) then
				assert( vreg->ofs = 0 )
				iname += "+"
				iname += *rname
			end if
		end if
	end if

	function = @iname

end function

'':::::
sub hPrepOperand _
	( _
		byval vreg as IRVREG ptr, _
		byref operand as string, _
		byval dtype as FB_DATATYPE = FB_DATATYPE_INVALID, _
		byval ofs as integer = 0, _
		byval isaux as integer = FALSE, _
		byval addprefix as integer = TRUE _
	) static

    if( vreg = NULL ) then
    	operand = ""
    	exit sub
    end if

    if( dtype = FB_DATATYPE_INVALID ) then
    	dtype = vreg->dtype
    end if

	select case as const vreg->typ
	case IR_VREGTYPE_VAR, IR_VREGTYPE_IDX, IR_VREGTYPE_PTR

		'' prefix
		if( addprefix ) then
			operand = dtypeTB(dtype).mname
			operand += " ["
		else
			operand = "["
		end if

		'' base + (index*mult) + offset

		'' variable or index
		dim as zstring ptr idx_op
		if( vreg->typ = IR_VREGTYPE_VAR ) then
			idx_op = symbGetMangledName( vreg->sym )
		else
        	idx_op = hGetIdxName( vreg )
		end if

        if( idx_op <> NULL ) then
        	operand += *idx_op
        end if

		'' offset
		ofs += vreg->ofs
		if( isaux ) then
			ofs += 4
		end if

		if( ofs > 0 ) then
			if( idx_op <> NULL ) then
				operand += "+"
			end if
			operand += str( ofs )

		elseif( ofs < 0 ) then
			operand += str( ofs )

		else
			if( idx_op = NULL ) then
				operand += "0"
			end if
		end if

		operand += "]"

	case IR_VREGTYPE_OFS
		operand = "offset "
		operand += *symbGetMangledName( vreg->sym )
		if( vreg->ofs <> 0 ) then
			operand += " + "
			operand += str( vreg->ofs )
		end if

	case IR_VREGTYPE_REG
		if( isaux = FALSE ) then
			operand = *hGetRegName( dtype, vreg->reg )
		else
			operand = *hGetRegName( dtype, vreg->vaux->reg )
		end if

	case IR_VREGTYPE_IMM
		dim i as longint
		if( isaux = FALSE ) then
			i = vreg->value.i
		else
			i = vreg->vaux->value.i
		end if

		if( dtype = FB_DATATYPE_BOOLEAN ) then
			'' Currently the compiler stores boolean constant values as 0/-1 internally,
			'' in the FBVALUE.i field which is a longint (i.e. an int, not a bool),
			'' so we have to convert to 0/1 manually here.
			if( i ) then
				operand = "1"
			else
				operand = "0"
			end if
		else
			operand = str( i )
		end if

	case else
    	operand = ""
	end select

end sub

'':::::
sub hPrepOperand64 _
	( _
		byval vreg as IRVREG ptr, _
		byref operand1 as string, _
		byref operand2 as string _
	) static

	hPrepOperand( vreg, operand1, FB_DATATYPE_UINT   , 0, FALSE )
	hPrepOperand( vreg, operand2, FB_DATATYPE_INTEGER, 0, TRUE )

end sub

'':::::
private sub outEx _
	( _
		byval s as zstring ptr, _
		byval bytes as integer = 0 _
	) static

	if( bytes = 0 ) then
		bytes = len( *s )
	end if

	if( put( #env.outf.num, , *s, bytes ) = 0 ) then
	end if

end sub

'':::::
sub outp _
	( _
		byval s as zstring ptr _
	)

    static as string ostr

	if( env.clopt.debug ) then
		ostr = TABCHAR
		ostr += *s
	else
		ostr = *s
	end if

	ostr += NEWLINE

	outEX( ostr, len( ostr ) )

end sub

'':::::
sub hBRANCH _
	( _
		byval mnemonic as zstring ptr, _
		byval label as zstring ptr _
	) static

    dim ostr as string

	ostr = *mnemonic
	ostr += " "
	ostr += *label
	outp( ostr )

end sub

'':::::
sub hPUSH _
	( _
		byval rname as zstring ptr _
	) static

    dim ostr as string

	ostr = "push "
	ostr += *rname
	outp( ostr )

end sub

'':::::
sub hPOP _
	( _
		byval rname as zstring ptr _
	) static

    dim ostr as string

    ostr = "pop "
    ostr += *rname
	outp( ostr )

end sub

'':::::
sub hMOV _
	( _
		byval dname as zstring ptr, _
		byval sname as zstring ptr _
	) static

    dim ostr as string

	ostr = "mov "
	ostr += *dname
	ostr += ", "
	ostr += *sname
	outp( ostr )

end sub

'':::::
private sub hXCHG _
	( _
		byval dname as zstring ptr, _
		byval sname as zstring ptr _
	) static

    dim ostr as string

	ostr = "xchg "
	ostr += *dname
	ostr += ", "
	ostr += *sname
	outp( ostr )

end sub

'':::::
private sub hCOMMENT _
	( _
		byval s as zstring ptr _
	) static

    dim ostr as string

    ostr = TABCHAR + "#"
    ostr += *s
    ostr += NEWLINE
	outEX( ostr )

end sub

'':::::
private sub hPUBLIC _
	( _
		byval label as zstring ptr, _
		byval isexport as integer _
	) static

    dim ostr as string

	ostr = NEWLINE + ".globl "
	ostr += *label

' PENDING: shared lib compatibility between win32/linux
'          rtlib/gfxlib needs -fvisibility=hidden, only available in gcc 4
'	if( env.clopt.target = FB_COMPTARGET_LINUX ) then
'		if( isexport ) then
'			ostr += NEWLINE + ".protected "
'			ostr += *label
'		else
'			ostr += NEWLINE + ".hidden "
'			ostr += *label
'		end if
'	end if

	ostr += NEWLINE
	outEx( ostr )

end sub

'':::::
sub hLABEL _
	( _
		byval label as zstring ptr _
	) static

    dim ostr as string

	ostr = *label
	ostr += ":" + NEWLINE
	outEx( ostr )

end sub

'':::::
private sub hALIGN _
	( _
		byval bytes as integer _
	) static

    dim ostr as string

    ostr = ".balign " + str( bytes ) + NEWLINE
	outEx( ostr )

end sub

'':::::
private sub hInitRegTB
	dim as integer lastclass, regs, i

	'' ebp and esp are reserved
	const int_regs = 6

	static as REG_SIZEMASK int_bitsmask(0 to int_regs-1) = _
	{ _
		REG_SIZEMASK_8 or REG_SIZEMASK_16 or REG_SIZEMASK_32, _		'' edx
						  REG_SIZEMASK_16 or REG_SIZEMASK_32, _		'' edi
						  REG_SIZEMASK_16 or REG_SIZEMASK_32, _		'' esi
		REG_SIZEMASK_8 or REG_SIZEMASK_16 or REG_SIZEMASK_32, _		'' ecx
		REG_SIZEMASK_8 or REG_SIZEMASK_16 or REG_SIZEMASK_32, _		'' ebx
		REG_SIZEMASK_8 or REG_SIZEMASK_16 or REG_SIZEMASK_32 _		'' eax
	}

	emit.regTB(FB_DATACLASS_INTEGER) = _
		regNewClass( FB_DATACLASS_INTEGER, _
					 int_regs, _
					 int_bitsmask( ), _
					 FALSE )

	'' no st(7) as STORE/LOAD/POW/.. need a free reg to work
	const flt_regs = 7

	static as REG_SIZEMASK flt_bitsmask(0 to flt_regs-1) = _
	{ _
		REG_SIZEMASK_32 or REG_SIZEMASK_64, _						'' st(0)
		REG_SIZEMASK_32 or REG_SIZEMASK_64, _						'' st(1)
		REG_SIZEMASK_32 or REG_SIZEMASK_64, _						'' st(2)
		REG_SIZEMASK_32 or REG_SIZEMASK_64, _						'' st(3)
		REG_SIZEMASK_32 or REG_SIZEMASK_64, _						'' st(4)
		REG_SIZEMASK_32 or REG_SIZEMASK_64, _						'' st(5)
		REG_SIZEMASK_32 or REG_SIZEMASK_64 _						'' st(6)
	}

	'' create non-stacked floating-point registers
	if( env.clopt.fputype = FB_FPUTYPE_SSE ) then
		emit.regTB(FB_DATACLASS_FPOINT) = _
			regNewClass( FB_DATACLASS_FPOINT, _
						 flt_regs, _
						 flt_bitsmask( ), _
						 FALSE )

		'' change floating-point register names to SSE registers
		for i = 0 to EMIT_MAXRNAMES - 1
			rnameTB(EMIT_MAXRTABLES-1, i) = "xmm" + Str(i)
		next i
	else
		emit.regTB(FB_DATACLASS_FPOINT) = _
			regNewClass( FB_DATACLASS_FPOINT, _
						 flt_regs, _
						 flt_bitsmask( ), _
						 TRUE )
	end if

end sub

'':::::
private sub hEndRegTB
    dim i as integer

	for i = 0 to EMIT_REGCLASSES-1
		regDelClass( emit.regTB(i) )
	next

end sub

private function hGetGlobalTypeAlign( byval dtype as integer ) as integer
	if( dtype = FB_DATATYPE_DOUBLE ) then
		function = 8
	else
		function = 4
	end if
end function

private function hGetGlobalVarAlign( byval sym as FBSYMBOL ptr ) as integer
	if( symbIsRef( sym ) ) then
		function = 4
	else
		function = hGetGlobalTypeAlign( symbGetType( sym ) )
	end if
end function

'':::::
private sub hEmitVarBss _
	( _
		byval s as FBSYMBOL ptr _
	) static

    dim as string alloc, ostr
	dim as integer attrib

	assert( symbIsExtern( s ) = FALSE )
	assert( symbIsDynamic( s ) = FALSE )

	attrib = symbGetAttrib( s )

	_setSection( IR_SECTION_BSS, 0 )

    '' allocation modifier
    if( (attrib and FB_SYMBATTRIB_COMMON) = 0 ) then
      	if( (attrib and FB_SYMBATTRIB_PUBLIC) > 0 ) then
       		hPUBLIC( *symbGetMangledName( s ), TRUE )
		end if
       	alloc = ".lcomm"
	else
       	hPUBLIC( *symbGetMangledName( s ), FALSE )
       	alloc = ".comm"
    end if

	hALIGN( hGetGlobalVarAlign( s ) )

	'' emit
    ostr = alloc + TABCHAR
    ostr += *symbGetMangledName( s )
	ostr += "," + str( symbGetRealSize( s ) )
    emitWriteStr( ostr, TRUE )

	'' Add debug info for public/shared globals, but not local statics
	edbgEmitGlobalVar( s, IR_SECTION_BSS )

end sub

'':::::
private sub hWriteHeader( ) static

	''
	edbgEmitHeader( env.inf.name )

	''
	emitWriteStr( ".intel_syntax noprefix", TRUE )

end sub

'':::::
private sub hEmitVarConst _
	( _
		byval s as FBSYMBOL ptr _
	) static

	dim as string stext, stype, ostr
	dim as integer dtype

	dtype = symbGetType( s )

	select case as const dtype
	case FB_DATATYPE_CHAR
		stext = QUOTE
		stext += *hEscape( symbGetVarLitText( s ) )
		stext += RSLASH + "0" + QUOTE

	case FB_DATATYPE_WCHAR
		stext = QUOTE
		stext += *hEscapeW( symbGetVarLitTextW( s ) )
		for i as integer = 1 to typeGetSize( FB_DATATYPE_WCHAR )
			stext += RSLASH + "0"
		next
		stext += QUOTE

	case else
		stext = *symbGetVarLitText( s )
	end select

	''
	'' !!!FIXME!!!
	''
	'' Linux appears to support .rodata section, but I'm not sure about other platforms, and that's
	'' probably the reason FB used to output a normal .data section in any case...
	''
	if( env.clopt.target = FB_COMPTARGET_LINUX ) then
		_setSection( IR_SECTION_CONST, 0 )
	else
		_setSection( IR_SECTION_DATA, 0 )
	end if

	'' some SSE instructions require operands to be 16-byte aligned
	if( s->var_.align ) then
		hALIGN ( s->var_.align )
	else
		hALIGN( hGetGlobalTypeAlign( dtype ) )
	end if


	stype = *_getTypeString( dtype )
	ostr = *symbGetMangledName( s )
	ostr += (":" + TABCHAR) + stype + TABCHAR + stext
	emitWriteStr( ostr )

end sub

'':::::
private sub hWriteCtor _
	( _
		byval proc_head as FB_GLOBCTORLIST_ITEM ptr, _
		byval is_ctor as integer _
	)

    if( proc_head = NULL ) then
    	exit sub
    end if

    do
    	'' was it emitted?
    	if( symbGetProcIsEmitted( proc_head->sym ) ) then
    		_setSection( iif( is_ctor, _
    						  IR_SECTION_CONSTRUCTOR, _
    						  IR_SECTION_DESTRUCTOR ), _
    					 symbGetProcPriority( proc_head->sym ) )
    		emitVARINIOFS( symbGetMangledName( proc_head->sym ), 0 )
    	end if

    	proc_head = proc_head->next
    loop while( proc_head <> NULL )

end sub

private sub hEmitExport( byval s as FBSYMBOL ptr )
    if( symbIsExport( s ) ) then
        _setSection( IR_SECTION_DIRECTIVE, 0 )

        dim as zstring ptr sname = symbGetMangledName( s )
        if( env.underscoreprefix ) then
            sname += 1
        end if

        emitWriteStr( ".ascii " + QUOTE + " -export:" + _
                      *sname + (QUOTE + NEWLINE), _
                      TRUE )
    end if
end sub

'':::::
private sub hDeclVariable _
	( _
		byval s as FBSYMBOL ptr _
	) static

	'' literal?
	if( symbGetIsLiteral( s ) ) then
		select case symbGetType( s )
		'' string? check if ever referenced
		case FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
			if( symbGetIsAccessed( s ) = FALSE ) then
				return
			end if

		end select

		hEmitVarConst( s )
		return
	end if

	'' Don't emit the fake jump-table vars, see also astBuildJMPTB()
	if( symbGetIsJumpTb( s ) ) then
		return
	end if

	'' Don't emit EXTERNs (only PUBLICs will be allocated here) or fake
	'' dynamic array symbols (their descriptor will be emitted instead)
	if( symbIsExtern( s ) or symbIsDynamic( s ) ) then
		return
	end if

	'' initialized?
	if( symbGetTypeIniTree( s ) ) then
    	'' never referenced?
    	if( symbGetIsAccessed( s ) = FALSE ) then
			'' not public?
    	    if( symbIsPublic( s ) = FALSE ) then
    	    	return
    	    end if
		end if

		_setSection( IR_SECTION_DATA, 0 )
		irhlFlushStaticInitializer( s )
		return
	end if

	hEmitVarBss( s )

end sub

'':::::
private sub hClearLocals _
	( _
		byval bytestoclear as integer, _
		byval baseoffset as integer _
	) static

	dim as integer i
    dim as string lname

	if( bytestoclear = 0 ) then
		exit sub
	end if

	if( env.clopt.cputype >= FB_CPUTYPE_686 ) then
		if( cunsg(bytestoclear) \ 8 > 7 ) then

	    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EDI ) = FALSE ) then
    			hPUSH( "edi" )
    		end if

			outp( "lea edi, [ebp-" & baseoffset + bytestoclear & "]" )
			outp( "mov ecx," & cunsg(bytestoclear) \ 8 )
			outp( "pxor mm0, mm0" )
			lname = *symbUniqueLabel( )
			hLABEL( lname )
			outp( "movq [edi], mm0" )
			outp( "add edi, 8" )
			outp( "dec ecx" )
			outp( "jnz " + lname )
			outp( "emms" )

    		if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EDI ) = FALSE ) then
    			hPOP( "edi" )
    		end if

		elseif( cunsg(bytestoclear) \ 8 > 0 ) then
			outp( "pxor mm0, mm0" )
			for i = cunsg(bytestoclear) \ 8 to 1 step -1
				outp( "movq [ebp-" & ( i*8 ) & "], mm0" )
			next
			outp( "emms" )

		end if

		if( bytestoclear and 4 ) then
			outp( "mov dword ptr [ebp-" & baseoffset + bytestoclear & "], 0" )
		end if

		exit sub
	end if

	if( cunsg(bytestoclear) \ 4 > 6 ) then

    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EDI ) = FALSE ) then
   			hPUSH( "edi" )
   		end if

		outp( "lea edi, [ebp-" & baseoffset + bytestoclear & "]" )
		outp( "mov ecx," & cunsg(bytestoclear) \ 4 )
		outp( "xor eax, eax" )
		outp( "rep stosd" )

   		if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EDI ) = FALSE ) then
   			hPOP( "edi" )
   		end if

	else
		for i = cunsg(bytestoclear) \ 4 to 1 step -1
			 outp( "mov dword ptr [ebp-" & baseoffset + ( i*4 ) & "], 0" )
		next
	end if

end sub

'':::::
private sub hCreateFrame _
	( _
		byval proc as FBSYMBOL ptr _
	) static

    dim as integer bytestoalloc, bytestoclear
	dim as zstring ptr lprof

	' No frame for naked functions
	if( symbIsNaked( proc ) = FALSE ) then

    	bytestoalloc = ((proc->proc.ext->stk.localmax - EMIT_LOCSTART) + 3) and (not 3)

    	if( (bytestoalloc <> 0) or _
    		(proc->proc.ext->stk.argofs <> EMIT_ARGSTART) or _
        	symbGetIsMainProc( proc ) or _
        	env.clopt.debug or _
			env.clopt.profile ) then

    		hPUSH( "ebp" )
    		outp( "mov ebp, esp" )

        	if( symbGetIsMainProc( proc ) ) then
				outp( "and esp, 0xFFFFFFF0" )
	    	end if

    		if( bytestoalloc > 0 ) then
    			outp( "sub esp, " + str( bytestoalloc ) )
    		end if
    	end if

		if( env.clopt.target = FB_COMPTARGET_DOS ) then
			if( env.clopt.profile ) then
				lprof = symbMakeProfileLabelName( )

				outEx(".section .data" + NEWLINE )
				outEx( ".balign 4" + NEWLINE )
				outEx( "." + *lprof + ":" + NEWLINE )
				outp( ".long 0" )
				outEx( ".section .text" + NEWLINE )
				outp( "mov edx, offset ." + *lprof )
				outp( "call _mcount" )
			end if
		end if

    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EBX ) ) then
    		hPUSH( "ebx" )
    	end if
    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_ESI ) ) then
    		hPUSH( "esi" )
    	end if
    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EDI ) ) then
    		hPUSH( "edi" )
    	end if

	end if

	''
#if 0
	bytestoclear = ((proc->proc.ext->stk.localofs - EMIT_LOCSTART) + 3) and (not 3)

	hClearLocals( bytestoclear, 0 )
#endif

end sub

''::::
private sub hDestroyFrame _
	( _
		byval proc as FBSYMBOL ptr, _
		byval bytestopop as integer _
	) static

	' don't do anything for naked functions, except the .size at the end
	if( symbIsNaked( proc ) = FALSE ) then

    	dim as integer bytestoalloc

    	bytestoalloc = ((proc->proc.ext->stk.localmax - EMIT_LOCSTART) + 3) and (not 3)

    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EDI ) ) then
    		hPOP( "edi" )
    	end if
    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_ESI ) ) then
    		hPOP( "esi" )
    	end if
    	if( EMIT_REGISUSED( FB_DATACLASS_INTEGER, EMIT_REG_EBX ) ) then
    		hPOP( "ebx" )
    	end if

    	if( (bytestoalloc <> 0) or _
    		(proc->proc.ext->stk.argofs <> EMIT_ARGSTART) or _
        	symbGetIsMainProc( proc ) or _
        	env.clopt.debug or _
			env.clopt.profile ) then
    		outp( "mov esp, ebp" )
    		hPOP( "ebp" )
    	end if

    	if( bytestopop > 0 ) then
    		outp( "ret " + str( bytestopop ) )
    	else
    		outp( "ret" )
    	end if

	end if

	if( env.clopt.target = FB_COMPTARGET_LINUX ) then
    	outEx( ".size " + *symbGetMangledName( proc ) + ", .-" + *symbGetMangledName( proc ) + NEWLINE )
	end if

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' implementation
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

private sub _emitLIT( byval s as zstring ptr )
	dim ostr as string
	ostr = *s + NEWLINE
	outEX( ostr )
end sub

private sub _emitJMPTB _
	( _
		byval tbsym as FBSYMBOL ptr, _
		byval values1 as ulongint ptr, _
		byval labels1 as FBSYMBOL ptr ptr, _
		byval labelcount as integer, _
		byval deflabel as FBSYMBOL ptr, _
		byval minval as ulongint, _
		byval maxval as ulongint _
	)

	dim as FBSYMBOL ptr label = any
	dim as string deflabelname, tb
	dim as integer i = any

	deflabelname = *symbGetMangledName( deflabel )

	assert( labelcount > 0 )

	tb = *symbGetMangledName( tbsym )

	''
	'' Emit entries for each value from minval to maxval.
	'' Each value that is in the values1 array uses the corresponding label
	'' from the labels1 array; all other values use the default label.
	''
	'' table:
	'' .int labelforvalue1
	'' .int labelforvalue2
	'' .int deflabel
	'' .int labelforvalue4
	'' ...
	''

	outEx( tb + ":" + NEWLINE )
	i = 0
	for value as ulongint = minval to maxval
		assert( i < labelcount )
		if( value = values1[i] ) then
			label = labels1[i]
			i += 1
		else
			label = deflabel
		end if
		outp( *_getTypeString( FB_DATATYPE_UINT ) + " " + *symbGetMangledName( label ) )
	next

end sub

'':::::
private sub _emitCALL _
	( _
		byval unused as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval bytestopop as integer _
	) static

    dim ostr as string

	ostr = "call "
	ostr += *symbGetMangledName( label )
	outp( ostr )

    if( bytestopop <> 0 ) then
    	ostr = "add esp, " + str( bytestopop )
    	outp( ostr )
    end if

end sub

'':::::
private sub _emitCALLPTR _
	( _
		byval svreg as IRVREG ptr, _
		byval unused as FBSYMBOL ptr, _
		byval bytestopop as integer _
	) static

    dim src as string
    dim ostr as string

	hPrepOperand( svreg, src )

	ostr = "call " + src
	outp( ostr )

    if( bytestopop <> 0 ) then
    	ostr = "add esp, " + str( bytestopop )
    	outp( ostr )
    end if

end sub

'':::::
private sub _emitBRANCH _
	( _
		byval unused as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval op as integer _
	) static

    dim ostr as string

	select case as const op
	case AST_OP_JLE
		ostr = "jle "
	case AST_OP_JGE
		ostr = "jge "
	case AST_OP_JLT
		ostr = "jl "
	case AST_OP_JGT
		ostr = "jg "
	case AST_OP_JEQ
		ostr = "je "
	case AST_OP_JNE
		ostr = "jne "
	end select

	ostr += *symbGetMangledName( label )
	outp( ostr )

end sub

'':::::
private sub _emitJUMP _
	( _
		byval unused1 as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval unused2 as integer _
	) static

    dim ostr as string

	ostr = "jmp "
	ostr += *symbGetMangledName( label )
	outp( ostr )

end sub

'':::::
private sub _emitJUMPPTR _
	( _
		byval svreg as IRVREG ptr, _
		byval unused1 as FBSYMBOL ptr, _
		byval unused2 as integer _
	) static

    dim src as string
    dim ostr as string

	hPrepOperand( svreg, src )

	ostr = "jmp " + src
	outp( ostr )

end sub

'':::::
private sub _emitRET _
	( _
		byval vreg as IRVREG ptr _
	) static

    dim ostr as string

	ostr = "ret " + str( vreg->value.i )
    outp( ostr )

end sub

'':::::
private sub _emitPUBLIC _
	( _
		byval label as FBSYMBOL ptr _
	) static

    dim ostr as string

	ostr = NEWLINE + ".globl "
	ostr += *symbGetMangledName( label )
	ostr += NEWLINE
	outEx( ostr )

end sub

'':::::
private sub _emitLABEL _
	( _
		byval label as FBSYMBOL ptr _
	) static

    dim ostr as string

	ostr = *symbGetMangledName( label )
	ostr += ":" + NEWLINE
	outEx( ostr )

end sub

'':::::
private sub _emitNOP _
	( _
		_
	) static

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' store
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub hULONG2DBL _
	( _
		byval svreg as IRVREG ptr _
	) static

	dim as string label, aux, ostr

	label = *symbUniqueLabel( )

	hPrepOperand( svreg, aux, FB_DATATYPE_INTEGER, 0, TRUE )
	ostr = "cmp " + aux + ", 0"

	outp ostr
	ostr = "jns " + label
	outp ostr
	hPUSH( "0x403f" )
	hPUSH( "0x80000000" )
	hPUSH( "0" )
	outp "fldt [esp]"
	outp "add esp, 12"
	outp "faddp"
	hLABEL( label )

end sub

'':::::
private sub _emitSTORL2L _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst1, dst2, src1, src2, ostr

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "mov " + dst1 + COMMA + src1
	outp ostr

	ostr = "mov " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitSTORI2L _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst1, dst2, src1, ext, ostr
    dim sdsize as integer

	sdsize = typeGetSize( svreg->dtype )

	hPrepOperand64( dvreg, dst1, dst2 )

	hPrepOperand( svreg, src1 )

	'' immediate?
	if( svreg->typ = IR_VREGTYPE_IMM ) then
		hMOV dst1, src1

		'' negative?
		if( typeIsSigned( svreg->dtype ) and (svreg->value.i < 0) ) then
			hMOV dst2, "-1"
		else
			hMOV dst2, "0"
		end if

		exit sub
	end if

	''
	if( sdsize < 4 ) then
		ext = *hGetRegName( FB_DATATYPE_INTEGER, svreg->reg )

		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "movsx "
		else
			ostr = "movzx "
		end if
		ostr += ext + COMMA + src1
		outp ostr

	else
		ext = src1
	end if

	ostr = "mov " + dst1 + COMMA + ext
	outp ostr

	if( typeIsSigned( svreg->dtype ) ) then

		hPUSH ext

		ostr = "sar " + ext + ", 31"
		outp ostr

		ostr = "mov " + dst2 + COMMA + ext
		outp ostr

		hPOP ext

	else
		ostr = "mov " + dst2 + ", 0"
		outp ostr
	end if

end sub


'':::::
private sub _emitSTORF2L _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst
    dim as string ostr

	hPrepOperand( dvreg, dst )

	'' signed?
	if( typeIsSigned( dvreg->dtype ) ) then
		ostr = "fistp " + dst
		outp ostr

	end if

end sub


'':::::
private sub _emitSTORI2I _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
	dim as integer ddsize, sdsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ddsize = typeGetSize( dvreg->dtype )
	sdsize = typeGetSize( svreg->dtype )

	if( ddsize = 1 ) then
		if( svreg->typ = IR_VREGTYPE_IMM ) then
			ddsize = 4
		end if
	end if

	'' dst size = src size
	if( (svreg->typ = IR_VREGTYPE_IMM) or (ddsize = sdsize) ) then
		ostr = "mov " + dst + COMMA + src
		outp ostr
	'' sizes are different..
	else
    	dim as string aux

		aux = *hGetRegName( dvreg->dtype, svreg->reg )

		'' dst size > src size
		if( ddsize > sdsize ) then
			if( typeIsSigned( svreg->dtype ) ) then
				ostr = "movsx "
			else
				ostr = "movzx "
			end if
			ostr += aux + COMMA + src
			outp ostr

			ostr = "mov " + dst + COMMA + aux
			outp ostr

		'' dst size < src size
		else
            '' handle DI/SI as source stored into a byte destine
            dim as integer is_disi

            is_disi = FALSE
            if( ddsize = 1 ) then
            	if( svreg->typ = IR_VREGTYPE_REG ) then
            		is_disi = (svreg->reg = EMIT_REG_ESI) or (svreg->reg = EMIT_REG_EDI)
            	end if
            end if

			if( is_disi ) then
    			dim as string aux8
    			dim as integer reg, isfree

				reg = hFindRegNotInVreg( dvreg, TRUE )

				aux8 = *hGetRegName( FB_DATATYPE_BYTE, reg )
				aux = *hGetRegName( svreg->dtype, reg )

				isfree = hIsRegFree(FB_DATACLASS_INTEGER, reg )
				if( isfree = FALSE ) then
					hPUSH aux
				end if

				ostr = "mov " + aux + COMMA + src
				outp ostr

				ostr = "mov " + dst + COMMA + aux8
				outp ostr

				if( isfree = FALSE ) then
					hPOP aux
				end if

			else
				ostr = "mov " + dst + COMMA + aux
				outp ostr
			end if
		end if
	end if

end sub

'':::::
private sub _emitSTORL2I _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	'' been too complex due the SI/DI crap, leave it to I2I
	_emitSTORI2I( dvreg, svreg )

end sub


'':::::
private sub _emitSTORF2I _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer ddsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ddsize = typeGetSize( dvreg->dtype )

	'' byte destine? damn..
	if( ddsize = 1 ) then

		outp "sub esp, 4"
		outp "fistp dword ptr [esp]"

        '' destine is a reg?
        if( dvreg->typ = IR_VREGTYPE_REG ) then

           	hMOV dst, "byte ptr [esp]"
           	outp "add esp, 4"

		'' destine is a var/idx/ptr
		else
            dim as integer reg, isfree
            dim as string aux, aux8

			reg = hFindRegNotInVreg( dvreg, TRUE )

			aux8 = *hGetRegName( FB_DATATYPE_BYTE, reg )
			aux  = *hGetRegName( FB_DATATYPE_INTEGER, reg )

			isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

			if( isfree = FALSE ) then
				hXCHG aux, "dword ptr [esp]"
			else
				hMOV aux8, "byte ptr [esp]"
			end if

			hMOV dst, aux8

			if( isfree = FALSE ) then
				hPOP aux
			else
				outp "add esp, 4"
			end if

		end if

	else
		'' signed?
		if( typeIsSigned( dvreg->dtype ) ) then
			ostr = "fistp " + dst
			outp ostr

		'' unsigned.. try a bigger type
		else
			'' uint?
			if( ddsize = 4 ) then
				outp "sub esp, 8"
				outp "fistp qword ptr [esp]"
				hPOP dst
				outp  "add esp, 4"

			'' ushort..
			else
				outp "sub esp, 4"
				outp "fistp dword ptr [esp]"
				hPOP dst
				outp  "add esp, 2"
			end if
		end if

	end if

end sub



'':::::
private sub _emitSTORL2F _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src, aux
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( (svreg->typ = IR_VREGTYPE_REG) or (svreg->typ = IR_VREGTYPE_IMM) ) then

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then

			hPrepOperand64( svreg, src, aux )

			hPUSH( aux )
			hPUSH( src )

			ostr = "fild " + dtypeTB(svreg->dtype).mname + " [esp]"
			outp ostr

			outp "add esp, 8"

		'' unsigned..
		else
			hPrepOperand64( svreg, src, aux )
			hPUSH aux
			hPUSH src
			outp "fild qword ptr [esp]"
			outp "add esp, 8"
			hULONG2DBL( svreg )

		end if

	'' not a reg or imm
	else
		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "fild " + src
			outp ostr

		'' unsigned, try a bigger type..
		else
			ostr = "fild " + src
			outp ostr
			hULONG2DBL( svreg )

		end if
	end if

	ostr = "fstp " + dst
	outp ostr

end sub



'':::::
private sub _emitSTORI2F _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer sdsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	sdsize = typeGetSize( svreg->dtype )

	'' byte source? damn..
	if( sdsize = 1 ) then
    	dim as string aux
    	dim as integer reg, isfree

		reg = hFindRegNotInVreg( svreg )

		aux = *hGetRegName( FB_DATATYPE_INTEGER, reg )

		isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

		if( isfree = FALSE ) then
			hPUSH aux
		end if

		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "movsx "
		else
			ostr = "movzx "
		end if
		ostr += aux + COMMA + src
		outp ostr

		hPUSH aux
		outp "fild dword ptr [esp]"
		outp "add esp, 4"

		if( isfree = FALSE ) then
			hPOP aux
		end if

		ostr = "fstp " + dst
		outp ostr

		exit sub
	end if

	''
	if( (svreg->typ = IR_VREGTYPE_REG) or (svreg->typ = IR_VREGTYPE_IMM) ) then

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then

			'' not an integer? make it
			if( (svreg->typ = IR_VREGTYPE_REG) and (sdsize < 4) ) then
				src = *hGetRegName( FB_DATATYPE_INTEGER, svreg->reg )
			end if

			hPUSH src

			ostr = "fild " + dtypeTB(svreg->dtype).mname + " [esp]"
			outp ostr

			outp "add esp, 4"

		'' unsigned..
		else

			'' uint..
			if( sdsize = 4 ) then
				hPUSH "0"
				hPUSH src
				outp "fild qword ptr [esp]"
				outp "add esp, 8"

			'' ushort..
			else
				if( svreg->typ <> IR_VREGTYPE_IMM ) then
					hPUSH "0"
				end if

				hPUSH src
				outp "fild dword ptr [esp]"

				if( svreg->typ <> IR_VREGTYPE_IMM ) then
					outp "add esp, 6"
				else
					outp "add esp, 4"
				end if
			end if

		end if

	'' not a reg or imm
	else

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "fild " + src
			outp ostr

		'' unsigned, try a bigger type..
		else
			'' uint..
			if( sdsize = 4 ) then
				hPUSH "0"
				hPUSH src
				outp "fild qword ptr [esp]"
				outp "add esp, 8"

			'' ushort..
			else
				hPUSH "0"
				hPUSH src
				outp "fild dword ptr [esp]"
				outp "add esp, 6"
			end if

		end if
	end if

	ostr = "fstp " + dst
	outp ostr

end sub



'':::::
private sub _emitSTORF2F _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer ddsize, sdsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ddsize = typeGetSize( dvreg->dtype )
	sdsize = typeGetSize( svreg->dtype )

	'' on fpu stack?
	if( svreg->typ = IR_VREGTYPE_REG ) then
		ostr = "fstp " + dst
		outp ostr

	else
		'' same size? just copy..
		if( sdsize = ddsize ) then

			hPrepOperand( svreg, src, FB_DATATYPE_INTEGER, 0 )
			ostr = "push " + src
			outp ostr

			if( sdsize > 4 ) then
				hPrepOperand( svreg, src, FB_DATATYPE_INTEGER, 4 )
				ostr = "push " + src
				outp ostr

				hPrepOperand( dvreg, dst, FB_DATATYPE_INTEGER, 4 )
				ostr = "pop " + dst
				outp ostr
			end if

			hPrepOperand( dvreg, dst, FB_DATATYPE_INTEGER, 0 )
			ostr = "pop " + dst
			outp ostr

		'' diff sizes, convert..
		else
			ostr = "fld " + src
			outp ostr

			ostr = "fstp " + dst
			outp ostr
		end if
	end if

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' load
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub _emitLOADL2L _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst1, dst2, src1, src2
    dim as string ostr

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "mov " + dst1 + COMMA + src1
	outp ostr

	ostr = "mov " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitLOADI2L _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst1, dst2, src1
    dim as integer sdsize
    dim as string ostr

	sdsize = typeGetSize( svreg->dtype )

	hPrepOperand64( dvreg, dst1, dst2 )

	hPrepOperand( svreg, src1 )

	'' immediate?
	if( svreg->typ = IR_VREGTYPE_IMM ) then
        hMOV dst1, src1

		'' negative?
		if( typeIsSigned( svreg->dtype ) and (svreg->value.i < 0) ) then
			hMOV dst2, "-1"
		else
			hMOV dst2, "0"
		end if

		exit sub
	end if

	''
	if( typeIsSigned( svreg->dtype ) ) then

		if( sdsize < 4 ) then
			ostr = "movsx " + dst1 + COMMA + src1
			outp ostr
		else
			hMOV dst1, src1
		end if

		hMOV dst2, dst1

		ostr = "sar " + dst2 + ", 31"
		outp ostr

	else

		if( sdsize < 4 ) then
			ostr = "movzx " + dst1 + COMMA + src1
			outp ostr
		else
			hMOV dst1, src1
		end if

		hMOV dst2, "0"

	end if

end sub



'':::::
private sub _emitLOADF2L _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src, aux
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		ostr = "fld " + src
		outp ostr
	end if

	hPrepOperand64( dvreg, dst, aux )

	'' signed?
	if( typeIsSigned( dvreg->dtype ) ) then

		outp "sub esp, 8"

		ostr = "fistp " + dtypeTB(dvreg->dtype).mname + " [esp]"
		outp ostr

		hPOP( dst )
		hPOP( aux )

	'' unsigned.. try a bigger type
	else
		dim as string label_geq, label_done
		dim as integer iseaxfree = any

		label_geq = *symbUniqueLabel( )
		label_done = *symbUniqueLabel( )

		'' eax free, or only used in dest?
		iseaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
		iseaxfree orelse= hIsRegInVreg( dvreg, EMIT_REG_EAX )

		outp "sub esp, 8"
        outp "mov dword ptr [esp], 0x5F000000" '' 2^63
        outp "fcom dword ptr [esp]"

		if( iseaxfree ) then
			outp "fnstsw ax"
			outp "test ah, 1"
		else
			hPUSH( "eax" )
			outp "fnstsw ax"
			outp "test ah, 1"
			hPOP( "eax" )
		end if

		hBRANCH( "jz", label_geq )

		'' if x < 2^63
        outp "fistp qword ptr [esp]"
		hPOP( dst )
		hPOP( aux )

		'' elseif x >= 2^63
		hBRANCH( "jmp", label_done )
		hLABEL( label_geq )

        outp "fsub dword ptr [esp]"
        outp "fistp qword ptr [esp]"
		hPOP( dst )
		hPOP( aux )
        outp "xor " + aux + ", 0x80000000"

		'' endif
		hLABEL( label_done )

	end if

end sub

'':::::
private sub _emitLOADI2I _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
	dim as integer ddsize, sdsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ddsize = typeGetSize( dvreg->dtype )
	sdsize = typeGetSize( svreg->dtype )

	if( ddsize = 1 ) then
		if( svreg->typ = IR_VREGTYPE_IMM ) then
			ddsize = 4
		end if
	end if

	'' dst size = src size
	if( ddsize = sdsize ) then
		ostr = "mov " + dst + COMMA + src
		outp ostr
	else
		'' dst size > src size
		if( ddsize > sdsize ) then
			if( typeIsSigned( svreg->dtype ) ) then
				ostr = "movsx "
			else
				ostr = "movzx "
			end if
			ostr += dst + COMMA + src
			outp ostr

		'' dst dize < src size
		else
			'' is src a reg too?
			if( svreg->typ = IR_VREGTYPE_REG ) then
				'' not the same?
				if( svreg->reg <> dvreg->reg ) then
					dim as string aux
					dim as integer dtype

					dtype = dvreg->dtype

					'' handle [E]DI/[E]SI source loaded to a byte destine
					if( ddsize = 1 ) then
						if( (svreg->reg = EMIT_REG_ESI) or _
							(svreg->reg = EMIT_REG_EDI) ) then

							dtype = FB_DATATYPE_INTEGER
							dst = *hGetRegName( dtype, dvreg->reg )
						end if
					end if

					aux = *hGetRegName( dtype, svreg->reg )
					ostr = "mov " + dst + COMMA + aux
					outp ostr
				end if

			'' src is not a reg
			else
				hPrepOperand( svreg, src, dvreg->dtype )

				ostr = "mov " + dst + COMMA + src
				outp ostr
			end if
		end if
	end if

end sub

'':::::
private sub _emitLOADL2I _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	'' been too complex due the SI/DI crap, leave it to I2I
	_emitLOADI2I( dvreg, svreg )

end sub


'':::::
private sub _emitLOADF2I _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer ddsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ddsize = typeGetSize( dvreg->dtype )

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		ostr = "fld " + src
		outp ostr
	end if

	'' byte destine? damn..
	if( ddsize = 1 ) then

		outp "sub esp, 4"
        outp "fistp dword ptr [esp]"

    	'' destine is a reg
    	if( dvreg->typ = IR_VREGTYPE_REG ) then
			hMOV dst, "byte ptr [esp]"
			outp "add esp, 4"

		'' destine is a var/idx/ptr
        else
    		dim as string aux, aux8
    		dim as integer reg, isfree

			reg = hFindRegNotInVreg( dvreg, TRUE )

			aux8 = *hGetRegName( FB_DATATYPE_BYTE, reg )
			aux  = *hGetRegName( FB_DATATYPE_INTEGER, reg )

			isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

			if( isfree = FALSE ) then
				hXCHG aux, "dword ptr [esp]"
			else
				hMOV aux8, "byte ptr [esp]"
			end if

			hMOV dst, aux8

			if( isfree = FALSE ) then
				hPOP aux
			else
				outp "add esp, 4"
			end if

        end if

	else

		'' signed?
		if( typeIsSigned( dvreg->dtype ) ) then

			outp "sub esp, 4"

			ostr = "fistp " + dtypeTB(dvreg->dtype).mname + " [esp]"
			outp ostr

			'' not an integer? make it
			if( ddsize < 4 ) then
				dst = *hGetRegName( FB_DATATYPE_INTEGER, dvreg->reg )
			end if

			hPOP dst

		'' unsigned.. try a bigger type
		else

			'' uint?
			if( ddsize = 4 ) then
				outp "sub esp, 8"
				outp "fistp qword ptr [esp]"
				hPOP dst
				outp  "add esp, 4"

			'' ushort..
			else
				outp "sub esp, 4"
				outp "fistp dword ptr [esp]"
				hPOP dst
				outp  "add esp, 2"
			end if

		end if

	end if

end sub


'':::::
private sub _emitLOADL2F _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src, aux
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( (svreg->typ = IR_VREGTYPE_REG) or (svreg->typ = IR_VREGTYPE_IMM) ) then

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then

			hPrepOperand64( svreg, src, aux )

			hPUSH( aux )
			hPUSH( src )

			ostr = "fild " + dtypeTB(svreg->dtype).mname + " [esp]"
			outp ostr

			outp "add esp, 8"

		'' unsigned, try a bigger type..
		else

			hPrepOperand64( svreg, src, aux )
			hPUSH aux
			hPUSH src
			outp "fild qword ptr [esp]"
			outp "add esp, 8"
			hULONG2DBL( svreg )

		end if

	'' not a reg or imm
	else

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "fild " + src
			outp ostr

		'' unsigned, try a bigger type..
		else
			ostr = "fild " + src
			outp ostr
			hULONG2DBL( svreg )

		end if

	end if

end sub



'':::::
private sub _emitLOADI2F _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer sdsize
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

    sdsize = typeGetSize( svreg->dtype )

	'' byte source? damn..
	if( sdsize = 1 ) then
    	dim as string aux
    	dim as integer isfree, reg

		reg = hFindRegNotInVreg( svreg )

		aux = *hGetRegName( FB_DATATYPE_INTEGER, reg )

		isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

		if( isfree = FALSE ) then
			hPUSH aux
		end if

		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "movsx " + aux + COMMA + src
			outp ostr
		else
			ostr = "movzx " + aux + COMMA + src
			outp ostr
		end if

		hPUSH aux
		outp "fild dword ptr [esp]"
		outp "add esp, 4"

		if( isfree = FALSE ) then
			hPOP aux
		end if

		exit sub
	end if

	''
	if( (svreg->typ = IR_VREGTYPE_REG) or (svreg->typ = IR_VREGTYPE_IMM) ) then

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then

			'' not an integer? make it
			if( (svreg->typ = IR_VREGTYPE_REG) and (sdsize < 4) ) then
				src = *hGetRegName( FB_DATATYPE_INTEGER, svreg->reg )
			end if

			hPUSH src

			ostr = "fild " + dtypeTB(svreg->dtype).mname + " [esp]"
			outp ostr

			outp "add esp, 4"

		'' unsigned, try a bigger type..
		else

			'' uint?
			if( sdsize = 4 ) then
				hPUSH "0"
				hPUSH src
				outp "fild qword ptr [esp]"
				outp "add esp, 8"

			'' ushort..
			else
				if( svreg->typ <> IR_VREGTYPE_IMM ) then
					hPUSH "0"
				end if

				hPUSH src
				outp "fild dword ptr [esp]"

				if( svreg->typ <> IR_VREGTYPE_IMM ) then
					outp "add esp, 6"
				else
					outp "add esp, 4"
				end if
			end if

		end if

	'' not a reg or imm
	else

		'' signed?
		if( typeIsSigned( svreg->dtype ) ) then
			ostr = "fild " + src
			outp ostr

		'' unsigned, try a bigger type..
		else
			'' uint..
			if( sdsize = 4 ) then
				hPUSH "0"
				hPUSH src
				outp "fild qword ptr [esp]"
				outp "add esp, 8"

			'' ushort..
			else
				hPUSH "0"
				hPUSH src
				outp "fild dword ptr [esp]"
				outp "add esp, 6"
			end if
		end if

	end if

end sub



'':::::
private sub _emitLOADF2F _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string src
    dim as string ostr

	hPrepOperand( svreg, src )

	ostr = "fld " + src
	outp ostr

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' binary ops
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub _emitMOVL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst1, dst2, src1, src2, ostr

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "mov " + dst1 + COMMA + src1
	outp ostr

	ostr = "mov " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitMOVI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src, ostr

	'' byte? handle SI, DI used as bytes..
	if( typeGetSize( dvreg->dtype ) = 1 ) then
		'' MOV is only used when both operands are registers
		dst = *hGetRegName( FB_DATATYPE_INTEGER, dvreg->reg )
		src = *hGetRegName( FB_DATATYPE_INTEGER, svreg->reg )
	else
		hPrepOperand( dvreg, dst )
		hPrepOperand( svreg, src )
	end if

	ostr = "mov " + dst + COMMA + src
	outp ostr

end sub


'':::::
private sub _emitMOVF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	'' do nothing, both are regs

end sub

'':::::
private sub _emitADDL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "add " + dst1 + COMMA + src1
	outp ostr

	ostr = "adc " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitADDI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim doinc as integer, dodec as integer
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	doinc = FALSE
	dodec = FALSE
	if( svreg->typ = IR_VREGTYPE_IMM ) then
		select case svreg->value.i
		case 1
			doinc = TRUE
		case -1
			dodec = TRUE
		end select
	end if

	if( doinc ) then
		ostr = "inc " + dst
		outp ostr
	elseif( dodec ) then
		ostr = "dec " + dst
		outp ostr
	else
		ostr = "add " + dst + COMMA + src
		outp ostr
	end if

end sub



'':::::
private sub _emitADDF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim src as string
    dim ostr as string

	hPrepOperand( svreg, src )

	if( svreg->typ = IR_VREGTYPE_REG ) then
		ostr = "faddp"
		outp ostr
	else
		if( typeGetClass( svreg->dtype ) = FB_DATACLASS_FPOINT ) then
			ostr = "fadd " + src
			outp ostr
		else
			'' Relying on hDoOptRemConv()
			assert( (typeGetSize( svreg->dtype ) = 2) or (typeGetSize( svreg->dtype ) = 4) )
			ostr = "fiadd " + src
			outp ostr
		end if
	end if

end sub

'':::::
private sub _emitSUBL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "sub " + dst1 + COMMA + src1
	outp ostr

	ostr = "sbb " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitSUBI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim doinc as integer, dodec as integer
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	doinc = FALSE
	dodec = FALSE
	if( svreg->typ = IR_VREGTYPE_IMM ) then
		select case svreg->value.i
		case 1
			dodec = TRUE
		case -1
			doinc = TRUE
		end select
	end if

	if( dodec ) then
		ostr = "dec " + dst
		outp ostr
	elseif( doinc ) then
		ostr = "inc " + dst
		outp ostr
	else
		ostr = "sub " + dst + COMMA + src
		outp ostr
	end if

end sub



'':::::
private sub _emitSUBF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim src as string
    dim doinc as integer, dodec as integer
    dim ostr as string

	hPrepOperand( svreg, src )

	if( svreg->typ = IR_VREGTYPE_REG ) then
		outp "fsubrp"
	else
		if( typeGetClass( svreg->dtype ) = FB_DATACLASS_FPOINT ) then
			ostr = "fsub " + src
			outp ostr
		else
			'' Relying on hDoOptRemConv()
			assert( (typeGetSize( svreg->dtype ) = 2) or (typeGetSize( svreg->dtype ) = 4) )
			ostr = "fisub " + src
			outp ostr
		end if
	end if

end sub

'':::::
private sub _emitMULL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim iseaxfree as integer, isedxfree as integer
    dim eaxindest as integer, edxindest as integer
    dim ofs as integer

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	iseaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
	isedxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDX )

	eaxindest = hIsRegInVreg( dvreg, EMIT_REG_EAX )
	edxindest = hIsRegInVreg( dvreg, EMIT_REG_EDX )

	hPUSH( src2 )
	hPUSH( src1 )
	hPUSH( dst2 )
	hPUSH( dst1 )

	ofs = 0

	if( edxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			ofs += 4
			hPUSH( "edx" )
		end if
	else
		if( isedxfree = FALSE ) then
			ofs += 4
			hPUSH( "edx" )
		end if
	end if

	if( eaxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			ofs += 4
			hPUSH "eax"
		end if
	else
		if( iseaxfree = FALSE ) then
			ofs += 4
			hPUSH "eax"
		end if
	end if

	'' res = low(dst) * low(src)
	outp "mov eax, [esp+" + str( 0+ofs ) + "]"
	outp "mul dword ptr [esp+" + str( 8+ofs ) + "]"

	'' hres= low(dst) * high(src) + high(res)
	outp "xchg eax, [esp+" + str ( 0+ofs ) + "]"

	outp "imul eax, [esp+" + str ( 12+ofs ) + "]"
	outp "add eax, edx"

	'' hres += high(dst) * low(src)
	outp "mov edx, [esp+" + str( 4+ofs ) + "]"
	outp "imul edx, [esp+" + str( 8+ofs ) + "]"
	outp "add edx, eax"
	outp "mov [esp+" + str( 4+ofs ) + "], edx"

	if( eaxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPOP "eax"
		end if
	else
		if( iseaxfree = FALSE ) then
			hPOP "eax"
		end if
	end if

	if( edxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPOP "edx"
		end if
	else
		if( isedxfree = FALSE ) then
			hPOP "edx"
		end if
	end if

	'' low(dst) = low(res)
	hPOP dst1
	'' high(dst) = hres
	hPOP dst2

	outp "add esp, 8"

	'' code:
	'' mov	eax, low(dst)
	'' mul	low(src)
	'' mov	ebx, low(dst)
	'' imul	ebx, high(src)
	'' add	ebx, edx
	'' mov	edx, high(dst)
	'' imul	edx, low(src)
	'' add	edx, ebx
	'' mov	low(dst), eax
	'' mov	high(dst), edx

end sub

'':::::
private sub _emitMULI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim reg as integer, isfree as integer, rname as string
    dim ostr as string
    dim dst as string, src as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( dvreg->typ <> IR_VREGTYPE_REG ) then

		reg = hFindRegNotInVreg( svreg )
		rname = *hGetRegName( svreg->dtype, reg )

		isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

		if( isfree = FALSE ) then
			hPUSH rname
		end if

		hMOV rname, dst
		ostr = "imul " + rname + COMMA + src
		outp ostr
		hMOV dst, rname

		if( isfree = FALSE ) then
			hPOP rname
		end if

	else
		ostr = "imul " + dst + COMMA + src
		outp ostr
	end if

end sub



'':::::
private sub _emitMULF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim src as string
    dim ostr as string

	hPrepOperand( svreg, src )

	if( svreg->typ = IR_VREGTYPE_REG ) then
		outp "fmulp"
	else
		if( typeGetClass( svreg->dtype ) = FB_DATACLASS_FPOINT ) then
			ostr = "fmul " + src
			outp ostr
		else
			'' Relying on hDoOptRemConv()
			assert( (typeGetSize( svreg->dtype ) = 2) or (typeGetSize( svreg->dtype ) = 4) )
			ostr = "fimul " + src
			outp ostr
		end if
	end if

end sub



'':::::
private sub _emitDIVF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim src as string
    dim ostr as string

	hPrepOperand( svreg, src )

	if( svreg->typ = IR_VREGTYPE_REG ) then
		outp "fdivrp"
	else
		if( typeGetClass( svreg->dtype ) = FB_DATACLASS_FPOINT ) then
			ostr = "fdiv " + src
			outp ostr
		else
			'' Relying on hDoOptRemConv()
			assert( (typeGetSize( svreg->dtype ) = 2) or (typeGetSize( svreg->dtype ) = 4) )
			ostr = "fidiv " + src
			outp ostr
		end if
	end if

end sub

'':::::
private sub _emitDIVI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer ecxtrashed
    dim as integer eaxfree, ecxfree, edxfree
    dim as integer eaxindest, ecxindest, edxindest
    dim as integer eaxinsource, edxinsource
    dim as string eax, ecx, edx
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( typeGetSize( dvreg->dtype ) = 4 ) then
		eax = "eax"
		ecx = "ecx"
		edx = "edx"
	else
		eax = "ax"
		ecx = "cx"
		edx = "dx"
	end if

	ecxtrashed = FALSE

	eaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
	ecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )
	edxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDX )

	eaxinsource = hIsRegInVreg( svreg, EMIT_REG_EAX )
	edxinsource = hIsRegInVreg( svreg, EMIT_REG_EDX )
	eaxindest = hIsRegInVreg( dvreg, EMIT_REG_EAX )
	edxindest = hIsRegInVreg( dvreg, EMIT_REG_EDX )
	ecxindest = hIsRegInVreg( dvreg, EMIT_REG_ECX )

	if( (eaxinsource) or (edxinsource) or (svreg->typ = IR_VREGTYPE_IMM) ) then
		ecxtrashed = TRUE
		if( ecxindest ) then
			hPUSH( "ecx" )
			if( dvreg->typ <> IR_VREGTYPE_REG ) then
				hPrepOperand( dvreg, ostr, FB_DATATYPE_INTEGER )
				hPUSH( ostr )
			end if
		elseif( ecxfree = FALSE ) then
			hPUSH( "ecx" )
		end if
		hMOV( ecx, src )
		src = ecx
	end if

	if( eaxindest = FALSE ) then
		if( (ecxindest) and (ecxtrashed) ) then
			if( eaxfree = FALSE ) then
				outp "xchg eax, [esp]"
			else
				hPOP "eax"
			end if
		else
			if( eaxfree = FALSE ) then
				hPUSH "eax"
			end if
			hMOV eax, dst
		end if

	else
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPUSH "eax"
			hMOV eax, dst
		end if
	end if

	if( edxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPUSH "edx"
		end if
	elseif( edxfree = FALSE ) then
		hPUSH "edx"
	end if

	if( typeIsSigned( dvreg->dtype ) ) then
		if( typeGetSize( dvreg->dtype ) = 4 ) then
			outp "cdq"
		else
			outp "cwd"
		end if

		ostr = "idiv " + src
		outp ostr

	else
		ostr = "xor " + edx + ", " + edx
		outp ostr

		ostr = "div " + src
		outp ostr
	end if

	if( edxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPOP "edx"
		end if
	elseif( edxfree = FALSE ) then
		hPOP "edx"
	end if

	if( eaxindest = FALSE ) then
		if( ecxindest and ecxtrashed ) then
			if( dvreg->typ <> IR_VREGTYPE_REG ) then
				if( eaxfree = FALSE ) then
					hPOP "ecx"					'' ecx= tos (eax)
					outp "xchg ecx, [esp]"			'' tos= ecx; ecx= dst
				else
					hPOP "ecx"					'' ecx= tos (ecx)
				end if
			end if
		end if

		hMOV dst, eax

		if( eaxfree = FALSE ) then
			hPOP "eax"
		end if

	else
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			if( (ecxfree = FALSE) and (ecxtrashed = FALSE) ) then
				outp "xchg ecx, [esp]"			'' tos= ecx; ecx= dst
				outp "xchg ecx, eax"			'' ecx= res; eax= dst
			else
				hMOV "ecx", "eax"			'' ecx= eax
				hPOP "eax"					'' restore eax
			end if

			hMOV dst, ecx					'' [eax+...] = ecx

			if( (ecxfree = FALSE) and (ecxtrashed = FALSE) ) then
				hPOP "ecx"
			end if
		end if
	end if

	if( ecxtrashed ) then
		if( (ecxfree = FALSE) and (ecxindest = FALSE) ) then
			hPOP "ecx"
		end if
	end if

end sub

'':::::
private sub _emitMODI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string dst, src
    dim as integer ecxtrashed
    dim as integer eaxfree, ecxfree, edxfree
    dim as integer eaxindest, ecxindest, edxindest
    dim as integer eaxinsource, edxinsource
    dim as string eax, ecx, edx
    dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( typeGetSize( dvreg->dtype ) = 4 ) then
		eax = "eax"
		ecx = "ecx"
		edx = "edx"
	else
		eax = "ax"
		ecx = "cx"
		edx = "dx"
	end if

	ecxtrashed = FALSE

	eaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
	ecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )
	edxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDX )

	eaxinsource = hIsRegInVreg( svreg, EMIT_REG_EAX )
	edxinsource = hIsRegInVreg( svreg, EMIT_REG_EDX )
	eaxindest = hIsRegInVreg( dvreg, EMIT_REG_EAX )
	edxindest = hIsRegInVreg( dvreg, EMIT_REG_EDX )
	ecxindest = hIsRegInVreg( dvreg, EMIT_REG_ECX )

	if( (eaxinsource) or (edxinsource) or (svreg->typ = IR_VREGTYPE_IMM) ) then
		ecxtrashed = TRUE
		if( ecxindest ) then
			hPUSH( "ecx" )
			if( dvreg->typ <> IR_VREGTYPE_REG ) then
				hPrepOperand( dvreg, ostr, FB_DATATYPE_INTEGER )
				hPUSH( ostr )
			end if
		elseif( ecxfree = FALSE ) then
			hPUSH( "ecx" )
		end if
		hMOV( ecx, src )
		src = ecx
	end if

	if( eaxindest = FALSE ) then
		if( (ecxindest) and (ecxtrashed) ) then
			if( eaxfree = FALSE ) then
				outp "xchg eax, [esp]"
			else
				hPOP "eax"
			end if
		else
			if( eaxfree = FALSE ) then
				hPUSH "eax"
			end if
			hMOV eax, dst
		end if

	else
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPUSH "eax"
			hMOV eax, dst
		end if
	end if

	if( edxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPUSH "edx"
		end if
	elseif( edxfree = FALSE ) then
		hPUSH "edx"
	end if

	if( typeIsSigned( dvreg->dtype ) ) then
		if( typeGetSize( dvreg->dtype ) = 4 ) then
			outp "cdq"
		else
			outp "cwd"
		end if

		ostr = "idiv " + src
		outp ostr

	else
		ostr = "xor " + edx + ", " + edx
		outp ostr

		ostr = "div " + src
		outp ostr
	end if

	hMOV eax, edx

	if( edxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPOP "edx"
		end if
	elseif( edxfree = FALSE ) then
		hPOP "edx"
	end if

	if( eaxindest = FALSE ) then
		if( ecxindest and ecxtrashed ) then
			if( dvreg->typ <> IR_VREGTYPE_REG ) then
				if( eaxfree = FALSE ) then
					hPOP "ecx"					'' ecx= tos (eax)
					outp "xchg ecx, [esp]"			'' tos= ecx; ecx= dst
				else
					hPOP "ecx"					'' ecx= tos (ecx)
				end if
			end if
		end if

		hMOV dst, eax

		if( eaxfree = FALSE ) then
			hPOP "eax"
		end if

	else
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			if( (ecxfree = FALSE) and (ecxtrashed = FALSE) ) then
				outp "xchg ecx, [esp]"			'' tos= ecx; ecx= dst
				outp "xchg ecx, eax"			'' ecx= res; eax= dst
			else
				hMOV "ecx", "eax"			'' ecx= eax
				hPOP "eax"					'' restore eax
			end if

			hMOV dst, ecx					'' [eax+...] = ecx

			if( (ecxfree = FALSE) and (ecxtrashed = FALSE) ) then
				hPOP "ecx"
			end if
		end if
	end if

	if( ecxtrashed ) then
		if( (ecxfree = FALSE) and (ecxindest = FALSE) ) then
			hPOP "ecx"
		end if
	end if

end sub

'':::::
private sub hSHIFTL _
	( _
		byval op as integer, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim as string dst1, dst2, src, label, mnemonic32, mnemonic64
	dim as integer tmpreg, tmpisfree
	dim as string tmpregname
	dim as string a, b
	dim as IRVREG ptr av, bv

	''
	if( op = AST_OP_SHL ) then
		'' x86 shl and sar are the same
		mnemonic32 = "shl "
		mnemonic64 = "shld "
	else
		if( typeIsSigned( dvreg->dtype ) ) then
			mnemonic32 = "sar "
		else
			mnemonic32 = "shr "
		end if
		mnemonic64 = "shrd "
	end if

	''
	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand( svreg, src, FB_DATATYPE_INTEGER )

	if( op = AST_OP_SHL ) then
		a = dst2
		av = dvreg->vaux
		b = dst1
		bv = dvreg
	else '' SHR
		a = dst1
		av = dvreg
		b = dst2
		bv = dvreg->vaux
	end if

	if( svreg->typ = IR_VREGTYPE_IMM ) then
		if( svreg->value.i >= 64 ) then
			'' zero both result halves
			if( bv->typ = IR_VREGTYPE_REG ) then
				outp "xor " + b + ", " + b
			else
				outp "mov " + b + ", 0"
			end if

			if( av->typ = IR_VREGTYPE_REG ) then
				outp "xor " + a + ", " + a
			else
				outp "mov " + a + ", 0"
			end if
		elseif( svreg->value.i >= 32 ) then
			tmpisfree = TRUE
			if( (bv->typ = IR_VREGTYPE_REG) or (av->typ = IR_VREGTYPE_REG) ) then
				'' a or b is a reg
				outp "mov " + a + ", " + b
			else
				'' neither is a reg; get a temp
				tmpreg = hFindFreeReg( FB_DATACLASS_INTEGER )
				if( tmpreg = INVALID ) then
					'' Can only use a temp reg that isn't used in the dest vreg,
					'' because the code generated below doesn't handle that case.
					tmpreg = hFindRegNotInVreg( dvreg )
					tmpisfree = FALSE
				end if
				tmpregname = *hGetRegName( FB_DATATYPE_INTEGER, tmpreg )
				if( tmpisfree = FALSE ) then
					hPUSH( tmpregname )
				end if
				outp "mov " + tmpregname + ", " + b
				outp "mov " + a + ", " + tmpregname
			end if

			if( (op = AST_OP_SHR) and typeIsSigned( dvreg->dtype ) ) then
				outp "sar " + b +", 31"
			elseif( bv->typ = IR_VREGTYPE_REG ) then
				outp "xor " + b + ", " + b
			else
				outp "mov " + b + ", 0"
			end if

			if( svreg->value.i > 32 ) then
				src = str( svreg->value.i - 32 )
				outp mnemonic32 + a + ", " + src
			end if

			if( tmpisfree = FALSE ) then
				hPOP( tmpregname )
			end if

		else '' src < 32
			if( bv->typ = IR_VREGTYPE_REG ) then
				outp mnemonic64 + a + ", " + b + ", " + src
				outp mnemonic32 + b + ", " + src
			elseif( av->typ = IR_VREGTYPE_REG ) then
				outp "xchg " + a + ", " + b
				outp mnemonic64 + b + ", " + a + ", " + src
				outp mnemonic32 + a + ", " + src
				outp "xchg " + a + ", " + b
			else
				tmpreg = hFindFreeReg( FB_DATACLASS_INTEGER )
				if( tmpreg = INVALID ) then
					'' Can only use a temp reg that isn't used in the dest vreg,
					'' because the code generated below doesn't handle that case.
					tmpreg = hFindRegNotInVreg( dvreg )
					tmpisfree = FALSE
				else
					tmpisfree = TRUE
				end if
				tmpregname = *hGetRegName( FB_DATATYPE_INTEGER, tmpreg )
				if( tmpisfree = FALSE ) then
					hPUSH( tmpregname )
				end if
				outp "mov " + tmpregname + ", " + b
				outp mnemonic64 + a + ", " + tmpregname + ", " + src
				outp mnemonic32 + tmpregname + ", " + src
				outp "mov " + b + ", " + tmpregname
				if( tmpisfree = FALSE ) then
					hPOP( "eax" )
				end if
			end if
		end if
	else
		'' if src is not an imm, use cl and check for the x86 glitches

		dim as integer iseaxfree, isedxfree, isecxfree
		dim as integer eaxindest, edxindest, ecxindest
		dim as integer ofs

		label = *symbUniqueLabel( )

		hPUSH( dst2 )
		hPUSH( dst1 )
		ofs = 0

		iseaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
		isedxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDX )
		isecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )

		eaxindest = hIsRegInVreg( dvreg, EMIT_REG_EAX )
 		edxindest = hIsRegInVreg( dvreg, EMIT_REG_EDX )
 		ecxindest = hIsRegInVreg( dvreg, EMIT_REG_ECX )

		if( (svreg->typ <> IR_VREGTYPE_REG) or (svreg->reg <> EMIT_REG_ECX) ) then
			'' handle src < dword
			if( typeGetSize( svreg->dtype ) <> 4 ) then
 				'' if it's not a reg, the right size was already set at the hPrepOperand() above
 				if( svreg->typ = IR_VREGTYPE_REG ) then
 					src = *hGetRegName( FB_DATATYPE_INTEGER, svreg->reg )
 				end if
 			end if

 			if( isecxfree = FALSE ) then
 				if( ecxindest and dvreg->typ = IR_VREGTYPE_REG ) then
 					hMOV( "ecx", src )
 					isecxfree = TRUE
 				else
 					hPUSH( src )
 					outp "xchg ecx, [esp]"
 					ofs += 4
 				end if
 			else
 				hMOV( "ecx", src )
 			end if
 		else
 			isecxfree = TRUE
 		end if

		'' load dst1 to eax
 		if( eaxindest ) then
 			if( dvreg->typ <> IR_VREGTYPE_REG ) then
 				outp "xchg eax, [esp+" + str( ofs+0 ) + "]"
 			else
 				outp "mov eax, [esp+" + str( ofs+0 ) + "]"
 			end if
 		else
 			if( iseaxfree = FALSE ) then
 				outp "xchg eax, [esp+" + str( ofs+0 ) + "]"
 			else
 				outp "mov eax, [esp+" + str( ofs+0 ) + "]"
 			end if
 		end if

 		'' load dst2 to edx
 		if( edxindest ) then
 			if( dvreg->typ <> IR_VREGTYPE_REG ) then
 				outp "xchg edx, [esp+" + str( ofs+4 ) + "]"
 			else
 				outp "mov edx, [esp+" + str( ofs+4 ) + "]"
 			end if
 		else
 			if( isedxfree = FALSE ) then
 				outp "xchg edx, [esp+" + str( ofs+4 ) + "]"
 			else
 				outp "mov edx, [esp+" + str( ofs+4 ) + "]"
 			end if
 		end if

		if( op = AST_OP_SHL ) then
 			outp "shld edx, eax, cl"
 			outp mnemonic32 + " eax, cl"
 		else
 			outp "shrd eax, edx, cl"
 			outp mnemonic32 + " edx, cl"
 		end if

 		outp "test cl, 32"
 		hBRANCH( "jz", label )

 		if( op = AST_OP_SHL ) then
 			outp "mov edx, eax"
 			outp "xor eax, eax"
 		else
 			outp "mov eax, edx"
 			if( typeIsSigned( dvreg->dtype ) ) then
 				outp "sar edx, 31"
 			else
 				outp "xor edx, edx"
 			end if
 		end if

 		hLABEL( label )

 		if( isecxfree = FALSE ) then
 			hPOP "ecx"
 		end if

		'' save dst2
 		if( edxindest ) then
 			if( dvreg->typ <> IR_VREGTYPE_REG ) then
 				outp "xchg edx, [esp+4]"
 			else
 				outp "mov [esp+4], edx"
 			end if
 		else
 			if( isedxfree = FALSE ) then
 				outp "xchg edx, [esp+4]"
 			else
 				outp "mov [esp+4], edx"
 			end if
 		end if

 		'' save dst1
 		if( eaxindest ) then
 			if( dvreg->typ <> IR_VREGTYPE_REG ) then
 				outp "xchg eax, [esp+0]"
 			else
 				outp "mov [esp+0], eax"
 			end if
 		else
 			if( iseaxfree = FALSE ) then
 				outp "xchg eax, [esp+0]"
 			else
 				outp "mov [esp+0], eax"
 			end if
 		end if

		hPOP( dst1 )
		hPOP( dst2 )
	end if

end sub

'':::::
private sub hSHIFTI _
	( _
		byval op as integer, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim eaxpreserved as integer, ecxpreserved as integer
    dim eaxfree as integer, ecxfree as integer
    dim reg as integer
    dim ecxindest as integer
    dim as string ostr, dst, src, tmp, mnemonic

	''
	if( typeIsSigned( dvreg->dtype ) ) then
		if( op = AST_OP_SHL ) then
			mnemonic = "sal"
		else
			mnemonic = "sar"
		end if
	else
		if( op = AST_OP_SHL ) then
			mnemonic = "shl"
		else
			mnemonic = "shr"
		end if
	end if

    ''
	hPrepOperand( dvreg, dst )

	ecxindest = FALSE
	eaxpreserved = FALSE
	ecxpreserved = FALSE

	if( svreg->typ = IR_VREGTYPE_IMM ) then
		hPrepOperand( svreg, src )
		tmp = dst

	else
		eaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
		ecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )

		if( svreg->typ = IR_VREGTYPE_REG ) then
			reg = svreg->reg
		else
			reg = INVALID
		end if

        ecxindest = hIsRegInVreg( dvreg, EMIT_REG_ECX )

		'' ecx in destine?
		if( ecxindest ) then
			'' preserve
			hPUSH "ecx"
			'' not a reg?
			if( dvreg->typ <> IR_VREGTYPE_REG ) then
				hPrepOperand( dvreg, ostr, FB_DATATYPE_INTEGER )
				hPUSH ostr
			end if

		'' ecx not free?
		elseif( (reg <> EMIT_REG_ECX) and (ecxfree = FALSE) ) then
			ecxpreserved = TRUE
			hPUSH "ecx"
		end if

		'' source not a reg?
		if( svreg->typ <> IR_VREGTYPE_REG ) then
			hPrepOperand( svreg, ostr, FB_DATATYPE_BYTE )
			hMOV "cl", ostr
		else
			'' source not ecx?
			if( reg <> EMIT_REG_ECX ) then
				hMOV "ecx", rnameTB(dtypeTB(FB_DATATYPE_INTEGER).rnametb, reg)
			end if
		end if

		'' load ecx to a tmp?
		if( ecxindest ) then
			'' tmp not free?
			if( eaxfree = FALSE ) then
				eaxpreserved = TRUE
				outp "xchg eax, [esp]"		'' eax= dst; push eax
			else
				hPOP "eax"				'' eax= dst; pop tos
			end if

			tmp = rnameTB(dtypeTB(dvreg->dtype).rnametb, EMIT_REG_EAX )

		else
			tmp = dst
		end if

		src = "cl"

	end if

	ostr = mnemonic + " " + tmp + COMMA + src
	outp ostr

	if( ecxindest ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hPOP "ecx"
			if( eaxpreserved ) then
				outp "xchg ecx, [esp]"		'' ecx= tos; tos= eax
			end if
		end if
		hMOV dst, rnameTB(dtypeTB(dvreg->dtype).rnametb, EMIT_REG_EAX)
	end if

	if( eaxpreserved ) then
		hPOP "eax"
	end if

	if( ecxpreserved ) then
		hPOP "ecx"
	end if

end sub

'':::::
private sub _emitSHLL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hSHIFTL( AST_OP_SHL, dvreg, svreg )

end sub

'':::::
private sub _emitSHLI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hSHIFTI( AST_OP_SHL, dvreg, svreg )

end sub

'':::::
private sub _emitSHRL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hSHIFTL( AST_OP_SHR, dvreg, svreg )

end sub

'':::::
private sub _emitSHRI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hSHIFTI( AST_OP_SHR, dvreg, svreg )

end sub

'':::::
private sub _emitANDL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "and " + dst1 + COMMA + src1
	outp ostr

	ostr = "and " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitANDI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ostr = "and " + dst + COMMA + src
	outp ostr

end sub

'':::::
private sub _emitORL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "or " + dst1 + COMMA + src1
	outp ostr

	ostr = "or " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitORI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ostr = "or " + dst + COMMA + src
	outp ostr

end sub

'':::::
private sub _emitXORL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "xor " + dst1 + COMMA + src1
	outp ostr

	ostr = "xor " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitXORI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ostr = "xor " + dst + COMMA + src
	outp ostr

end sub

'':::::
private sub _emitEQVL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "xor " + dst1 + COMMA + src1
	outp ostr

	ostr = "xor " + dst2 + COMMA + src2
	outp ostr

	ostr = "not " + dst1
	outp ostr

	ostr = "not " + dst2
	outp ostr

end sub

'':::::
private sub _emitEQVI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ostr = "xor " + dst + COMMA + src
	outp ostr

	if( dvreg->dtype = FB_DATATYPE_BOOLEAN ) then
		ostr = "xor " + dst + COMMA + "1"
	else
		ostr = "not " + dst
	end if
	outp ostr

end sub

'':::::
private sub _emitIMPL _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string, src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	ostr = "not " + dst1
	outp ostr

	ostr = "not " + dst2
	outp ostr

	ostr = "or " + dst1 + COMMA + src1
	outp ostr

	ostr = "or " + dst2 + COMMA + src2
	outp ostr

end sub

'':::::
private sub _emitIMPI _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim dst as string, src as string
    dim ostr as string

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( dvreg->dtype = FB_DATATYPE_BOOLEAN ) then
		ostr = "xor " + dst + COMMA + "1"
	else
		ostr = "not " + dst
	end if
	outp ostr

	ostr = "or " + dst + COMMA + src
	outp ostr

end sub


'':::::
private sub _emitATN2 _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim src as string
    dim ostr as string

	hPrepOperand( svreg, src )

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		ostr = "fld " + src
		outp ostr
	else
		outp "fxch"
	end if
	outp "fpatan"

end sub

'':::::
private sub _emitPOW _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim src as string
	dim ostr as string

	hPrepOperand( svreg, src )

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		ostr = "fld " + src
		outp ostr
		outp "fxch"
	end if

	outp "fabs"
	outp "fyl2x"
	outp "fld st(0)"
	outp "frndint"
	outp "fsub st(1), st(0)"
	outp "fxch"
	outp "f2xm1"
	outp "fld1"
	outp "faddp"
	outp "fscale"
	outp "fstp st(1)"

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' relational
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub hCMPL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval mnemonic as zstring ptr, _
		byval rev_mnemonic as zstring ptr, _
		byval usg_mnemonic as zstring ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval isinverse as integer = FALSE _
	) static

    dim as string dst1, dst2, src1, src2, rname, ostr, lname, falselabel

	hPrepOperand64( dvreg, dst1, dst2 )
	hPrepOperand64( svreg, src1, src2 )

	if( label = NULL ) then
		lname = *symbUniqueLabel( )
	else
		lname = *symbGetMangledName( label )
	end if

	'' check high
	ostr = "cmp " + dst2 + COMMA + src2
	outp ostr

	falselabel = *symbUniqueLabel( )

	'' set the boolean result?
	if( rvreg <> NULL ) then
		hPrepOperand( rvreg, rname )
		hMOV( rname, "-1" )
	end if

	ostr = "j" + *mnemonic
	if( isinverse = FALSE ) then
		hBRANCH( ostr, lname )
	else
		hBRANCH( ostr, falselabel )
	end if

	if( len( *rev_mnemonic ) > 0 ) then
		ostr = "j" + *rev_mnemonic
		hBRANCH( ostr, falselabel )
	end if

	'' check low
	ostr = "cmp " + dst1 + COMMA + src1
	outp ostr

	ostr = "j" + *usg_mnemonic
	hBRANCH( ostr, lname )

	hLabel( falselabel )

	if( rvreg <> NULL ) then
		ostr = "xor " + rname + COMMA + rname
		outp ostr

		hLabel( lname )
	end if

end sub

'':::::
private sub hCMPI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval mnemonic as zstring ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

    dim as string rname, rname8, dst, src, ostr, lname
    dim as integer isedxfree, dotest

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( label = NULL ) then
		lname = *symbUniqueLabel( )
	else
		lname = *symbGetMangledName( label )
	end if

	'' optimize "cmp" to "test"
	dotest = FALSE
	if( (svreg->typ = IR_VREGTYPE_IMM) and (dvreg->typ = IR_VREGTYPE_REG) ) then
		if( svreg->value.i = 0 ) then
			dotest = TRUE
		end if
	end if

	if( dotest ) then
		ostr = "test " + dst + COMMA + dst
		outp ostr
	else
		ostr = "cmp " + dst + COMMA + src
		outp ostr
	end if

	'' no result to be set? just branch
	if( rvreg = NULL ) then
		ostr = "j" + *mnemonic
		hBRANCH( ostr, lname )
		exit sub
	end if

	hPrepOperand( rvreg, rname )

	'' can it be optimized?
	if( (env.clopt.cputype >= FB_CPUTYPE_486) and (rvreg->typ = IR_VREGTYPE_REG) ) then

		rname8 = *hGetRegName( FB_DATATYPE_BYTE, rvreg->reg )

		'' handle EDI and ESI
		if( (rvreg->reg = EMIT_REG_ESI) or (rvreg->reg = EMIT_REG_EDI) ) then

			isedxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDX )
			if( isedxfree = FALSE ) then
				ostr = "xchg edx, " + rname
				outp ostr
			end if

			ostr = "set" + *mnemonic + " dl"
			outp ostr

			if( isedxfree = FALSE ) then
				ostr = "xchg edx, " + rname
				outp ostr
			else
				hMOV rname, "edx"
			end if

		else
			ostr = "set" + *mnemonic + " " + rname8
			outp ostr
		end if

		if( rvreg->dtype <> FB_DATATYPE_BOOLEAN ) then 
			'' convert 1 to -1 (TRUE in QB/FB)
			ostr = "shr " + rname + ", 1"
			outp ostr

			ostr = "sbb " + rname + COMMA + rname
			outp ostr
		end if

	'' old (and slow) boolean set
	else

		if( rvreg->dtype = FB_DATATYPE_BOOLEAN ) then 
			ostr = "mov " + rname + ", 1"
		else
			ostr = "mov " + rname + ", -1"
		end if
		outp ostr

		ostr = "j" + *mnemonic
		hBRANCH( ostr, lname )

		ostr = "xor " + rname + COMMA + rname
		outp ostr

		hLabel( lname )
	end if

end sub


'':::::
private sub hCMPF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval mnemonic as zstring ptr, _
		byval mask as zstring ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim as string rname, rname8, dst, src, ostr, lname
    dim as integer iseaxfree, isedxfree

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	if( label = NULL ) then
		lname = *symbUniqueLabel( )
	else
		lname = *symbGetMangledName( label )
	end if

	'' do comp
	if( svreg->typ = IR_VREGTYPE_REG ) then
		outp "fcompp"
	else
		'' can it be optimized to ftst?
		if( typeGetClass( svreg->dtype ) = FB_DATACLASS_FPOINT ) then
			ostr = "fcomp " + src
			outp ostr
		else
			ostr = "ficomp " + src
			outp ostr
		end if
	end if

    iseaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
    if( rvreg <> NULL ) then
    	iseaxfree = (rvreg->reg = EMIT_REG_EAX)
	end if

    if( iseaxfree = FALSE ) then
    	hPUSH( "eax" )
    end if

    '' load fpu flags
    outp "fnstsw ax"
	if( len( *mask ) > 0 ) then
		ostr = "test ah, " + *mask
		outp ostr
	else
		outp "sahf"
	end if

	if( iseaxfree = FALSE ) then
		hPOP( "eax" )
	end if

    '' no result to be set? just branch
    if( rvreg = NULL ) then
    	ostr = "j" + *mnemonic
    	hBRANCH( ostr, lname )
    	exit sub
    end if

    hPrepOperand( rvreg, rname )

	'' can it be optimized?
	if( env.clopt.cputype >= FB_CPUTYPE_486 ) then
		rname8 = *hGetRegName( FB_DATATYPE_BYTE, rvreg->reg )

		'' handle EDI and ESI
		if( (rvreg->reg = EMIT_REG_ESI) or (rvreg->reg = EMIT_REG_EDI) ) then

			isedxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDX )
			if( isedxfree = FALSE ) then
				ostr = "xchg edx, " + rname
				outp ostr
			end if

			ostr = "set" + *mnemonic + (TABCHAR + "dl")
			outp ostr

			if( isedxfree = FALSE ) then
				ostr = "xchg edx, " + rname
				outp ostr
			else
				hMOV rname, "edx"
			end if
		else
			ostr = "set" + *mnemonic + " " + rname8
			outp ostr
		end if

		'' convert 1 to -1 (TRUE in QB/FB)
		ostr = "shr " + rname + ", 1"
		outp ostr

		ostr = "sbb " + rname + COMMA + rname
		outp ostr

	'' old (and slow) boolean set
	else
    	ostr = "mov " + rname + ", -1"
    	outp ostr

    	ostr = "j" + *mnemonic
    	hBRANCH( ostr, lname )

		ostr = "xor " + rname + COMMA + rname
		outp ostr

		hLabel( lname )
	end if

end sub

'':::::
private sub _emitCGTL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string, rjmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "g"
		rjmp = "l"
	else
		jmp = "a"
		rjmp = "b"
	end if

	hCMPL( rvreg, label, jmp, rjmp, "a", dvreg, svreg )

end sub

'':::::
private sub _emitCGTI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "g"
	else
		jmp = "a"
	end if

	hCMPI( rvreg, label, jmp, dvreg, svreg )

end sub



'':::::
private sub _emitCGTF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPF( rvreg, label, "z", "0b01000001", dvreg, svreg )

end sub

'':::::
private sub _emitCLTL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string, rjmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "l"
		rjmp = "g"
	else
		jmp = "b"
		rjmp = "a"
	end if

	hCMPL( rvreg, label, jmp, rjmp, "b", dvreg, svreg )

end sub

'':::::
private sub _emitCLTI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "l"
	else
		jmp = "b"
	end if

	hCMPI( rvreg, label, jmp, dvreg, svreg )

end sub


'':::::
private sub _emitCLTF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPF( rvreg, label, "nz", "0b00000001", dvreg, svreg )

end sub

'':::::
private sub _emitCEQL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPL( rvreg, label, "ne", "", "e", dvreg, svreg, TRUE )

end sub

'':::::
private sub _emitCEQI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPI( rvreg, label, "e", dvreg, svreg )

end sub


'':::::
private sub _emitCEQF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPF( rvreg, label, "nz", "0b01000000", dvreg, svreg )

end sub

'':::::
private sub _emitCNEL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPL( rvreg, label, "ne", "", "ne", dvreg, svreg )

end sub

'':::::
private sub _emitCNEI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPI( rvreg, label, "ne", dvreg, svreg )

end sub


'':::::
private sub _emitCNEF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPF( rvreg, label, "z", "0b01000000", dvreg, svreg )

end sub

'':::::
private sub _emitCLEL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string, rjmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "l"
		rjmp = "g"
	else
		jmp = "b"
		rjmp = "a"
	end if

	hCMPL( rvreg, label, jmp, rjmp, "be", dvreg, svreg )

end sub

'':::::
private sub _emitCLEI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "le"
	else
		jmp = "be"
	end if

	hCMPI( rvreg, label, jmp, dvreg, svreg )

end sub


'':::::
private sub _emitCLEF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPF( rvreg, label, "nz", "0b01000001", dvreg, svreg )

end sub


'':::::
private sub _emitCGEL _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string, rjmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "g"
		rjmp = "l"
	else
		jmp = "a"
		rjmp = "b"
	end if

	hCMPL( rvreg, label, jmp, rjmp, "ae", dvreg, svreg )

end sub

'':::::
private sub _emitCGEI _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim jmp as string

	if( typeIsSigned( dvreg->dtype ) ) then
		jmp = "ge"
	else
		jmp = "ae"
	end if

	hCMPI( rvreg, label, jmp, dvreg, svreg )

end sub


'':::::
private sub _emitCGEF _
	( _
		byval rvreg as IRVREG ptr, _
		byval label as FBSYMBOL ptr, _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	hCMPF( rvreg, label, "ae", "", dvreg, svreg )

end sub


''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' unary ops
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub _emitNEGL _
	( _
		byval dvreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )

	ostr = "neg " + dst1
	outp ostr

	ostr = "adc " + dst2 + ", 0"
	outp ostr

	ostr = "neg " + dst2
	outp ostr

end sub

'':::::
private sub _emitNEGI _
	( _
		byval dvreg as IRVREG ptr _
	) static

    dim dst as string
    dim ostr as string

	hPrepOperand( dvreg, dst )

	ostr = "neg " + dst
	outp ostr

end sub


'':::::
private sub _emitNEGF _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fchs"

end sub

'':::::
private sub _emitNOTL _
	( _
		byval dvreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )

	ostr = "not " + dst1
	outp ostr

	ostr = "not " + dst2
	outp ostr

end sub

'':::::
private sub _emitNOTI _
	( _
		byval dvreg as IRVREG ptr _
	) static

    dim dst as string
    dim ostr as string

	hPrepOperand( dvreg, dst )

	if( dvreg->dtype = FB_DATATYPE_BOOLEAN ) then
		ostr = "xor " + dst + COMMA + "1"

	else
		ostr = "not " + dst
	end if
	
	outp ostr

end sub

'':::::
private sub _emitABSL _
	( _
		byval dvreg as IRVREG ptr _
	) static

    dim dst1 as string, dst2 as string
    dim reg as integer, isfree as integer, rname as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )

	reg = hFindRegNotInVreg( dvreg )
	rname = *hGetRegName( FB_DATATYPE_INTEGER, reg )

	isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

	if( isfree = FALSE ) then
		hPUSH( rname )
	end if

	hMOV( rname, dst2 )

	ostr = "sar " + rname + ", 31"
	outp ostr

	ostr = "xor " + dst1 + COMMA + rname
	outp ostr

	ostr = "xor " + dst2 + COMMA + rname
	outp ostr

	ostr = "sub " + dst1 + COMMA + rname
	outp ostr

	ostr = "sbb " + dst2 + COMMA + rname
	outp ostr

	if( isfree = FALSE ) then
		hPOP( rname )
	end if

end sub

'':::::
private sub _emitABSI _
	( _
		byval dvreg as IRVREG ptr _
	) static

    dim dst as string
    dim reg as integer, isfree as integer, rname as string, bits as integer
    dim ostr as string

	hPrepOperand( dvreg, dst )

	reg = hFindRegNotInVreg( dvreg )
	rname = *hGetRegName( dvreg->dtype, reg )

	isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

	if( isfree = FALSE ) then
		hPUSH( rname )
	end if

	bits = typeGetBits( dvreg->dtype ) - 1

	hMOV( rname, dst )

	ostr = "sar " + rname + COMMA + str( bits )
	outp ostr

	ostr = "xor " + dst + COMMA + rname
	outp ostr

	ostr = "sub " + dst + COMMA + rname
	outp ostr

	if( isfree = FALSE ) then
		hPOP( rname )
	end if

end sub


'':::::
private sub _emitABSF _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fabs"

end sub

''
'' Algorithm:
''	select case( highdword )
''	case is < 0
''		highdword = -1
''		lowdword  = -1
''	case is > 0
''		highdword = 0
''		lowdword  = 1
''	case else
''		if( lowdword <> 0 ) then
''			highdword = 0
''			lowdword  = 1
''		else
''			highdword = 0
''			lowdword  = 0
''		end if
''	end select
''
'' - Result is stored into the input registers, so must be careful
''   about overwriting
''
'' - For 1/-1 it's enough to check the high dword only, but to determine
''   whether the LONGINT is 0, both dwords must be checked
''
'' - Should have as few jumps/assignments as possible
''
private sub _emitSGNL( byval dvreg as IRVREG ptr )
	dim as string low, high, exitlabel, tmp32
	dim as integer tmpreg = any, istmpfree = any

	hPrepOperand64( dvreg, low, high )
	exitlabel = *symbUniqueLabel( )
	tmpreg = hFindRegNotInVreg( dvreg )
	istmpfree = hIsRegFree( FB_DATACLASS_INTEGER, tmpreg )
	tmp32 = *hGetRegName( FB_DATATYPE_INTEGER, tmpreg )

	if( istmpfree = FALSE ) then
		hPUSH( tmp32 )
	end if

	hMOV( tmp32, low )              '' tmp = low (backup the low dword)
	outp( "cmp " + high + ", 0" )   '' select case high
	hMOV( low , "-1" )              '' low  = -1
	hMOV( high, "-1" )              '' high = -1
	hBRANCH( "jl", exitlabel )      '' case is < 0 goto exit
	hMOV( low , "1" )               '' low  = 1
	hMOV( high, "0" )               '' high = 0
	hBRANCH( "jg", exitlabel )      '' case is > 0 goto exit
	outp( "cmp " + tmp32 + ", 0" )  '' select case tmp (high = 0, but must check original low dword too)
	'hMOV( low , "1" )              '' (low  = 1)
	'hMOV( high, "0" )              '' (high = 0)
	hBRANCH( "jne", exitlabel )     '' case is <> 0 goto exit
	hMOV( low , "0" )               '' low  = 0
	'hMOV( high, "0" )              '' (high = 0)
	hLABEL( exitlabel )             '' exit:

	if( istmpfree = FALSE ) then
		hPOP( tmp32 )
	end if
end sub

private sub _emitSGNI( byval dvreg as IRVREG ptr )
	dim as string dst, exitlabel

	hPrepOperand( dvreg, dst )
	exitlabel = *symbUniqueLabel( )

	outp( "cmp " + dst + ", 0" )  '' select case x
	hBRANCH( "je", exitlabel )    '' case 0 goto exit (x = 0)
	hMOV( dst, "1" )              '' x = 1
	hBRANCH( "jg", exitlabel )    '' case is > 0 goto exit
	hMOV( dst, "-1" )             '' x = -1
	hLABEL( exitlabel )           '' exit:
end sub

private sub _emitSGNF( byval dvreg as IRVREG ptr )
	dim as string dst, label
	dim as integer iseaxfree = any

	hPrepOperand( dvreg, dst )

	label = *symbUniqueLabel( )

    iseaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )

    if( iseaxfree = FALSE ) then
    	hPUSH( "eax" )
    end if

	outp "ftst"
	outp "fnstsw ax"
	outp "sahf"

	if( iseaxfree = FALSE ) then
		hPOP( "eax" )
	end if

	'' if dst = 0
	hBRANCH( "jz", label )
	'' elseif dst > 0
	outp "fstp st(0)"
	outp "fld1"
	hBRANCH( "ja", label )
	'' else
	outp "fchs"

	hLABEL( label )
end sub

'':::::
private sub _emitSIN _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fsin"

end sub

'':::::
private sub _emitASIN _
	( _
		byval dvreg as IRVREG ptr _
	) static

	'' asin( x ) = atn( sqr( (x*x) / (1-x*x) ) )
	outp "fld st(0)"
    outp "fmul st(0), st(0)"
    outp "fld1"
	outp "fsubrp"
	outp "fsqrt"
	outp "fpatan"

end sub

'':::::
private sub _emitCOS _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fcos"

end sub

'':::::
private sub _emitACOS _
	( _
		byval dvreg as IRVREG ptr _
	) static

	'' acos( x ) = atn( sqr( (1-x*x) / (x*x) ) )
	outp "fld st(0)"
    outp "fmul st(0), st(0)"
    outp "fld1"
	outp "fsubrp"
	outp "fsqrt"
	outp "fxch"
	outp "fpatan"

end sub

'':::::
private sub _emitTAN _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fptan"
	outp "fstp st(0)"

end sub

'':::::
private sub _emitATAN _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fld1"
	outp "fpatan"

end sub

'':::::
private sub _emitSQRT _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fsqrt"

end sub

'':::::
private sub _emitLOG _
	( _
		byval dvreg as IRVREG ptr _
	) static

	'' log( x ) = log2( x ) / log2( e ).

    outp "fldln2"
    outp "fxch"
    outp "fyl2x"

end sub

'':::::
private sub _emitEXP _
	( _
		byval dvreg as IRVREG ptr _
	) static

	outp "fldl2e"
	outp "fmulp st(1), st"
	outp "fld st"
    outp "frndint"
	outp "fsub st(1), st"
	outp "fxch"
	outp "f2xm1"
	'' can't use fld1 because max 2 fp regs can be used
	hPUSH( "0x3f800000" )
	outp "fadd dword ptr [esp]"
	outp "add esp, 4"
	outp "fscale"
	outp "fstp st(1)"
end sub

private sub hFpuChangeRC( byref regname as string, byval mode as zstring ptr )
	outp( "sub esp, 4" )
	outp( "fnstcw [esp]" )
	hMOV( regname, "[esp]" )
	if( *mode <> "11" ) then
		outp( "and " + regname + ", 0b1111001111111111" )
	end if
	outp( "or " + regname +  (", 0b0000" + *mode + "0000000000") )
	hPUSH( regname )
	outp( "fldcw [esp]" )
	outp( "add esp, 4" )
end sub

private sub hEmitFloatFunc( byval func as integer )
	dim as integer reg = any, isregfree = any
	dim as string regname

	reg = hFindFreeReg( FB_DATACLASS_INTEGER )
	if( reg = INVALID ) then
		reg = EMIT_REG_EAX
		isregfree = FALSE
	else
		isregfree = TRUE
	end if

	regname = *hGetRegName( FB_DATATYPE_INTEGER, reg )

	if( isregfree = FALSE ) then
		hPUSH( regname )
	end if

	select case( func )
	case 1
		'' st(0) = floor( st(0) )
		'' round down toward -infinity
		hFpuChangeRC( regname, "01" )
		outp( "frndint" )
	case 2
		'' st(0) = fix( st(0) ) = floor( abs( st(0) ) ) * sng( st(0) )
		'' chop truncating toward 0
		hFpuChangeRC( regname, "11" )
		outp( "frndint" )
	case 3
		'' st(0) = st(0) - fix( st(0) )
		'' chop truncating toward 0
		hFpuChangeRC( regname, "11" )
		outp( "fld st(0)" )
		outp( "frndint" )
		outp( "fsubp" )
	end select

	'' restore FPU rounding
	outp( "fldcw [esp]" )
	outp( "add esp, 4" )

	if( isregfree = FALSE ) then
		hPOP( regname )
	end if
end sub

private sub _emitFLOOR( byval dvreg as IRVREG ptr )
	hEmitFloatFunc( 1 )
end sub

private sub _emitFIX( byval dvreg as IRVREG ptr )
	hEmitFloatFunc( 2 )
end sub

private sub _emitFRAC( byval dvreg as IRVREG ptr )
	hEmitFloatFunc( 3 )
end sub

private sub _emitCONVFD2FS( byval dvreg as IRVREG ptr )
	assert( dvreg->typ = IR_VREGTYPE_REG )
	assert( dvreg->regFamily = IR_REG_FPU_STACK )

	'' fld stores into st(0) but doesn't convert from
	'' qword to dword, a dword temp var must be used,
	'' in order to get the truncation
	outp( "sub esp, 4" )
	outp( "fstp dword ptr [esp]" )
	outp( "fld dword ptr [esp]" )
	outp( "add esp, 4" )
end sub

'':::::
private sub _emitXchgTOS _
	( _
		byval svreg as IRVREG ptr _
	) static

    dim as string src
    dim as string ostr

	hPrepOperand( svreg, src )

	ostr = "fxch " + src
	outp( ostr )

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' stack
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

private sub _emitSTACKALIGN( byval vreg as IRVREG ptr, byval unused as integer )
	if( vreg->value.i > 0 ) then
		outp( "sub esp, " + str( vreg->value.i ) )
	else
		outp( "add esp, " + str( -vreg->value.i ) )
	end if
end sub

'':::::
private sub _emitPUSHL _
	( _
		byval svreg as IRVREG ptr, _
		byval unused as integer _
	) static

    dim src1 as string, src2 as string
    dim ostr as string

	hPrepOperand64( svreg, src1, src2 )

	ostr = "push " + src2
	outp ostr

	ostr = "push " + src1
	outp ostr

end sub

private sub _emitPUSHI( byval svreg as IRVREG ptr, byval unused as integer )
	dim as string src, tmp32
	dim as integer sdsize = any, tmpreg = any, istmpfree = any

	hPrepOperand( svreg, src )

	sdsize = typeGetSize( svreg->dtype )

	'' PUSH only supports 4-byte operands, if it's smaller we need to
	'' work-around/load into 4-byte location first.

	select case( svreg->typ )
	case IR_VREGTYPE_REG
		if( sdsize < 4 ) then
			'' Use eax instead of al etc., this can pull in "random"
			'' values from the unused part of the register,
			'' but should be ok.
			src = *hGetRegName( FB_DATATYPE_INTEGER, svreg->reg )
		end if
		outp( "push " + src )

	case IR_VREGTYPE_IMM
		outp( "push " + src )

	case else
		if( sdsize < 4 ) then
			'' Load into 4-byte reg first - it's not safe to assume
			'' we can just use DWORD PTR instead of BYTE PTR or
			'' WORD PTR (possible buffer overrun).

			tmpreg = hFindRegNotInVreg( svreg )
			istmpfree = hIsRegFree( FB_DATACLASS_INTEGER, tmpreg )
			tmp32 = *hGetRegName( FB_DATATYPE_INTEGER, tmpreg )

			if( istmpfree = FALSE ) then
				hPUSH( tmp32 )
			end if

			'' mov tmp, [src]
			'' (zero-extending, because e.g. &hFF should become
			'' &h000000FF, and not &hFFFFFFFF)
			outp( "movzx " + tmp32 + ", " + src )

			'' push tmp
			outp( "push " + tmp32 )

			if( istmpfree = FALSE ) then
				hPOP( tmp32 )
			end if
		else
			assert( sdsize = 4 )
			outp( "push " + src )
		end if
	end select

end sub

'':::::
private sub _emitPUSHF _
	( _
		byval svreg as IRVREG ptr, _
		byval unused as integer _
	) static

    dim src as string, sdsize as integer
    dim ostr as string

	hPrepOperand( svreg, src )

	sdsize = typeGetSize( svreg->dtype )

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		if( svreg->dtype = FB_DATATYPE_SINGLE ) then
			ostr = "push " + src
			outp ostr
		else
			hPrepOperand( svreg, src, FB_DATATYPE_INTEGER, 4 )
			ostr = "push " + src
			outp ostr

    		hPrepOperand( svreg, src, FB_DATATYPE_INTEGER, 0 )
			ostr = "push " + src
			outp ostr
		end if
	else
		ostr = "sub esp," + str( sdsize )
		outp ostr

		ostr = "fstp " + dtypeTB(svreg->dtype).mname + " [esp]"
		outp ostr
	end if

end sub

private sub _emitPUSHUDT( byval svreg as IRVREG ptr, byval sdsize as integer )
	dim as string src, tmp32, tmp16
	dim as integer ofs = any, tmpreg = any, istmpfree = any, remainder = any

	'' The UDT should be pushed byte-by-byte, it must end up in the same
	'' order on stack as it is originally layed out in memory.
	''
	'' For example, this sequence of bytes in memory (the UDT to push):
	''    &hAA &hBB &hCC &hDD &hEE &hFF
	''
	'' It's 6 bytes, i.e. not a multiple of 8, i.e. there is a remainder:
	''                        &hEE &hFF
	'' The two bytes are read with a WORD PTR, zero extended, then pushed:
	''    offset = sdsize - 2
	''    &h0000FFEE <- word ptr [svreg + offset]
	''    push &h0000FFEE
	'' producing on stack:
	''                        &hEE &hFF &h00 &h00
	''
	'' Then there are 4 bytes left to handle:
	''    &hAA &hBB &hCC &hDD
	'' They're read out using a DWORD PTR, then pushed:
	''    offset = 0
	''    push dword ptr [svreg + offset]
	'' producing on stack:
	''    &hAA &hBB &hCC &hDD &hEE &hFF &h00 &h00
	''
	'' which is the desired "copy".

	'' Push remainder (last 1/2/3 bytes of the struct, located in memory
	'' at src + length - N)
	remainder = sdsize and (4-1)
	if( remainder > 0 ) then
		'' Load into 4-byte reg first - it's not safe to assume
		'' we can just use DWORD PTR instead of BYTE PTR or
		'' WORD PTR (possible buffer overrun).

		tmpreg = hFindRegNotInVreg( svreg )
		istmpfree = hIsRegFree( FB_DATACLASS_INTEGER, tmpreg )
		tmp32 = *hGetRegName( FB_DATATYPE_INTEGER, tmpreg )

		if( istmpfree = FALSE ) then
			hPUSH( tmp32 )
		end if

		select case( remainder )
		case 3
			'' 3-byte remainder:
			''        &h11 &h22 &h33
			'' It's probably best to access them as &h2211 WORD
			'' and &h33 BYTE. &h11 has a good chance of having
			'' 4-byte alignment, since the whole UDT will typically
			'' start at 4-byte boundary; at least for stack vars...

			'' 1. load 3rd byte:
			''        &h00000033 <- byte ptr [src + length - 1]
			hPrepOperand( svreg, src, FB_DATATYPE_BYTE, sdsize - 1 )
			outp( "movzx " + tmp32 + ", " + src )

			'' 2. shl
			''        &h00330000 <- &h00000033 shl 16
			outp( "shl " + tmp32 + ", 16" )

			'' 3. load first two bytes into the lower 16 bits
			''    of the register:
			''        &h00002211 <- word ptr [src + length - 3]
			''        &h00332211 <- &h00330000, &h00002211
			tmp16 = *hGetRegName( FB_DATATYPE_SHORT, tmpreg )
			hPrepOperand( svreg, src, FB_DATATYPE_SHORT, sdsize - 3 )
			outp( "mov " + tmp16 + ", " + src )

			'' 4. push
			''        push &h00332211
			'' producing on stack:
			''        &h11 &h22 &h33 &h00

		case 2
			'' mov tmp, word ptr [src + length - 2]
			'' (zero-extending, because e.g. &hFFFF should become
			'' &h0000FFFF, and not &hFFFFFFFF)
			ofs = sdsize - 2
			hPrepOperand( svreg, src, FB_DATATYPE_SHORT, ofs )
			outp( "movzx " + tmp32 + ", " + src )

		case 1
			'' mov tmp, byte ptr [src + length - 1]
			'' (zero-extending, ditto)
			ofs = sdsize - 1
			hPrepOperand( svreg, src, FB_DATATYPE_BYTE, ofs )
			outp( "movzx " + tmp32 + ", " + src )

		end select

		'' push tmp
		'' &h0000FFFF becomes &hFF &hFF &h00 &h00 on stack
		outp( "push " + tmp32 )

		if( istmpfree = FALSE ) then
			hPOP( tmp32 )
		end if

		sdsize -= remainder
	end if

	'' Push whole dwords, backwards (from high address to low address,
	'' since the stack grows downwards)
	ofs = sdsize - 4
	while( ofs >= 0 )
		hPrepOperand( svreg, src, FB_DATATYPE_INTEGER, ofs )
		outp( "push " + src )
		ofs -= 4
	wend

end sub

'':::::
private sub _emitPOPL _
	( _
		byval dvreg as IRVREG ptr, _
		byval unused as integer _
	) static

    dim dst1 as string, dst2 as string
    dim ostr as string

	hPrepOperand64( dvreg, dst1, dst2 )

	ostr = "pop " + dst1
	outp ostr

	ostr = "pop " + dst2
	outp ostr

end sub

'':::::
private sub _emitPOPI _
	( _
		byval dvreg as IRVREG ptr, _
		byval unused as integer _
	) static

    dim as string dst, ostr
    dim as integer dsize

	hPrepOperand( dvreg, dst )

	dsize = typeGetSize( dvreg->dtype )

	if( dvreg->typ = IR_VREGTYPE_IMM ) then
		'' gosub quirk: return-to-label needs to pop return address from the stack
		'' see ast-gosub.bas::astGosubAddReturn() - (jeffm)

		if( dvreg->value.i = 4 ) then
			if( hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX ) ) then
				hPOP "eax"
			else
				outp "add esp, 4"
			end if
		else
			ostr = "add esp, " + str( dvreg->value.i )
			outp ostr
		end if

	elseif( dsize = 4 ) then
		'' POP 4 bytes directly, no need for intermediate code
		ostr = "pop " + dst
		outp ostr

	else
		if( dvreg->typ = IR_VREGTYPE_REG ) then
			dst = *hGetRegName( FB_DATATYPE_INTEGER, dvreg->reg )
			ostr = "pop " + dst
			outp ostr
		else
			dim as integer reg, isfree
			dim as string aux8, aux16, aux32

			assert( (dsize = 1) or (dsize = 2) )

			reg = hFindRegNotInVreg( dvreg )

			aux8  = *hGetRegName( FB_DATATYPE_BYTE, reg )
			aux16 = *hGetRegName( FB_DATATYPE_SHORT, reg )
			aux32 = *hGetRegName( FB_DATATYPE_INTEGER, reg )

			isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

			if( isfree ) then
				'' reg is free, overwrite with value from stack
				hMOV aux32, "dword ptr [esp]"
			else
				'' reg is used, swap it with value from stack,
				'' so the reg can be used with the new value,
				'' while its old value is preserved and
				'' restored later during the pop.
				hXCHG aux32, "dword ptr [esp]"
			end if

			'' Extract the wanted byte/short and store it
			if( dsize = 1 ) then
				hMOV dst, aux8
			else
				hMOV dst, aux16
			end if

			if( isfree ) then
				'' pop
				outp "add esp, 4"
			else
				'' pop and restore the preserved reg value
				hPOP aux32
			end if
		end if

	end if

end sub


'':::::
private sub _emitPOPF _
	( _
		byval dvreg as IRVREG ptr, _
		byval unused as integer _
	) static

    dim as string dst, ostr
    dim as integer dsize

	hPrepOperand( dvreg, dst )

	dsize = typeGetSize( dvreg->dtype )

	if( dvreg->typ <> IR_VREGTYPE_REG ) then
		if( dvreg->dtype = FB_DATATYPE_SINGLE ) then
			ostr = "pop " + dst
			outp ostr
		else
			hPrepOperand( dvreg, dst, FB_DATATYPE_INTEGER, 0 )
			ostr = "pop " + dst
			outp ostr

			hPrepOperand( dvreg, dst, FB_DATATYPE_INTEGER, 4 )
			ostr = "pop " + dst
			outp ostr
		end if
	else
		ostr = "fld " + dtypeTB(dvreg->dtype).mname + " [esp]"
		outp ostr

		ostr = "add esp," + str( dsize )
		outp ostr
	end if

end sub

private sub _emitPOPST0( )
	outp( "fstp st(0)" )
end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' addressing
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub _emitADDROF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim as string dst, src
	dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src, , , , FALSE )

	ostr = "lea " + dst + ", " + src
	outp ostr

end sub

'':::::
private sub _emitDEREF _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr _
	) static

	dim as string dst, src
	dim as string ostr

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src, FB_DATATYPE_UINT )

	ostr = "mov " + dst + COMMA + src
	outp ostr

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' memory
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub hMemMoveRep _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval bytes as integer _
	) static

	dim as string dst, src
	dim as string ostr
	dim as integer ecxfree, edifree, esifree
	dim as integer ediinsrc, ecxinsrc

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )
	edifree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDI )
	esifree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ESI )

	ediinsrc = hIsRegInVreg( svreg, EMIT_REG_EDI )
	ecxinsrc = hIsRegInVreg( svreg, EMIT_REG_ECX )

	if( ecxfree = FALSE ) then
		hPUSH( "ecx" )
	end if
	if( edifree = FALSE ) then
		hPUSH( "edi" )
	end if
	if( esifree = FALSE ) then
		hPUSH( "esi" )
	end if

	if( ediinsrc = FALSE ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hMOV( "edi", dst )
		else
			'' not esi already?
			if( dvreg->reg <> EMIT_REG_EDI ) then
           		hMOV( "edi", dst )
			end if
		end if

	else
		if( ecxinsrc ) then
			hPUSH( "ecx" )
		end if

		hMOV( "ecx", dst )

		if( ecxinsrc ) then
			outp "xchg ecx, [esp]"
		end if
	end if

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		hMOV( "esi", src )
	else
		'' not esi already?
		if( svreg->reg <> EMIT_REG_ESI ) then
           	hMOV( "esi", src )
		end if
	end if

	if( ediinsrc ) then
		if( ecxinsrc = FALSE ) then
			hMOV( "edi", "ecx" )
		else
			hPOP( "edi" )
		end if
	end if

	if( bytes > 4 ) then
		ostr = "mov ecx, " + str( cunsg(bytes) \ 4 )
		outp ostr
		outp "rep movsd"

	elseif( bytes = 4 ) then
		outp "mov ecx, [esi]"
		outp "mov [edi], ecx"
		if( (bytes and 3) > 0 ) then
			outp "add esi, 4"
			outp "add edi, 4"
		end if
	end if

		bytes and= 3
	if( bytes > 0 ) then
		if( bytes >= 2 ) then
			outp "mov cx, [esi]"
			outp "mov [edi], cx"
			if( bytes = 3 ) then
				outp "add esi, 2"
				outp "add edi, 2"
			end if
		end if

		if( (bytes and 1) <> 0 ) then
			outp "mov cl, [esi]"
			outp "mov [edi], cl"
		end if
	end if

	if( esifree = FALSE ) then
		hPOP( "esi" )
	end if
	if( edifree = FALSE ) then
		hPOP( "edi" )
	end if
	if( ecxfree = FALSE ) then
		hPOP( "ecx" )
	end if

end sub

'':::::
private sub hMemMoveBlk _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval bytes as integer _
	) static

	dim as string dst, src, aux
	dim as integer i, ofs, reg, isfree

	reg = hFindRegNotInVreg( dvreg )

	'' no free regs left?
	if( hIsRegInVreg( svreg, reg ) ) then
		hMemMoveRep( dvreg, svreg, bytes )
		exit sub
	end if

	aux = *hGetRegName( FB_DATATYPE_INTEGER, reg )

	isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )
	if( isfree = FALSE ) then
		hPUSH( aux )
	end if

	ofs = 0
	'' move dwords
	for i = 1 to cunsg(bytes) \ 4
		hPrepOperand( svreg, src, FB_DATATYPE_INTEGER, ofs )
		hMOV( aux, src )
		hPrepOperand( dvreg, dst, FB_DATATYPE_INTEGER, ofs )
		hMOV( dst, aux )
		ofs += 4
	next

	'' a word left?
	if( (bytes and 2) <> 0 ) then
		aux = *hGetRegName( FB_DATATYPE_SHORT, reg )
		hPrepOperand( svreg, src, FB_DATATYPE_SHORT, ofs )
		hMOV( aux, src )
		hPrepOperand( dvreg, dst, FB_DATATYPE_SHORT, ofs )
		hMOV( dst, aux )
		ofs += 2
	end if

	'' a byte left?
	if( (bytes and 1) <> 0 ) then
		aux = *hGetRegName( FB_DATATYPE_BYTE, reg )
		hPrepOperand( svreg, src, FB_DATATYPE_BYTE, ofs )
		hMOV( aux, src )
		hPrepOperand( dvreg, dst, FB_DATATYPE_BYTE, ofs )
		hMOV( dst, aux )
	end if

	if( isfree = FALSE ) then
		hPOP( aux )
	end if

end sub

'':::::
private sub _emitMEMMOVE _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval bytes as integer, _
		byval extra as integer _
	) static

	'' handle the assumption done at ast-node-mem::newMEM()
	if( culng( bytes ) > EMIT_MEMBLOCK_MAXLEN ) then
		hMemMoveRep( dvreg, svreg, bytes )
	else
		hMemMoveBlk( dvreg, svreg, bytes )
	end if

end sub

'':::::
private sub _emitMEMSWAP _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval bytes as integer, _
		byval extra as integer _
	) static

	'' implemented as function

end sub

'':::::
private sub hMemClearRepIMM _
	( _
		byval dvreg as IRVREG ptr, _
		byval bytes as ulong _
	) static

	dim as string dst
	dim as string ostr
	dim as integer eaxfree, ecxfree, edifree

	hPrepOperand( dvreg, dst )

	eaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
	ecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )
	edifree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDI )

	if( eaxfree = FALSE ) then
		hPUSH( "eax" )
	end if
	if( ecxfree = FALSE ) then
		hPUSH( "ecx" )
	end if
	if( edifree = FALSE ) then
		hPUSH( "edi" )
	end if

	if( dvreg->typ <> IR_VREGTYPE_REG ) then
		hMOV( "edi", dst )
	else
		'' not edi already?
		if( dvreg->reg <> EMIT_REG_EDI ) then
           	hMOV( "edi", dst )
        end if
	end if

	outp "xor eax, eax"

	if( bytes > 4 ) then
		ostr = "mov ecx, " + str( bytes \ 4 )
		outp ostr
		outp "rep stosd"

	elseif( bytes = 4 ) then
		outp "mov dword ptr [edi], eax"
		if( (bytes and 3) > 0 ) then
			outp "add edi, 4"
		end if
	end if

	bytes and= 3
	if( bytes > 0 ) then
		if( bytes >= 2 ) then
			outp "mov word ptr [edi], ax"
			if( bytes = 3 ) then
				outp "add edi, 2"
			end if
		end if

		if( (bytes and 1) <> 0 ) then
			outp "mov byte ptr [edi], al"
		end if
	end if

	if( edifree = FALSE ) then
		hPOP( "edi" )
	end if
	if( ecxfree = FALSE ) then
		hPOP( "ecx" )
	end if
	if( eaxfree = FALSE ) then
		hPOP( "eax" )
	end if

end sub

'':::::
private sub hMemClearBlkIMM _
	( _
		byval dvreg as IRVREG ptr, _
		byval bytes as ulong _
	) static

	dim as string dst
	dim as integer i, ofs

	ofs = 0
	'' move dwords
	for i = 1 to bytes \ 4
		hPrepOperand( dvreg, dst, FB_DATATYPE_INTEGER, ofs )
		hMOV( dst, "0" )
		ofs += 4
	next

	'' a word left?
	if( (bytes and 2) <> 0 ) then
		hPrepOperand( dvreg, dst, FB_DATATYPE_SHORT, ofs )
		hMOV( dst, "0" )
		ofs += 2
	end if

	'' a byte left?
	if( (bytes and 1) <> 0 ) then
		hPrepOperand( dvreg, dst, FB_DATATYPE_BYTE, ofs )
		hMOV( dst, "0" )
	end if

end sub

'':::::
private sub hMemClear _
	( _
		byval dvreg as IRVREG ptr, _
		byval bytes_vreg as IRVREG ptr _
	) static

	dim as string dst, bytes
	dim as string ostr
	dim as integer eaxfree, ecxfree, edifree

	hPrepOperand( dvreg, dst )
	hPrepOperand( bytes_vreg, bytes )

	eaxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
	ecxfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_ECX )
	edifree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EDI )

	if( eaxfree = FALSE ) then
		hPUSH( "eax" )
	end if
	if( ecxfree = FALSE ) then
		hPUSH( "ecx" )
	end if
	if( edifree = FALSE ) then
		hPUSH( "edi" )
	end if

	if( hIsRegInVreg( bytes_vreg, EMIT_REG_EDI ) = FALSE ) then
		if( dvreg->typ <> IR_VREGTYPE_REG ) then
			hMOV( "edi", dst )
		else
			'' not edi already?
			if( dvreg->reg <> EMIT_REG_EDI ) then
            	hMOV( "edi", dst )
            end if
		end if

		if( bytes_vreg->typ <> IR_VREGTYPE_REG ) then
			hMOV( "ecx", bytes )
		else
			'' not ecx already?
			if( bytes_vreg->reg <> EMIT_REG_ECX ) then
            	hMOV( "ecx", bytes )
            end if
		end if

	else
		hPUSH( bytes )

		ostr = "lea edi, " + dst
		outp ostr

		hPOP( "ecx" )
	end if

	outp "xor eax, eax"

	outp "push ecx"
	outp "shr ecx, 2"
	outp "rep stosd"
	outp "pop ecx"
	outp "and ecx, 3"
	outp "rep stosb"

	if( edifree = FALSE ) then
		hPOP( "edi" )
	end if
	if( ecxfree = FALSE ) then
		hPOP( "ecx" )
	end if
	if( eaxfree = FALSE ) then
		hPOP( "eax" )
	end if

end sub

'':::::
private sub _emitMEMCLEAR _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval unused as integer, _
		byval extra as integer _
	)

	'' handle the assumption done at ast-node-mem::newMEM()
	if( irIsIMM( svreg ) ) then
		dim as ulong bytes = svreg->value.i
		if( bytes > EMIT_MEMBLOCK_MAXLEN ) then
			hMemClearRepIMM( dvreg, bytes )
		else
			hMemClearBlkIMM( dvreg, bytes )
		end if

	else
    	hMemClear( dvreg, svreg )
	end if

end sub

'':::::
private sub _emitSTKCLEAR _
	( _
		byval dvreg as IRVREG ptr, _
		byval svreg as IRVREG ptr, _
		byval bytes as integer, _
		byval baseofs as integer _
	) static

	hClearLocals( bytes, baseofs )

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' debugging
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private sub _emitLINEINI _
	( _
		byval proc as FBSYMBOL ptr, _
		byval lnum as integer, _
		byval pos_ as integer _
	)

	edbgLineBegin( proc, lnum, pos_ )

end sub

'':::::
private sub _emitLINEEND _
	( _
		byval proc as FBSYMBOL ptr, _
		byval lnum as integer, _
		byval pos_ as integer _
	)

	edbgLineEnd( proc, lnum, pos_ )

end sub

'':::::
private sub _emitSCOPEINI _
	( _
		byval sym as FBSYMBOL ptr, _
		byval lnum as integer, _
		byval pos_ as integer _
	)

	edbgEmitScopeINI( sym )

end sub

'':::::
private sub _emitSCOPEEND _
	( _
		byval sym as FBSYMBOL ptr, _
		byval lnum as integer, _
		byval pos_ as integer _
	)

	edbgEmitScopeEND( sym )

end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' boolean
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

''
'' Load/store b2b, b2i, i2b
''
'' Basic rules: booleans store 0/1 to be g++-compatible, but we produce 0/-1
'' when converting to integers to be FB-compatible.
''

private sub _emitLOADB2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	'' Assumes that booleans are stored as 0|1 only, and any other value
	'' is undefined.  Also assume src and dst are always same size.
	
	dim as string dst, src

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	hMOV( dst, src )

end sub

private sub _emitSTORB2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	'' storing boolean to boolean same as loading to boolean.  See LOADB2B 
	'' for assumptions
	_emitLOADB2B( dvreg, svreg )
end sub

private sub _emitLOADB2I( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )

	dim as string src, dst
	
	hPrepOperand( svreg, src )
	hPrepOperand( dvreg, dst )

	assert( svreg->dtype = FB_DATATYPE_BOOLEAN )

	if( irIsIMM( svreg ) ) then
		if( svreg->value.i ) then
			hMOV( dst, "-1" )
		else
			hMOV( dst, "0" )
		end if

		exit sub
	end if

	'' copy the 0|1
	if( typeGetSize( dvreg->dtype ) > typeGetSize( svreg->dtype ) ) then
		outp( "movzx " + dst + ", " + src )
	else
		hMOV( dst, src )
	end if

	'' convert 0|1 to 0|-1
	outp( "neg " + dst )

end sub

private sub _emitSTORB2I( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	'' storing boolean to integer same as loading to integer and LOADB2I
	'' takes care of the zero-extension
	_emitLOADB2I( dvreg, svreg )
end sub

private sub _emitLOADI2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )

	dim as string src, dst, dst8
	dim as integer ddsize = any
	
	hPrepOperand( svreg, src )
	hPrepOperand( dvreg, dst )

	ddsize = typeGetSize( FB_DATATYPE_BOOLEAN )

	assert( dvreg->dtype = FB_DATATYPE_BOOLEAN )
	assert( (ddsize = 1) or (ddsize = 4) )

	'' immediate?
	if( irIsIMM( svreg ) ) then
		if( svreg->value.i ) then
			hMOV( dst, "1" )
		else
			hMOV( dst, "0" )
		end if

	'' 1-byte boolean? (then we can "setne" directly into it)
	elseif( ddsize = 1 ) then
		'' int8 to boolean
		'' if src is non-zero, produce 0|1 and store it into dst
		outp( "cmp " + src + ", 0" )
		outp( "setne " + dst )

	'' 4-byte booleans: dst is register with 8-bit accessor? (then we can "setne" directly into it)
	elseif( irIsREG( dvreg ) and (dvreg->reg <> EMIT_REG_ESI) and (dvreg->reg <> EMIT_REG_EDI) ) then
		'' int to boolean reg
		dst8 = *hGetRegName( FB_DATATYPE_BYTE, dvreg->reg )
		outp( "cmp " + src + ", 0" )
		outp( "setne " + dst8 )
		outp( "movzx " + dst + ", " + dst8 )

	'' 4-byte booleans: do it the hard way .. with an extra reg
	else
		dim as string aux, aux8
		dim as integer reg, isfree

		reg = hFindRegNotInVreg( dvreg, TRUE )

		aux8 = *hGetRegName( FB_DATATYPE_BYTE, reg )
		aux = *hGetRegName( dvreg->dtype, reg )

		isfree = hIsRegFree(FB_DATACLASS_INTEGER, reg )
		if( isfree = FALSE ) then
			hPUSH aux
		end if

		'' is src zero ?
		outp "cmp " + src + COMMA + "0"

		'' set byte to one (1) if src not equal to zero
		outp "setne " + aux8

		if( irIsREG( dvreg ) ) then
			outp "movzx " + dst + COMMA + aux8
		else
			outp "movzx " + aux + COMMA + aux8
			outp "mov " + dst + COMMA + aux
		end if

		if( isfree = FALSE ) then
			hPOP aux
		end if
	end if

end sub

private sub _emitSTORI2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	'' storing integer to boolean same as loading to boolean and LOADI2B
	'' takes care of the ESI/DSI as byte reg stuff
	_emitLOADI2B( dvreg, svreg )
end sub

''
'' Load/store f2b or b2f
''
''

private sub _emitLOADF2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )

    dim as string dst, src
    dim as integer ddsize = any
	dim as integer isfree = any

	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	ddsize = typeGetSize( dvreg->dtype )

	assert( dvreg->dtype = FB_DATATYPE_BOOLEAN )

	isfree = hIsRegFree( FB_DATACLASS_INTEGER, EMIT_REG_EAX )
	isfree orelse= hIsRegInVreg( dvreg, EMIT_REG_EAX )

	if( svreg->typ <> IR_VREGTYPE_REG ) then
		outp "fld " + src
	end if
	
	if( isfree = FALSE ) then
		outp "push eax"
	end if

	outp "ftst"
	outp "fnstsw ax"
	
	#if 1
	outp "sahf"
	outp "setnz al"
	#else
	outp "test ah, 0b01000000"
	outp "setz al"
	#endif
	
	outp "fstp st(0)"

	if( ddsize = 1 ) then
		outp "mov " + dst + ", al"
	else
		outp "movzx " + dst + ", al"
	end if

	if( isfree = FALSE ) then
		outp "pop eax"
	end if

end sub

private sub _emitSTORF2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	_emitLOADF2B(dvreg, svreg)
end sub

private sub _emitLOADB2F( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	
	dim as string src, dst
	dim as integer sdsize = any
	
	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	sdsize = typeGetSize( svreg->dtype )

	assert( svreg->dtype = FB_DATATYPE_BOOLEAN )

	'' immediate?
	if( irIsIMM( svreg ) ) then
		if( svreg->value.i ) then
			outp "fld1"
			outp "fchs"
		else
			outp "fldz"
		end if
		exit sub
	end if

	'' byte source? damn..
	if( sdsize = 1 ) then
    	dim as string aux
    	dim as integer isfree, reg

		reg = hFindRegNotInVreg( svreg )

		aux = *hGetRegName( FB_DATATYPE_INTEGER, reg )

		isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

		if( isfree = FALSE ) then
			hPUSH aux
		end if

		outp "movzx " + aux + COMMA + src

		hPUSH aux
		outp "fild dword ptr [esp]"
		outp "fchs"
		outp "add esp, 4"

		if( isfree = FALSE ) then
			hPOP aux
		end if
	else
		outp "fild " + src
	end if
	outp "fchs"

end sub

private sub _emitSTORB2F( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )

	dim as string src, dst
	dim as integer sdsize = any
	
	hPrepOperand( dvreg, dst )
	hPrepOperand( svreg, src )

	sdsize = typeGetSize( svreg->dtype )

	assert( svreg->dtype = FB_DATATYPE_BOOLEAN )

	'' immediate?
	if( irIsIMM( svreg ) ) then
		if( svreg->value.i ) then
			outp "fld1"
			outp "fchs"
		else
			outp "fldz"
		end if
		outp "fstp " + dst
		exit sub
	end if

	'' byte source? damn..
	if( sdsize = 1 ) then
    	dim as string aux
    	dim as integer isfree, reg

		reg = hFindRegNotInVreg( svreg )

		aux = *hGetRegName( FB_DATATYPE_INTEGER, reg )

		isfree = hIsRegFree( FB_DATACLASS_INTEGER, reg )

		if( isfree = FALSE ) then
			hPUSH aux
		end if

		outp "movzx " + aux + COMMA + src

		hPUSH aux
		outp "fild dword ptr [esp]"
		outp "add esp, 4"

		if( isfree = FALSE ) then
			hPOP aux
		end if
	else
		outp "fild " + src
	end if

	outp "fchs"
	outp "fstp " + dst

end sub


''
'' Load/store b2l or l2b
'' (same rules as _emitLOADB2I())
''

private sub _emitLOADB2L( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )

	dim as string dst1, dst2
	hPrepOperand64( dvreg, dst1, dst2 )

	assert( svreg->dtype = FB_DATATYPE_BOOLEAN )

	if( irIsIMM( svreg ) ) then
		if( svreg->value.i ) then
			hMOV( dst1, "-1" )
			hMOV( dst2, "-1" )
		else
			hMOV( dst1, "0" )
			hMOV( dst2, "0" )
		end if
	else
		dim as string src
		hPrepOperand( svreg, src )

		'' copy the 0|1 into the low dword
		assert( typeGetSize( dvreg->dtype ) > typeGetSize( svreg->dtype ) )
		outp( "movzx " + dst1 + ", " + src )

		'' convert 0|1 to 0|-1
		outp( "neg " + dst1 )

		'' copy 0|-1 into upper dword too
		hMOV( dst2, dst1 )
	end if

end sub

private sub _emitSTORB2L( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	_emitLOADB2L(dvreg, svreg)
end sub

private sub _emitLOADL2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )

	dim as string dst
	dim as integer ddsize
	dim as string src1, src2
	dim as string aux, aux8

	hPrepOperand64( svreg, src1, src2 )
	hPrepOperand( dvreg, dst )

	ddsize = typeGetSize( FB_DATATYPE_BOOLEAN )

	assert( dvreg->dtype = FB_DATATYPE_BOOLEAN )
	assert( (ddsize = 1) or (ddsize = 4) )

	'' immediate?
	if( irIsIMM( svreg ) ) then
		if( svreg->value.i ) then
			hMOV( dst, "1" )
		else
			hMOV( dst, "0" )
		end if

	'' destine is register with 8-bit accessor? use it
	elseif( irIsREG( dvreg ) and (dvreg->reg <> EMIT_REG_ESI) and (dvreg->reg <> EMIT_REG_EDI) ) then

		aux8 = *hGetRegName( FB_DATATYPE_BYTE, dvreg->reg )
		aux = *hGetRegName( FB_DATATYPE_INTEGER, dvreg->reg )

		hMOV( aux, src1 )
		outp( "or " + aux + ", " + src2 )
		outp( "cmp " + aux + ", 0" )
		outp( "setne " + aux8 )
		if( ddsize <> 1 ) then
			outp( "movzx " + aux + ", " + aux8 )
		end if
	
	'' use an extra reg
	else

		dim as integer reg, isfree = FALSE

		reg = hFindRegNotInVreg( dvreg, TRUE )

		aux8 = *hGetRegName( FB_DATATYPE_BYTE, reg )
		aux = *hGetRegName( FB_DATATYPE_INTEGER, reg )

		isfree = hIsRegFree(FB_DATACLASS_INTEGER, reg )
		if( isfree = FALSE ) then
			hPUSH aux
		end if

		'' source is also a reg?
		if( irIsREG( svreg ) ) then
			'' combine high/low dwords (check is source is already has one of the dwords)
			if( reg = svreg->reg ) then
				'' reg already has src1
				outp( "or " + aux + ", " + src2 )
			elseif( reg = svreg->vaux->reg ) then
				'' reg already has src2
				outp( "or " + aux + ", " + src1 )
			else
				hMOV( aux, src1 )
				outp( "or " + aux + ", " + src2 )
			end if
		else
			'' combine high/low dwords
			hMOV( aux, src1 )
			outp( "or " + aux + ", " + src2 )
		end if
	
		'' if (src1 or src2) is non-zero, produce 0|1 and store it into dst
		outp( "cmp " + aux + ", 0" )
		outp( "setne " + aux8 )
		if( ddsize = 1 ) then
			hMOV( dst, aux8 )
		else
			outp( "movzx " + aux + ", " + aux8 )
			hMOV( dst, aux )
		end if

		if( isfree = FALSE ) then
			hPOP aux
		end if

	end if

end sub

private sub _emitSTORL2B( byval dvreg as IRVREG ptr, byval svreg as IRVREG ptr )
	_emitLOADL2B(dvreg, svreg)
end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' initializers
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

sub emitVARINIBEGIN( byval sym as FBSYMBOL ptr )
	_setSection( IR_SECTION_DATA, 0 )

	'' Add debug info for public/shared globals, but not local statics
	edbgEmitGlobalVar( sym, IR_SECTION_DATA )

	hALIGN( hGetGlobalVarAlign( sym ) )

	'' public?
	if( symbIsPublic( sym ) ) then
		hPUBLIC( *symbGetMangledName( sym ), symbIsExport( sym )  )
	end if

	hLABEL( *symbGetMangledName( sym ) )
end sub

sub emitVARINIi( byval dtype as integer, byval value as longint )
	dim s as string
	s = *_getTypeString( dtype ) + " "
	if( ISLONGINT( dtype ) ) then
		s += "0x" + hex( value )
	else
		s += str( value )
	end if
	s += NEWLINE
	outEx( s )
end sub

sub emitVARINIf( byval dtype as integer, byval value as double )
	'' can't use STR() because GAS doesn't support the 1.#INF notation
	outEx( *_getTypeString( dtype ) + " " + hFloatToHex( value, dtype ) + NEWLINE )
end sub

sub emitVARINIOFS( byval sname as zstring ptr, byval ofs as integer )
	static as string ostr
	ostr = ".int "
	ostr += *sname
	if( ofs <> 0 ) then
		ostr += " + "
		ostr += str( ofs )
	end if
	ostr += NEWLINE
	outEx( ostr )
end sub

sub emitVARINISTR( byval s as const zstring ptr )
	static as string ostr
	ostr = ".ascii " + QUOTE
	ostr += *s
	ostr += RSLASH + "0" + QUOTE + NEWLINE
	outEx( ostr )
end sub

sub emitVARINIWSTR( byval s as zstring ptr )
	static as string ostr
	ostr = ".ascii " + QUOTE
	ostr += *s
	for i as integer = 1 to typeGetSize( FB_DATATYPE_WCHAR )
		ostr += RSLASH + "0"
	next
	ostr += QUOTE + NEWLINE
	outEx( ostr )
end sub

sub emitVARINIPAD( byval bytes as integer )
	outEx( ".skip " + str( bytes ) + ",0" + NEWLINE )
end sub

sub emitFBCTINFBEGIN( )
	_setSection( IR_SECTION_INFO, 0 )
end sub

sub emitFBCTINFSTRING( byval s as const zstring ptr )
	static as string ln
	ln = *emit.vtbl.getTypeString( FB_DATATYPE_CHAR )
	ln += " """ + *s + $"\0"""
	emitWriteStr( ln )
end sub

sub emitFBCTINFEND( )
	emitWriteStr( "" )
end sub

''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' functions table
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

#define EMIT_CBENTRY(op) @_emit##op##

	'' same order as EMIT_NODEOP
	dim shared _opFnTB(0 to EMIT_MAXOPS-1) as any ptr => _
	{ _
		EMIT_CBENTRY(NOP), _
		_
		EMIT_CBENTRY(LOADI2I), EMIT_CBENTRY(LOADF2I), EMIT_CBENTRY(LOADL2I), EMIT_CBENTRY(LOADB2I), _
		EMIT_CBENTRY(LOADI2F), EMIT_CBENTRY(LOADF2F), EMIT_CBENTRY(LOADL2F), EMIT_CBENTRY(LOADB2F), _
		EMIT_CBENTRY(LOADI2L), EMIT_CBENTRY(LOADF2L), EMIT_CBENTRY(LOADL2L), EMIT_CBENTRY(LOADB2L), _
		EMIT_CBENTRY(LOADI2B), EMIT_CBENTRY(LOADF2B), EMIT_CBENTRY(LOADL2B), EMIT_CBENTRY(LOADB2B), _
		_
		EMIT_CBENTRY(STORI2I), EMIT_CBENTRY(STORF2I), EMIT_CBENTRY(STORL2I), EMIT_CBENTRY(STORB2I), _
		EMIT_CBENTRY(STORI2F), EMIT_CBENTRY(STORF2F), EMIT_CBENTRY(STORL2F), EMIT_CBENTRY(STORB2F), _
		EMIT_CBENTRY(STORI2L), EMIT_CBENTRY(STORF2L), EMIT_CBENTRY(STORL2L), EMIT_CBENTRY(STORB2L), _
		EMIT_CBENTRY(STORI2B), EMIT_CBENTRY(STORF2B), EMIT_CBENTRY(STORL2B), EMIT_CBENTRY(STORB2B), _
        _
		EMIT_CBENTRY(MOVI), EMIT_CBENTRY(MOVF), EMIT_CBENTRY(MOVL), _
		EMIT_CBENTRY(ADDI), EMIT_CBENTRY(ADDF), EMIT_CBENTRY(ADDL), _
		EMIT_CBENTRY(SUBI), EMIT_CBENTRY(SUBF), EMIT_CBENTRY(SUBL), _
		EMIT_CBENTRY(MULI), EMIT_CBENTRY(MULF), EMIT_CBENTRY(MULL), _
		EMIT_CBENTRY(DIVI), EMIT_CBENTRY(DIVF), NULL              , _
		EMIT_CBENTRY(MODI), NULL              , NULL              , _
		EMIT_CBENTRY(SHLI), EMIT_CBENTRY(SHLL), _
		EMIT_CBENTRY(SHRI), EMIT_CBENTRY(SHRL), _
		EMIT_CBENTRY(ANDI), EMIT_CBENTRY(ANDL), _
		EMIT_CBENTRY(ORI) , EMIT_CBENTRY(ORL) , _
		EMIT_CBENTRY(XORI), EMIT_CBENTRY(XORL), _
		EMIT_CBENTRY(EQVI), EMIT_CBENTRY(EQVL), _
		EMIT_CBENTRY(IMPI), EMIT_CBENTRY(IMPL), _
		EMIT_CBENTRY(ATN2), _
		EMIT_CBENTRY(POW), _
		EMIT_CBENTRY(ADDROF), _
		EMIT_CBENTRY(DEREF), _
        _
		EMIT_CBENTRY(CGTI), EMIT_CBENTRY(CGTF), EMIT_CBENTRY(CGTL), _
		EMIT_CBENTRY(CLTI), EMIT_CBENTRY(CLTF), EMIT_CBENTRY(CLTL), _
		EMIT_CBENTRY(CEQI), EMIT_CBENTRY(CEQF), EMIT_CBENTRY(CEQL), _
		EMIT_CBENTRY(CNEI), EMIT_CBENTRY(CNEF), EMIT_CBENTRY(CNEL), _
		EMIT_CBENTRY(CGEI), EMIT_CBENTRY(CGEF), EMIT_CBENTRY(CGEL), _
		EMIT_CBENTRY(CLEI), EMIT_CBENTRY(CLEF), EMIT_CBENTRY(CLEL), _
        _
		EMIT_CBENTRY(NEGI), EMIT_CBENTRY(NEGF), EMIT_CBENTRY(NEGL), _
		EMIT_CBENTRY(NOTI), EMIT_CBENTRY(NOTL), _
	   _
		NULL, _
        _
		EMIT_CBENTRY(ABSI), EMIT_CBENTRY(ABSF), EMIT_CBENTRY(ABSL), _
		EMIT_CBENTRY(SGNI), EMIT_CBENTRY(SGNF), EMIT_CBENTRY(SGNL), _
        _
		EMIT_CBENTRY(FIX), _
		EMIT_CBENTRY(FRAC), _
		EMIT_CBENTRY(CONVFD2FS), _
	   _
		NULL, _
	   _
		EMIT_CBENTRY(SIN), EMIT_CBENTRY(ASIN), _
		EMIT_CBENTRY(COS), EMIT_CBENTRY(ACOS), _
		EMIT_CBENTRY(TAN), EMIT_CBENTRY(ATAN), _
		EMIT_CBENTRY(SQRT), _
	   _
		NULL, _
		NULL, _
	   _
		EMIT_CBENTRY(LOG), _
		EMIT_CBENTRY(EXP), _
		EMIT_CBENTRY(FLOOR), _
		EMIT_CBENTRY(XCHGTOS), _
        _
		EMIT_CBENTRY(STACKALIGN), _
		EMIT_CBENTRY(PUSHI), EMIT_CBENTRY(PUSHF), EMIT_CBENTRY(PUSHL), _
		EMIT_CBENTRY(POPI), EMIT_CBENTRY(POPF), EMIT_CBENTRY(POPL), _
		EMIT_CBENTRY(PUSHUDT), _
		EMIT_CBENTRY(POPST0), _
        _
		EMIT_CBENTRY(CALL), _
		EMIT_CBENTRY(CALLPTR), _
		EMIT_CBENTRY(BRANCH), _
		EMIT_CBENTRY(JUMP), _
		EMIT_CBENTRY(JUMPPTR), _
		EMIT_CBENTRY(RET), _
        _
		EMIT_CBENTRY(LABEL), _
		EMIT_CBENTRY(PUBLIC), _
		EMIT_CBENTRY(LIT), _
		EMIT_CBENTRY(JMPTB), _
        _
		EMIT_CBENTRY(MEMMOVE), _
		EMIT_CBENTRY(MEMSWAP), _
		EMIT_CBENTRY(MEMCLEAR), _
		EMIT_CBENTRY(STKCLEAR), _
        _
		EMIT_CBENTRY(LINEINI), _
		EMIT_CBENTRY(LINEEND), _
		EMIT_CBENTRY(SCOPEINI), _
		EMIT_CBENTRY(SCOPEEND) _
	}



'':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' emit.vtbl implementation
'':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
private function _init _
	( _
	) as integer

	''
	hInitRegTB( )

	'' Remap wchar to target-specific type
	dtypeTB(FB_DATATYPE_WCHAR) = dtypeTB(env.target.wchar)

	''
	emit.lastsection = INVALID
	emit.lastpriority = INVALID

	dim as uinteger iroptions = _
		IR_OPT_CPUSELFBOPS or IR_OPT_CPUBOPFLAGS or _
		IR_OPT_ADDRCISC

	if( env.clopt.fputype = FB_FPUTYPE_SSE ) then
		iroptions or= IR_OPT_FPUCONV
	end if

	irSetOption( iroptions )

	edbgInit( )

	function = TRUE

end function

'':::::
private sub _end

	''
	emit.lastsection = INVALID
	emit.lastpriority = INVALID

	''
	hEndRegTB( )

end sub

'':::::
private function _getOptionValue _
	( _
		byval opt as IR_OPTIONVALUE _
	) as integer

	select case opt
	case IR_OPTIONVALUE_MAXMEMBLOCKLEN
		return EMIT_MEMBLOCK_MAXLEN

	case else
		errReportEx( FB_ERRMSG_INTERNAL, __FUNCTION__ )

	end select

end function


'':::::
private function _open _
	( _
	) as integer

	if( hFileExists( env.outf.name ) ) then
		kill env.outf.name
	end if

	env.outf.num = freefile
	if( open( env.outf.name, for binary, access read write, as #env.outf.num ) <> 0 ) then
		return FALSE
	end if

	'' header
	hWriteHeader( )

	function = TRUE

end function

'':::::
private sub _close( )

	'' Close any STABS #include block (and return to the toplevel .bas
	'' file name) before emitting the global vars
	edbgInclude( NULL )

	'' const/data/bss
	symbForEachGlobal( FB_SYMBCLASS_VAR, @hDeclVariable )

	'' DLL export table
	if( env.clopt.export and (env.target.options and FB_TARGETOPT_EXPORT) ) then
		symbForEachGlobal( FB_SYMBCLASS_PROC, @hEmitExport )
	end if

	'' Global ctor/dtor lists
	hWriteCtor( symbGetGlobCtorListHead( ), TRUE )
	hWriteCtor( symbGetGlobDtorListHead( ), FALSE )

	''
	edbgEmitFooter( )

	''
	if( close( #env.outf.num ) <> 0 ) then
		'' ...
	end if

	env.outf.num = 0

end sub

'':::::
private function _procGetFrameRegName _
	( _
	) as const zstring ptr

	static as zstring * 3+1 sname = "ebp"

	function = @sname

end function

'':::::
private function _isRegPreserved _
	( _
		byval dclass as integer, _
		byval reg as integer _
	) as integer static

    '' fp? fpu stack *must* be cleared before calling any function
    if( dclass = FB_DATACLASS_FPOINT ) then
    	return FALSE
    end if

    select case as const reg
    case EMIT_REG_EAX, EMIT_REG_ECX, EMIT_REG_EDX
    	return FALSE
    case else
    	return TRUE
	end select

end function

'':::::
private sub _getResultReg _
	( _
		byval dtype as integer, _
		byval dclass as integer, _
		byref r1 as integer, _
		byref r2 as integer _
	)

	if( dclass = FB_DATACLASS_INTEGER ) then
		r1 = EMIT_REG_EAX
		if( ISLONGINT( typeGet( dtype ) ) ) then
			r2 = EMIT_REG_EDX
		else
			r2 = INVALID
		end if
	else
		r1 = 0							'' st(0)
		r2 = INVALID
	end if

end sub

'':::::
private function _getFreePreservedReg _
	( _
		byval dclass as integer, _
		byval dtype as integer _
	) as integer static

	function = INVALID

	'' fp? no other regs can be used
	if( dclass = FB_DATACLASS_FPOINT ) then
		exit function
	end if

	'' try to reuse regs that are preserved between calls
	if( emit.regTB(dclass)->isFree( emit.regTB(dclass), EMIT_REG_EBX ) ) then
		function = EMIT_REG_EBX

	elseif( emit.regTB(dclass)->isFree( emit.regTB(dclass), EMIT_REG_ESI ) ) then
		if( typeGetSize( dtype ) <> 1 ) then
			function = EMIT_REG_ESI
		end if

	elseif( emit.regTB(dclass)->isFree( emit.regTB(dclass), EMIT_REG_EDI ) ) then
		if( typeGetSize( dtype ) <> 1 ) then
			function = EMIT_REG_EDI
		end if
	end if

end function

'':::::
private sub _procBegin _
	( _
		byval proc as FBSYMBOL ptr _
	)

    proc->proc.ext->stk.localofs = EMIT_LOCSTART
    proc->proc.ext->stk.localmax = EMIT_LOCSTART
	proc->proc.ext->stk.argofs = EMIT_ARGSTART

	edbgProcBegin( proc )

end sub

'':::::
private sub _procEnd _
	( _
		byval proc as FBSYMBOL ptr _
	)

	edbgProcEnd( proc )

end sub

private sub _procAllocStaticVars( byval s as FBSYMBOL ptr )
	while( s )
		select case( symbGetClass( s ) )
		'' scope block? recursion..
		case FB_SYMBCLASS_SCOPE
			_procAllocStaticVars( symbGetScopeSymbTbHead( s ) )

		'' variable?
		case FB_SYMBCLASS_VAR
			'' static?
			if( symbIsStatic( s ) ) then
				hDeclVariable( s )
			end if
		end select

		s = symbGetNext( s )
	wend
end sub

private sub _procAllocLocal _
	( _
		byval proc as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr _
	)

	dim as integer ofs = any, lgt = any

	'' Do not allocate stack space for fake dynamic array symbols; only the
	'' corresponding descriptors will be emitted.
	''
	'' It should not be necessary to assign the array symbol the same offset
	'' as its descriptor either, as any accesses should be based on the
	'' descriptor instead.
	if( symbIsDynamic( sym ) ) then
		assert( sym->ofs = 0 )
		exit sub
	end if

	lgt = symbGetRealSize( sym )

    proc->proc.ext->stk.localofs += ((lgt + 3) and not 3)

	ofs = -proc->proc.ext->stk.localofs

    if( -ofs > proc->proc.ext->stk.localmax ) then
    	proc->proc.ext->stk.localmax = -ofs
    end if

	sym->ofs = ofs

end sub

private sub _procAllocArg _
	( _
		byval proc as FBSYMBOL ptr, _
		byval sym as FBSYMBOL ptr _
	)

	dim as integer lgt = any

	assert( symbIsParam( sym ) )

	if( symbIsParamByVal( sym ) ) then
		lgt = symbGetLen( sym )
	else
		'' Bydesc/byref
		lgt = env.pointersize
	end if

	sym->ofs = proc->proc.ext->stk.argofs
    proc->proc.ext->stk.argofs += ((lgt + 3) and not 3)

end sub

'':::::
private sub _procHeader _
	( _
		byval proc as FBSYMBOL ptr, _
		byval initlabel as FBSYMBOL ptr _
	)

	'' do nothing, proc will be only emitted at PROCFOOTER

	emitReset( )

	edbgProcEmitBegin( proc )

end sub

'':::::
private sub _procFooter _
	( _
		byval proc as FBSYMBOL ptr, _
		byval bytestopop as integer, _
		byval initlabel as FBSYMBOL ptr, _
		byval exitlabel as FBSYMBOL ptr _
	)

    dim as integer oldpos = any, ispublic = any

    ispublic = symbIsPublic( proc )

	_setSection( IR_SECTION_CODE, 0 )

	''
	edbgEmitProcHeader( proc )

	''
	hALIGN( 16 )

	if( ispublic ) then
		hPUBLIC( symbGetMangledName( proc ), symbIsExport( proc ) )
	end if

	hLABEL( symbGetMangledName( proc ) )

	if( env.clopt.target = FB_COMPTARGET_LINUX ) then
		outEx( ".type " + *symbGetMangledName( proc ) + ", @function" + NEWLINE )
	end if

	'' frame
	hCreateFrame( proc )

    edbgEmitLineFlush( proc, proc->proc.ext->dbg.iniline, proc )

    ''
    emitFlush( )

	''
	hDestroyFrame( proc, bytestopop )

    edbgEmitLineFlush( proc, proc->proc.ext->dbg.endline, exitlabel )

    edbgEmitProcFooter( proc, initlabel, exitlabel )

end sub

'':::::
private sub _scopeBegin _
	( _
		byval s as FBSYMBOL ptr _
	)

	edbgScopeBegin( s )

end sub

'':::::
private sub _scopeEnd _
	( _
		byval s as FBSYMBOL ptr _
	)

	edbgScopeEnd( s )

end sub

'':::::
private sub _setSection _
	( _
		byval section as integer, _
		byval priority as integer _
	)

	dim as const zstring ptr sec = _getSectionString( section, priority )
	if( sec = NULL ) then
		exit sub
	end if

    static as string ostr

	ostr = *sec
	ostr += NEWLINE

	outEx( ostr )

end sub

private function _getTypeString( byval dtype as integer ) as const zstring ptr
	select case as const typeGet( dtype )
	case FB_DATATYPE_UBYTE, FB_DATATYPE_BYTE, FB_DATATYPE_BOOLEAN
		function = @".byte"
	case FB_DATATYPE_USHORT, FB_DATATYPE_SHORT
		function = @".short"
	case FB_DATATYPE_INTEGER, FB_DATATYPE_UINT, FB_DATATYPE_ENUM
		function = @".int"
	case FB_DATATYPE_LONG, FB_DATATYPE_ULONG, FB_DATATYPE_SINGLE
		'' SINGLE: emitted as raw bytes in form of .long 0x...,
		'' instead of .float 1.234...,
		'' to allow the exact bytes to be emitted by hFloatToHex(),
		'' instead of a str() approximation.
		function = @".long"
	case FB_DATATYPE_LONGINT, FB_DATATYPE_ULONGINT, FB_DATATYPE_DOUBLE
		'' DOUBLE: ditto, instead of .double
		function = @".quad"
	case FB_DATATYPE_FIXSTR, FB_DATATYPE_CHAR, FB_DATATYPE_WCHAR
		'' wchar stills the same as it is emitted as escape sequences
		function = @".ascii"
	case FB_DATATYPE_STRING, FB_DATATYPE_STRUCT
		function = @".INVALID"
	case FB_DATATYPE_POINTER
		function = @".long"
	case else
		function = @".INVALID"
	end select
end function

'':::::
private function _getSectionString _
	( _
		byval section as integer, _
		byval priority as integer _
	) as const zstring ptr

	static as string ostr

	if( (section = emit.lastsection) and (priority = emit.lastpriority) ) then
		return NULL
	end if

	ostr = NEWLINE

	'' Omit the .section directive on Darwin
	if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then
		ostr += ".section "
	end if

	ostr += "."

	select case as const section
	case IR_SECTION_CONST
		select case as const fbGetOption( FB_COMPOPT_TARGET )
		case FB_COMPTARGET_CYGWIN, FB_COMPTARGET_DOS, _
		     FB_COMPTARGET_WIN32, FB_COMPTARGET_XBOX
			ostr += "rdata"

		case FB_COMPTARGET_DARWIN
			ostr += "const"

		case else
			ostr += "rodata"

		end select

	case IR_SECTION_DATA
		ostr += "data"

	case IR_SECTION_BSS
		ostr += "bss"

	case IR_SECTION_CODE
		ostr += "text"

	case IR_SECTION_DIRECTIVE
		ostr += "drectve"

	case IR_SECTION_INFO
		ostr += FB_INFOSEC_NAME

	case IR_SECTION_CONSTRUCTOR
		ostr += "ctors"
		if( priority > 0 ) then
			ostr += "." + right( "00000" + str( 65535 - priority ), 5 )
		end if
		if( env.clopt.target = FB_COMPTARGET_LINUX ) then
			ostr += ", " + QUOTE + "aw" + QUOTE + ", @progbits"
		end if

	case IR_SECTION_DESTRUCTOR
		ostr += "dtors"
		if( priority > 0 ) then
			ostr += "." + right( "00000" + str( 65535 - priority ), 5 )
		end if
		if( env.clopt.target = FB_COMPTARGET_LINUX ) then
			ostr += ", " + QUOTE +  "aw" + QUOTE + ", @progbits"
		end if

	end select

	function = strptr( ostr )

	emit.lastsection = section
	emit.lastpriority = priority

end function


''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'' initialization/finalization
''::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

'':::::
function emitGasX86_ctor _
	( _
	) as integer

	static as EMIT_VTBL _vtbl = _
	( _
		@_init, _
		@_end, _
		@_getOptionValue, _
		@_open, _
		@_close, _
		@_isRegPreserved, _
		@_getFreePreservedReg, _
		@_getResultReg, _
		@_procGetFrameRegName, _
		@_procBegin, _
		@_procEnd, _
		@_procHeader, _
		@_procFooter, _
		@_procAllocArg, _
		@_procAllocLocal, _
		@_procAllocStaticVars, _
		@_scopeBegin, _
		@_scopeEnd, _
		@_setSection, _
		@_getTypeString, _
		@_getSectionString _
	)

	emit.vtbl = _vtbl
	emit.opFnTb = @_opFnTB(0)

	if( env.clopt.fputype >= FB_FPUTYPE_SSE ) then
		_init_opFnTB_SSE( emit.opFnTb )
	end if

	function = TRUE

end function


