;  Copyright (C) 2009, 2008 Brian Gladman
;
;  All rights reserved.
;
;  Redistribution and use in source and binary forms, with or without
;  modification, are permitted provided that the following conditions are
;  met:
;  1. Redistributions of source code must retain the above copyright notice,
;  this list of conditions and the following disclaimer.
;
;  2. Redistributions in binary form must reproduce the above copyright
;  notice, this list of conditions and the following disclaimer in the
;  documentation and/or other materials provided with the distribution.
;
;  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
;  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
;  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
;  HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
;  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
;  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
;  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;

; Standardised register numbering scheme

%define r0  rax
%define r1  rdx
%define r2  rcx
%define r3  rbx
%define r4  rsi
%define r5  rdi
%define r6  rbp
%define r7  rsp

%define r0d eax
%define r1d edx
%define r2d ecx
%define r3d ebx
%define r4d esi
%define r5d edi
%define r6d ebp
%define r7d esp

%define r0w ax
%define r1w dx
%define r2w cx
%define r3w bx
%define r4w si
%define r5w di
%define r6w bp
%define r7w sp

%define r0b al
%define r1b dl
%define r2b cl
%define r3b bl
%define r4b sil
%define r5b dil
%define r6b bpl
%define r7b spl

%define asm_sym(x) __g %+ x

; Standard macro for alignment (used to allow easy subsititution of
; alternative padding schemes)

%macro xalign 1
    align %1
%endmacro

; YASM macros for handling Windows Prologues and Epilogues
;
; Copyright 2008, 2009 Brian Gladman
;
; Windows x64 prologue macro:
;
;     FRAME_PROC name, var_slots, register_save_list 
;
; Windows x64 epilogue macro:
;
;     EXIT_PROC register_save_list
;
; Windows x64 epilogue and end procedure macro
;
;     END_PROC register_save_list
;
; where:

;   name:                routine name
;   var_slots:           stack space needed in 8 byte units
;   register_save_list:  comma separated list of registers to
;                        save and restore: a list of general
;                        purpose registers followed, if XMM
;                        registers are present, by XMM, and
;                        then a list of the numbers of the 
;                        XMM registers to save and restore
;                        e.g.  rsi, rdi, rbp, XMM, 6, 7, 8

; On return the macro variable 'stack_use' gives the total number 
; of bytes used on the stack. This allows the function parameters
; to be accessed at [rsp + stack_use + 8 * n] where n starts at 1
; (for n = 1..4 this is shadow space for a register parameter)  

%macro FRAME_PROC 2-*

    global  asm_sym(%1)
    
%ifdef DLL
    export  asm_sym(%1)
%endif

    PROC_FRAME asm_sym(%1)
    %rotate 1
      
    %if %1 < 0
        %error Negative stack allocation not allowed
    %else
	    %assign var_slots %1
    %endif
    %rotate 1

	%assign reg_slots 0
	%assign xmm_seen 0
    %if %0 > 2
        %rep %0 - 2
		    %ifnum %1
			    %if xmm_seen == 0
				    %error Not an XMM register
				%else
				    alloc_stack 16
					save_xmm128 XMM%1, 0
					%assign reg_slots reg_slots + 2
				%endif
		    %elifid %1
		        %ifidni XMM, %1 
					%if reg_slots & 1 == 0
					    alloc_stack 8
  					    %assign reg_slots reg_slots + 1
    				    %assign xmm_seen 1 
					%else
				        %assign xmm_seen 2					 
					%endif
				%elif xmm_seen == 0
					push_reg  %1
  					%assign reg_slots reg_slots + 1
				%else
				    %error XMM registers must be last in the save list
				%endif 
			%else
			    %error Bad parameter list
			%endif
            %rotate 1
        %endrep
    %endif

	%if (reg_slots & 1) == (var_slots & 1)
	    %assign var_slots var_slots + 1
	%endif

    %if var_slots > 0
        alloc_stack 8 * var_slots
	%endif
	%assign stack_use 8 * (reg_slots + var_slots)
    END_PROLOGUE

%endmacro

%macro EXIT_PROC 0-*

    add rsp, 8 * var_slots
    %if %0 > 0
        %rep %0
            %rotate -1
			%ifnum %1
			    movdqa XMM%1, [rsp]
				add rsp, 16
			%elifidni %1, XMM
			    %if xmm_seen == 1 
				    add rsp, 8
				%endif
			%else 
                pop  %1
			%endif  
        %endrep
    %endif
    ret

%endmacro

%macro END_PROC 0-*

    %if var_slots
	    add rsp, 8 * var_slots
    %endif
    %if %0 > 0
        %rep %0
            %rotate -1
			%ifnum %1
			    movdqa XMM%1, [rsp]
				add rsp, 16
			%elifidni %1, XMM
			    %if xmm_seen == 1 
				    add rsp, 8
				%endif
			%else 
                pop  %1
			%endif  
        %endrep
    %endif
	ret
    ENDPROC_FRAME

%endmacro

%macro LEAF_PROC 1
    
    global  asm_sym(%1)
    
%ifdef DLL
    export  asm_sym(%1)
%endif

asm_sym(%1):

%endmacro

; Macros for using assembler code using the GCC Calling
; Conventions in Windows.  These macros move the first
; six integer parameters from Microsoft ABI calling
; calling conventions to those used by GCC: 
;
;   function(    MSVC --> GCC
;       p1,       rcx     rdi
;       p2,       rdx     rsi
;       p3,        r8     rdx
;       p4,        r9     rcx
;       p5,  [rsp+40]      r8
;       p6,  [rsp+48]      r9
;
;   WIN64_GCC_PROC name, n_parms, (frame | leaf)
;
;   WIN64_GCC_EXIT frame | leaf
;
;   WIN64_GCC_END  frame | leaf
; 
;     name    subroutine name
;     n_parms number of parameters (default 6)
;     type    frame or leaf function (default frame)
;
; These defines are also used and must be set before the
; macros are used:
;
;     reg_save_list   list of registers to be saved
;                     and restored
;     var_slots       number of 8 byte slots needed
;                     on the stack (excluding the 
;                     register save/restore space)

%macro WIN64_GCC_PROC 1-3 6, frame

    %ifidn %3, frame

        %ifndef reg_save_list
            %define reg_save_list rsi, rdi
        %endif

        %ifndef var_slots
            %define var_slots 0
        %endif

        FRAME_PROC %1, var_slots, reg_save_list
        
    %elifidn %3, leaf

        LEAF_PROC %1

    %else

        %error no (or wrong) function type defined 

    %endif

        %if %2 > 0
            mov     rdi, rcx
        %endif
        %if %2 > 1
            mov     rsi, rdx
        %endif
        %if %2 > 2
            mov     rdx, r8
        %endif
        %if %2 > 3
            mov     rcx, r9
        %endif
        %if %2 > 4
            mov     r8, [rsp + stack_use + 40]
        %endif
        %if %2 > 5
            mov     r9, [rsp + stack_use + 48]
        %endif
        
%endmacro

%macro WIN64_GCC_EXIT 0-1 frame

    %ifidn %1, frame
        EXIT_PROC reg_save_list
    %elifidn %1, leaf
        ret
    %else
        %error no (or wrong) function type defined 
    %endif

%endmacro

%macro WIN64_GCC_END 0-1 frame

    %ifidn %1, frame
        END_PROC reg_save_list
    %elifidn %1, leaf
        ret
    %else
        %error no (or wrong) function type defined 
    %endif

%endmacro
