mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-26 21:13:48 +00:00
1322 lines
29 KiB
Text
1322 lines
29 KiB
Text
|
;****************************************************************************
|
||
|
;*
|
||
|
;* ========================================================================
|
||
|
;*
|
||
|
;* The contents of this file are subject to the SciTech MGL Public
|
||
|
;* License Version 1.0 (the "License"); you may not use this file
|
||
|
;* except in compliance with the License. You may obtain a copy of
|
||
|
;* the License at http://www.scitechsoft.com/mgl-license.txt
|
||
|
;*
|
||
|
;* Software distributed under the License is distributed on an
|
||
|
;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||
|
;* implied. See the License for the specific language governing
|
||
|
;* rights and limitations under the License.
|
||
|
;*
|
||
|
;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
|
||
|
;*
|
||
|
;* The Initial Developer of the Original Code is SciTech Software, Inc.
|
||
|
;* All Rights Reserved.
|
||
|
;*
|
||
|
;* ========================================================================
|
||
|
;*
|
||
|
;* Language: NetWide Assembler (NASM) or Turbo Assembler (TASM)
|
||
|
;* Environment: Any Intel Environment
|
||
|
;*
|
||
|
;* Description: Macros to provide memory model independant assembly language
|
||
|
;* module for C programming. Supports the large and flat memory
|
||
|
;* models.
|
||
|
;*
|
||
|
;* The defines that you should use when assembling modules that
|
||
|
;* use this macro package are:
|
||
|
;*
|
||
|
;* __LARGE__ Assemble for 16-bit large model
|
||
|
;* __FLAT__ Assemble for 32-bit FLAT memory model
|
||
|
;* __NOU__ No underscore for all external C labels
|
||
|
;* __NOU_VAR__ No underscore for global variables only
|
||
|
;*
|
||
|
;* The default settings are for 16-bit large memory model with
|
||
|
;* leading underscores for symbol names.
|
||
|
;*
|
||
|
;* The main intent of the macro file is to enable programmers
|
||
|
;* to write _one_ set of source that can be assembled to run
|
||
|
;* in either 16 bit real and protected modes or 32 bit
|
||
|
;* protected mode without the need to riddle the code with
|
||
|
;* 'if flatmodel' style conditional assembly (it is still there
|
||
|
;* but nicely hidden by a macro layer that enhances the
|
||
|
;* readability and understandability of the resulting code).
|
||
|
;*
|
||
|
;****************************************************************************
|
||
|
|
||
|
; Include the appropriate version in here depending on the assembler. NASM
|
||
|
; appears to always try and parse code, even if it is in a non-compiling
|
||
|
; block of a ifdef expression, and hence crashes if we include the TASM
|
||
|
; macro package in the same header file. Hence we split the macros up into
|
||
|
; two separate header files.
|
||
|
|
||
|
ifdef __NASM_MAJOR__
|
||
|
|
||
|
;============================================================================
|
||
|
; Macro package when compiling with NASM.
|
||
|
;============================================================================
|
||
|
|
||
|
; Turn off underscores for globals if disabled for all externals
|
||
|
|
||
|
%ifdef __NOU__
|
||
|
%define __NOU_VAR__
|
||
|
%endif
|
||
|
|
||
|
; Define the __WINDOWS__ symbol if we are compiling for any Windows
|
||
|
; environment
|
||
|
|
||
|
%ifdef __WINDOWS16__
|
||
|
%define __WINDOWS__ 1
|
||
|
%endif
|
||
|
%ifdef __WINDOWS32__
|
||
|
%define __WINDOWS__ 1
|
||
|
%define __WINDOWS32_386__ 1
|
||
|
%endif
|
||
|
|
||
|
; Macros for accessing 'generic' registers
|
||
|
|
||
|
%ifdef __FLAT__
|
||
|
%idefine _ax eax
|
||
|
%idefine _bx ebx
|
||
|
%idefine _cx ecx
|
||
|
%idefine _dx edx
|
||
|
%idefine _si esi
|
||
|
%idefine _di edi
|
||
|
%idefine _bp ebp
|
||
|
%idefine _sp esp
|
||
|
%idefine _es
|
||
|
%idefine UCHAR BYTE ; Size of a character
|
||
|
%idefine USHORT WORD ; Size of a short
|
||
|
%idefine UINT DWORD ; Size of an integer
|
||
|
%idefine ULONG DWORD ; Size of a long
|
||
|
%idefine BOOL DWORD ; Size of a boolean
|
||
|
%idefine DPTR DWORD ; Size of a data pointer
|
||
|
%idefine FDPTR FWORD ; Size of a far data pointer
|
||
|
%idefine NDPTR DWORD ; Size of a near data pointer
|
||
|
%idefine CPTR DWORD ; Size of a code pointer
|
||
|
%idefine FCPTR FWORD ; Size of a far code pointer
|
||
|
%idefine NCPTR DWORD ; Size of a near code pointer
|
||
|
%idefine FPTR NEAR ; Distance for function pointers
|
||
|
%idefine DUINT dd ; Declare a integer variable
|
||
|
%idefine intsize 4
|
||
|
%idefine flatmodel 1
|
||
|
%else
|
||
|
%idefine _ax ax
|
||
|
%idefine _bx bx
|
||
|
%idefine _cx cx
|
||
|
%idefine _dx dx
|
||
|
%idefine _si si
|
||
|
%idefine _di di
|
||
|
%idefine _bp bp
|
||
|
%idefine _sp sp
|
||
|
%idefine _es es:
|
||
|
%idefine UCHAR BYTE ; Size of a character
|
||
|
%idefine USHORT WORD ; Size of a short
|
||
|
%idefine UINT WORD ; Size of an integer
|
||
|
%idefine ULONG DWORD ; Size of a long
|
||
|
%idefine BOOL WORD ; Size of a boolean
|
||
|
%idefine DPTR DWORD ; Size of a data pointer
|
||
|
%idefine FDPTR DWORD ; Size of a far data pointer
|
||
|
%idefine NDPTR WORD ; Size of a near data pointer
|
||
|
%idefine CPTR DWORD ; Size of a code pointer
|
||
|
%idefine FCPTR DWORD ; Size of a far code pointer
|
||
|
%idefine NCPTR WORD ; Size of a near code pointer
|
||
|
%idefine FPTR FAR ; Distance for function pointers
|
||
|
%idefine DUINT dw ; Declare a integer variable
|
||
|
%idefine intsize 2
|
||
|
%endif
|
||
|
%idefine invert ~
|
||
|
%idefine offset
|
||
|
%idefine use_nasm
|
||
|
|
||
|
; Convert all jumps to near jumps, since NASM does not so this automatically
|
||
|
|
||
|
%idefine jo jo near
|
||
|
%idefine jno jno near
|
||
|
%idefine jz jz near
|
||
|
%idefine jnz jnz near
|
||
|
%idefine je je near
|
||
|
%idefine jne jne near
|
||
|
%idefine jb jb near
|
||
|
%idefine jbe jbe near
|
||
|
%idefine ja ja near
|
||
|
%idefine jae jae near
|
||
|
%idefine jl jl near
|
||
|
%idefine jle jle near
|
||
|
%idefine jg jg near
|
||
|
%idefine jge jge near
|
||
|
%idefine jc jc near
|
||
|
%idefine jnc jnc near
|
||
|
%idefine js js near
|
||
|
%idefine jns jns near
|
||
|
|
||
|
%ifdef DOUBLE
|
||
|
%idefine REAL QWORD
|
||
|
%idefine DREAL dq
|
||
|
%else
|
||
|
%idefine REAL DWORD
|
||
|
%idefine DREAL dd
|
||
|
%endif
|
||
|
|
||
|
; Boolean truth values (same as those in debug.h)
|
||
|
|
||
|
%idefine False 0
|
||
|
%idefine True 1
|
||
|
%idefine No 0
|
||
|
%idefine Yes 1
|
||
|
%idefine Yes 1
|
||
|
|
||
|
; Macro to be invoked at the start of all modules to set up segments for
|
||
|
; later use. Does nothing for NASM.
|
||
|
|
||
|
%imacro header 1
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to begin a data segment
|
||
|
|
||
|
%imacro begdataseg 1
|
||
|
%ifdef __GNUC__
|
||
|
segment .data public class=DATA use32 flat
|
||
|
%else
|
||
|
%ifdef flatmodel
|
||
|
segment _DATA public align=4 class=DATA use32 flat
|
||
|
%else
|
||
|
segment _DATA public align=4 class=DATA use16
|
||
|
%endif
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to end a data segment
|
||
|
|
||
|
%imacro enddataseg 1
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to begin a code segment
|
||
|
|
||
|
%imacro begcodeseg 1
|
||
|
%ifdef __PIC__
|
||
|
%ifdef __LINUX__
|
||
|
extern _GLOBAL_OFFSET_TABLE_
|
||
|
%else
|
||
|
extern __GLOBAL_OFFSET_TABLE_
|
||
|
%endif
|
||
|
%endif
|
||
|
%ifdef __GNUC__
|
||
|
segment .text public class=CODE use32 flat
|
||
|
%else
|
||
|
%ifdef flatmodel
|
||
|
segment _TEXT public align=16 class=CODE use32 flat
|
||
|
%else
|
||
|
segment %1_TEXT public align=16 class=CODE use16
|
||
|
%endif
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to begin a near code segment
|
||
|
|
||
|
%imacro begcodeseg_near 0
|
||
|
%ifdef __GNUC__
|
||
|
segment .text public class=CODE use32 flat
|
||
|
%else
|
||
|
%ifdef flatmodel
|
||
|
segment _TEXT public align=16 class=CODE use32 flat
|
||
|
%else
|
||
|
segment _TEXT public align=16 class=CODE use16
|
||
|
%endif
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to end a code segment
|
||
|
|
||
|
%imacro endcodeseg 1
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to end a near code segment
|
||
|
|
||
|
%imacro endcodeseg_near 0
|
||
|
%endmacro
|
||
|
|
||
|
; Macro for an extern C symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
%imacro cextern 2
|
||
|
%ifdef __NOU_VAR__
|
||
|
extern %1
|
||
|
%else
|
||
|
extern _%1
|
||
|
%define %1 _%1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro cexternfunc 2
|
||
|
%ifdef __NOU__
|
||
|
extern %1
|
||
|
%else
|
||
|
extern _%1
|
||
|
%define %1 _%1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro for a public C symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
%imacro cpublic 1
|
||
|
%ifdef __NOU_VAR__
|
||
|
global %1
|
||
|
%1:
|
||
|
%else
|
||
|
global _%1
|
||
|
_%1:
|
||
|
%define %1 _%1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro for an global C symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
%imacro cglobal 1
|
||
|
%ifdef __NOU_VAR__
|
||
|
global %1
|
||
|
%else
|
||
|
global _%1
|
||
|
%define %1 _%1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro for an global C function symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
%imacro cglobalfunc 1
|
||
|
%ifdef __PIC__
|
||
|
global %1:function
|
||
|
%else
|
||
|
%ifdef __NOU__
|
||
|
global %1
|
||
|
%else
|
||
|
global _%1
|
||
|
%define %1 _%1
|
||
|
%endif
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to start a C callable function. This will be a far function for
|
||
|
; 16-bit code, and a near function for 32-bit code.
|
||
|
|
||
|
%imacro cprocstatic 1
|
||
|
%push cproc
|
||
|
%1:
|
||
|
%ifdef flatmodel
|
||
|
%stacksize flat
|
||
|
%define ret retn
|
||
|
%else
|
||
|
%stacksize large
|
||
|
%define ret retf
|
||
|
%endif
|
||
|
%assign %$localsize 0
|
||
|
%endmacro
|
||
|
|
||
|
%imacro cprocstart 1
|
||
|
%push cproc
|
||
|
cglobalfunc %1
|
||
|
%1:
|
||
|
%ifdef flatmodel
|
||
|
%stacksize flat
|
||
|
%define ret retn
|
||
|
%else
|
||
|
%stacksize large
|
||
|
%define ret retf
|
||
|
%endif
|
||
|
%assign %$localsize 0
|
||
|
%endmacro
|
||
|
|
||
|
; This macro sets up a procedure to be exported from a 16 bit DLL. Since the
|
||
|
; calling conventions are always _far _pascal for 16 bit DLL's, we actually
|
||
|
; rename this routine with an extra underscore with 'C' calling conventions
|
||
|
; and a small DLL stub will be provided by the high level code to call the
|
||
|
; assembler routine.
|
||
|
|
||
|
%imacro cprocstartdll16 1
|
||
|
%ifdef __WINDOWS16__
|
||
|
cprocstart _%1
|
||
|
%else
|
||
|
cprocstart %1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to start a C callable near function.
|
||
|
|
||
|
%imacro cprocnear 1
|
||
|
%push cproc
|
||
|
cglobalfunc %1
|
||
|
%1:
|
||
|
%define ret retn
|
||
|
%ifdef flatmodel
|
||
|
%stacksize flat
|
||
|
%else
|
||
|
%stacksize small
|
||
|
%endif
|
||
|
%assign %$localsize 0
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to start a C callable far function.
|
||
|
|
||
|
%imacro cprocfar 1
|
||
|
%push cproc
|
||
|
cglobalfunc %1
|
||
|
%1:
|
||
|
%define ret retf
|
||
|
%ifdef flatmodel
|
||
|
%stacksize flat
|
||
|
%else
|
||
|
%stacksize large
|
||
|
%endif
|
||
|
%assign %$localsize 0
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to end a C function
|
||
|
|
||
|
%imacro cprocend 0
|
||
|
%pop
|
||
|
%endmacro
|
||
|
|
||
|
; Macros for entering and exiting C callable functions. Note that we must
|
||
|
; always save and restore the SI and DI registers for C functions, and for
|
||
|
; 32 bit C functions we also need to save and restore EBX and clear the
|
||
|
; direction flag.
|
||
|
|
||
|
%imacro enter_c 0
|
||
|
push _bp
|
||
|
mov _bp,_sp
|
||
|
%ifnidn %$localsize,0
|
||
|
sub _sp,%$localsize
|
||
|
%endif
|
||
|
%ifdef flatmodel
|
||
|
push ebx
|
||
|
%endif
|
||
|
push _si
|
||
|
push _di
|
||
|
%endmacro
|
||
|
|
||
|
%imacro leave_c 0
|
||
|
pop _di
|
||
|
pop _si
|
||
|
%ifdef flatmodel
|
||
|
pop ebx
|
||
|
cld
|
||
|
%endif
|
||
|
%ifnidn %$localsize,0
|
||
|
mov _sp,_bp
|
||
|
%endif
|
||
|
pop _bp
|
||
|
%endmacro
|
||
|
|
||
|
%imacro use_ebx 0
|
||
|
%ifdef flatmodel
|
||
|
push ebx
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro unuse_ebx 0
|
||
|
%ifdef flatmodel
|
||
|
pop ebx
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macros for saving and restoring the value of DS,ES,FS,GS when it is to
|
||
|
; be used in assembly routines. This evaluates to nothing in the flat memory
|
||
|
; model, but is saves and restores DS in the large memory model.
|
||
|
|
||
|
%imacro use_ds 0
|
||
|
%ifndef flatmodel
|
||
|
push ds
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro unuse_ds 0
|
||
|
%ifndef flatmodel
|
||
|
pop ds
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro use_es 0
|
||
|
%ifndef flatmodel
|
||
|
push es
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro unuse_es 0
|
||
|
%ifndef flatmodel
|
||
|
pop es
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macros for loading the address of a data pointer into a segment and
|
||
|
; index register pair. The %imacro explicitly loads DS or ES in the 16 bit
|
||
|
; memory model, or it simply loads the offset into the register in the flat
|
||
|
; memory model since DS and ES always point to all addressable memory. You
|
||
|
; must use the correct _REG (ie: _BX) %imacros for documentation purposes.
|
||
|
|
||
|
%imacro _lds 2
|
||
|
%ifdef flatmodel
|
||
|
mov %1,%2
|
||
|
%else
|
||
|
lds %1,%2
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro _les 2
|
||
|
%ifdef flatmodel
|
||
|
mov %1,%2
|
||
|
%else
|
||
|
les %1,%2
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macros for adding and subtracting a value from registers. Two value are
|
||
|
; provided, one for 16 bit modes and another for 32 bit modes (the extended
|
||
|
; register is used in 32 bit modes).
|
||
|
|
||
|
%imacro _add 3
|
||
|
%ifdef flatmodel
|
||
|
add e%1, %3
|
||
|
%else
|
||
|
add %1, %2
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro _sub 3
|
||
|
%ifdef flatmodel
|
||
|
sub e%1, %3
|
||
|
%else
|
||
|
sub %1, %2
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to clear the high order word for the 32 bit extended registers.
|
||
|
; This is used to convert an unsigned 16 bit value to an unsigned 32 bit
|
||
|
; value, and will evaluate to nothing in 16 bit modes.
|
||
|
|
||
|
%imacro clrhi 1
|
||
|
%ifdef flatmodel
|
||
|
movzx e%1,%1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro sgnhi 1
|
||
|
%ifdef flatmodel
|
||
|
movsx e%1,%1
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to load an extended register with an integer value in either mode
|
||
|
|
||
|
%imacro loadint 2
|
||
|
%ifdef flatmodel
|
||
|
mov e%1,%2
|
||
|
%else
|
||
|
xor e%1,e%1
|
||
|
mov %1,%2
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macros to load and store integer values with string instructions
|
||
|
|
||
|
%imacro LODSINT 0
|
||
|
%ifdef flatmodel
|
||
|
lodsd
|
||
|
%else
|
||
|
lodsw
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
%imacro STOSINT 0
|
||
|
%ifdef flatmodel
|
||
|
stosd
|
||
|
%else
|
||
|
stosw
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Macros to provide resb, resw, resd compatibility with NASM
|
||
|
|
||
|
%imacro dclb 1
|
||
|
times %1 db 0
|
||
|
%endmacro
|
||
|
|
||
|
%imacro dclw 1
|
||
|
times %1 dw 0
|
||
|
%endmacro
|
||
|
|
||
|
%imacro dcld 1
|
||
|
times %1 dd 0
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to get the addres of the GOT for Linux/FreeBSD shared
|
||
|
; libraries into the EBX register.
|
||
|
|
||
|
%imacro get_GOT 1
|
||
|
call %%getgot
|
||
|
%%getgot: pop %1
|
||
|
add %1,_GLOBAL_OFFSET_TABLE_+$$-%%getgot wrt ..gotpc
|
||
|
%endmacro
|
||
|
|
||
|
; Macro to get the address of a *local* variable that is global to
|
||
|
; a single module in a manner that will work correctly when compiled
|
||
|
; into a Linux shared library. Note that this will *not* work for
|
||
|
; variables that are defined as global to all modules. For that
|
||
|
; use the LEA_G macro
|
||
|
|
||
|
%macro LEA_L 2
|
||
|
%ifdef __PIC__
|
||
|
get_GOT %1
|
||
|
lea %1,[%1+%2 wrt ..gotoff]
|
||
|
%else
|
||
|
lea %1,[%2]
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; Same macro as above but for global variables public to *all*
|
||
|
; modules.
|
||
|
|
||
|
%macro LEA_G 2
|
||
|
%ifdef __PIC__
|
||
|
get_GOT %1
|
||
|
mov %1,[%1+%2 wrt ..got]
|
||
|
%else
|
||
|
lea %1,[%2]
|
||
|
%endif
|
||
|
%endmacro
|
||
|
|
||
|
; macros to declare assembler function stubs for function structures
|
||
|
|
||
|
%imacro BEGIN_STUBS_DEF 2
|
||
|
begdataseg _STUBS
|
||
|
%ifdef __NOU_VAR__
|
||
|
extern %1
|
||
|
%define STUBS_START %1
|
||
|
%else
|
||
|
extern _%1
|
||
|
%define STUBS_START _%1
|
||
|
%endif
|
||
|
enddataseg _STUBS
|
||
|
begcodeseg _STUBS
|
||
|
%assign off %2
|
||
|
%endmacro
|
||
|
|
||
|
%imacro DECLARE_STUB 1
|
||
|
%ifdef __PIC__
|
||
|
global %1:function
|
||
|
%1:
|
||
|
get_GOT eax
|
||
|
mov eax,[eax+STUBS_START wrt ..got]
|
||
|
jmp [eax+off]
|
||
|
%else
|
||
|
%ifdef __NOU__
|
||
|
global %1
|
||
|
%1:
|
||
|
%else
|
||
|
global _%1
|
||
|
_%1:
|
||
|
%endif
|
||
|
jmp [DWORD STUBS_START+off]
|
||
|
%endif
|
||
|
%assign off off+4
|
||
|
%endmacro
|
||
|
|
||
|
%imacro SKIP_STUB 1
|
||
|
%assign off off+4
|
||
|
%endmacro
|
||
|
|
||
|
%imacro DECLARE_STDCALL 2
|
||
|
%ifdef STDCALL_MANGLE
|
||
|
global _%1@%2
|
||
|
_%1@%2:
|
||
|
%else
|
||
|
%ifdef STDCALL_USCORE
|
||
|
global _%1
|
||
|
_%1:
|
||
|
%else
|
||
|
global %1
|
||
|
%1:
|
||
|
%endif
|
||
|
%endif
|
||
|
jmp [DWORD STUBS_START+off]
|
||
|
%assign off off+4
|
||
|
%endmacro
|
||
|
|
||
|
%imacro END_STUBS_DEF 0
|
||
|
endcodeseg _STUBS
|
||
|
%endmacro
|
||
|
|
||
|
; macros to declare assembler import stubs for binary loadable drivers
|
||
|
|
||
|
%imacro BEGIN_IMPORTS_DEF 1
|
||
|
BEGIN_STUBS_DEF %1,4
|
||
|
%endmacro
|
||
|
|
||
|
%imacro DECLARE_IMP 2
|
||
|
DECLARE_STUB %1
|
||
|
%endmacro
|
||
|
|
||
|
%imacro SKIP_IMP 2
|
||
|
SKIP_STUB %1
|
||
|
%endmacro
|
||
|
|
||
|
%imacro SKIP_IMP2 1
|
||
|
DECLARE_STUB %1
|
||
|
%endmacro
|
||
|
|
||
|
%imacro SKIP_IMP3 1
|
||
|
SKIP_STUB %1
|
||
|
%endmacro
|
||
|
|
||
|
%imacro END_IMPORTS_DEF 0
|
||
|
END_STUBS_DEF
|
||
|
%endmacro
|
||
|
|
||
|
else ; __NASM_MAJOR__
|
||
|
|
||
|
;============================================================================
|
||
|
; Macro package when compiling with TASM.
|
||
|
;============================================================================
|
||
|
|
||
|
; Turn off underscores for globals if disabled for all externals
|
||
|
|
||
|
ifdef __NOU__
|
||
|
__NOU_VAR__ = 1
|
||
|
endif
|
||
|
|
||
|
; Define the __WINDOWS__ symbol if we are compiling for any Windows
|
||
|
; environment
|
||
|
|
||
|
ifdef __WINDOWS16__
|
||
|
__WINDOWS__ = 1
|
||
|
endif
|
||
|
ifdef __WINDOWS32__
|
||
|
__WINDOWS__ = 1
|
||
|
__WINDOWS32_386__ = 1
|
||
|
endif
|
||
|
ifdef __WIN386__
|
||
|
__WINDOWS__ = 1
|
||
|
__WINDOWS32_386__ = 1
|
||
|
endif
|
||
|
ifdef __VXD__
|
||
|
__WINDOWS__ = 1
|
||
|
__WINDOWS32_386__ = 1
|
||
|
MASM
|
||
|
.386
|
||
|
NO_SEGMENTS = 1
|
||
|
include vmm.inc ; IGNORE DEPEND
|
||
|
include vsegment.inc ; IGNORE DEPEND
|
||
|
IDEAL
|
||
|
endif
|
||
|
|
||
|
; Macros for accessing 'generic' registers
|
||
|
|
||
|
ifdef __FLAT__
|
||
|
_ax EQU eax ; EAX is used for accumulator
|
||
|
_bx EQU ebx ; EBX is used for accumulator
|
||
|
_cx EQU ecx ; ECX is used for looping
|
||
|
_dx EQU edx ; EDX is used for data register
|
||
|
_si EQU esi ; ESI is the source index register
|
||
|
_di EQU edi ; EDI is the destination index register
|
||
|
_bp EQU ebp ; EBP is used for base pointer register
|
||
|
_sp EQU esp ; ESP is used for stack pointer register
|
||
|
_es EQU ; ES and DS are the same in 32 bit PM
|
||
|
typedef UCHAR BYTE ; Size of a character
|
||
|
typedef USHORT WORD ; Size of a short
|
||
|
typedef UINT DWORD ; Size of an integer
|
||
|
typedef ULONG DWORD ; Size of a long
|
||
|
typedef BOOL DWORD ; Size of a boolean
|
||
|
typedef DPTR DWORD ; Size of a data pointer
|
||
|
typedef FDPTR FWORD ; Size of a far data pointer
|
||
|
typedef NDPTR DWORD ; Size of a near data pointer
|
||
|
typedef CPTR DWORD ; Size of a code pointer
|
||
|
typedef FCPTR FWORD ; Size of a far code pointer
|
||
|
typedef NCPTR DWORD ; Size of a near code pointer
|
||
|
typedef DUINT DWORD ; Declare a integer variable
|
||
|
FPTR EQU NEAR ; Distance for function pointers
|
||
|
intsize = 4 ; Size of an integer
|
||
|
flatmodel = 1 ; This is a flat memory model
|
||
|
P386 ; Turn on 386 code generation
|
||
|
MODEL FLAT ; Set up for 32 bit simplified FLAT model
|
||
|
else
|
||
|
_ax EQU ax ; AX is used for accumulator
|
||
|
_bx EQU bx ; BX is used for accumulator
|
||
|
_cx EQU cx ; CX is used for looping
|
||
|
_dx EQU dx ; DX is used for data register
|
||
|
_si EQU si ; SI is the source index register
|
||
|
_di EQU di ; DI is the destination index register
|
||
|
_bp EQU bp ; BP is used for base pointer register
|
||
|
_sp EQU sp ; SP is used for stack pointer register
|
||
|
_es EQU es: ; ES is used for segment override
|
||
|
typedef UCHAR BYTE ; Size of a character
|
||
|
typedef USHORT WORD ; Size of a short
|
||
|
typedef UINT WORD ; Size of an integer
|
||
|
typedef ULONG DWORD ; Size of a long
|
||
|
typedef BOOL WORD ; Size of a boolean
|
||
|
typedef DPTR DWORD ; Size of a data pointer
|
||
|
typedef FDPTR DWORD ; Size of a far data pointer
|
||
|
typedef NDPTR WORD ; Size of a near data pointer
|
||
|
typedef CPTR DWORD ; Size of a code pointer
|
||
|
typedef FCPTR DWORD ; Size of a far code pointer
|
||
|
typedef NCPTR WORD ; Size of a near code pointer
|
||
|
typedef DUINT WORD ; Declare a integer variable
|
||
|
FPTR EQU FAR ; Distance for function pointers
|
||
|
intsize = 2 ; Size of an integer
|
||
|
P386 ; Turn on 386 code generation
|
||
|
endif
|
||
|
invert EQU not
|
||
|
|
||
|
; Provide a typedef for real floating point numbers
|
||
|
|
||
|
ifdef DOUBLE
|
||
|
typedef REAL QWORD
|
||
|
typedef DREAL QWORD
|
||
|
else
|
||
|
typedef REAL DWORD
|
||
|
typedef DREAL DWORD
|
||
|
endif
|
||
|
|
||
|
; Macros to access the floating point stack registers to convert them
|
||
|
; from NASM style to TASM style
|
||
|
|
||
|
st0 EQU st(0)
|
||
|
st1 EQU st(1)
|
||
|
st2 EQU st(2)
|
||
|
st3 EQU st(3)
|
||
|
st4 EQU st(4)
|
||
|
st5 EQU st(5)
|
||
|
st6 EQU st(6)
|
||
|
st7 EQU st(7)
|
||
|
st8 EQU st(8)
|
||
|
|
||
|
; Boolean truth values (same as those in debug.h)
|
||
|
|
||
|
ifndef __VXD__
|
||
|
False = 0
|
||
|
True = 1
|
||
|
No = 0
|
||
|
Yes = 1
|
||
|
Yes = 1
|
||
|
endif
|
||
|
|
||
|
; Macros for the _DATA data segment. This segment contains initialised data.
|
||
|
|
||
|
MACRO begdataseg name
|
||
|
ifdef __VXD__
|
||
|
MASM
|
||
|
VXD_LOCKED_DATA_SEG
|
||
|
IDEAL
|
||
|
else
|
||
|
ifdef flatmodel
|
||
|
DATASEG
|
||
|
else
|
||
|
SEGMENT _DATA DWORD PUBLIC USE16 'DATA'
|
||
|
endif
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO enddataseg name
|
||
|
ifdef __VXD__
|
||
|
MASM
|
||
|
VXD_LOCKED_DATA_ENDS
|
||
|
IDEAL
|
||
|
else
|
||
|
ifndef flatmodel
|
||
|
ENDS _DATA
|
||
|
endif
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro for the main code segment.
|
||
|
|
||
|
MACRO begcodeseg name
|
||
|
ifdef __VXD__
|
||
|
MASM
|
||
|
VXD_LOCKED_CODE_SEG
|
||
|
IDEAL
|
||
|
else
|
||
|
ifdef flatmodel
|
||
|
CODESEG
|
||
|
ASSUME CS:FLAT,DS:FLAT,SS:FLAT
|
||
|
else
|
||
|
SEGMENT &name&_TEXT PARA PUBLIC USE16 'CODE'
|
||
|
ASSUME CS:&name&_TEXT,DS:_DATA
|
||
|
endif
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro for a near code segment
|
||
|
|
||
|
MACRO begcodeseg_near
|
||
|
ifdef flatmodel
|
||
|
CODESEG
|
||
|
ASSUME CS:FLAT,DS:FLAT,SS:FLAT
|
||
|
else
|
||
|
SEGMENT _TEXT PARA PUBLIC USE16 'CODE'
|
||
|
ASSUME CS:_TEXT,DS:_DATA
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO endcodeseg name
|
||
|
ifdef __VXD__
|
||
|
MASM
|
||
|
VXD_LOCKED_CODE_ENDS
|
||
|
IDEAL
|
||
|
else
|
||
|
ifndef flatmodel
|
||
|
ENDS &name&_TEXT
|
||
|
endif
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO endcodeseg_near
|
||
|
ifndef flatmodel
|
||
|
ENDS _TEXT
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro to be invoked at the start of all modules to set up segments for
|
||
|
; later use.
|
||
|
|
||
|
MACRO header name
|
||
|
begdataseg name
|
||
|
enddataseg name
|
||
|
ENDM
|
||
|
|
||
|
; Macro for an extern C symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
MACRO cextern name,size
|
||
|
ifdef __NOU_VAR__
|
||
|
EXTRN name:size
|
||
|
else
|
||
|
EXTRN _&name&:size
|
||
|
name EQU _&name&
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO cexternfunc name,size
|
||
|
ifdef __NOU__
|
||
|
EXTRN name:size
|
||
|
else
|
||
|
EXTRN _&name&:size
|
||
|
name EQU _&name&
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO stdexternfunc name,num_args,size
|
||
|
ifdef STDCALL_MANGLE
|
||
|
EXTRN _&name&@&num_args&:size
|
||
|
name EQU _&name&@&num_args
|
||
|
else
|
||
|
EXTRN name:size
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro for a public C symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
MACRO cpublic name
|
||
|
ifdef __NOU_VAR__
|
||
|
name:
|
||
|
PUBLIC name
|
||
|
else
|
||
|
_&name&:
|
||
|
PUBLIC _&name&
|
||
|
name EQU _&name&
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro for an global C symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
MACRO cglobal name
|
||
|
ifdef __NOU_VAR__
|
||
|
PUBLIC name
|
||
|
else
|
||
|
PUBLIC _&name&
|
||
|
name EQU _&name&
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro for an global C function symbol. If the C compiler requires leading
|
||
|
; underscores, then the underscores are added to the symbol names, otherwise
|
||
|
; they are left off. The symbol name is referenced in the assembler code
|
||
|
; using the non-underscored symbol name.
|
||
|
|
||
|
MACRO cglobalfunc name
|
||
|
ifdef __NOU__
|
||
|
PUBLIC name
|
||
|
else
|
||
|
PUBLIC _&name&
|
||
|
name EQU _&name&
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro to start a C callable function. This will be a far function for
|
||
|
; 16-bit code, and a near function for 32-bit code.
|
||
|
|
||
|
MACRO cprocstatic name ; Set up model independant private proc
|
||
|
ifdef flatmodel
|
||
|
PROC name NEAR
|
||
|
else
|
||
|
PROC name FAR
|
||
|
endif
|
||
|
LocalSize = 0
|
||
|
ENDM
|
||
|
|
||
|
MACRO cprocstart name ; Set up model independant proc
|
||
|
ifdef flatmodel
|
||
|
ifdef __NOU__
|
||
|
PROC name NEAR
|
||
|
else
|
||
|
PROC _&name& NEAR
|
||
|
endif
|
||
|
else
|
||
|
ifdef __NOU__
|
||
|
PROC name FAR
|
||
|
else
|
||
|
PROC _&name& FAR
|
||
|
endif
|
||
|
endif
|
||
|
LocalSize = 0
|
||
|
cglobalfunc name
|
||
|
ENDM
|
||
|
|
||
|
MACRO cprocnear name ; Set up near proc
|
||
|
ifdef __NOU__
|
||
|
PROC name NEAR
|
||
|
else
|
||
|
PROC _&name& NEAR
|
||
|
endif
|
||
|
LocalSize = 0
|
||
|
cglobalfunc name
|
||
|
ENDM
|
||
|
|
||
|
MACRO cprocfar name ; Set up far proc
|
||
|
ifdef __NOU__
|
||
|
PROC name FAR
|
||
|
else
|
||
|
PROC _&name& FAR
|
||
|
endif
|
||
|
LocalSize = 0
|
||
|
cglobalfunc name
|
||
|
ENDM
|
||
|
|
||
|
MACRO cprocend ; End procedure macro
|
||
|
ENDP
|
||
|
ENDM
|
||
|
|
||
|
; This macro sets up a procedure to be exported from a 16 bit DLL. Since the
|
||
|
; calling conventions are always _far _pascal for 16 bit DLL's, we actually
|
||
|
; rename this routine with an extra underscore with 'C' calling conventions
|
||
|
; and a small DLL stub will be provided by the high level code to call the
|
||
|
; assembler routine.
|
||
|
|
||
|
MACRO cprocstartdll16 name
|
||
|
ifdef __WINDOWS16__
|
||
|
cprocstart _&name&
|
||
|
else
|
||
|
cprocstart name
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macros for entering and exiting C callable functions. Note that we must
|
||
|
; always save and restore the SI and DI registers for C functions, and for
|
||
|
; 32 bit C functions we also need to save and restore EBX and clear the
|
||
|
; direction flag.
|
||
|
|
||
|
MACRO save_c_regs
|
||
|
ifdef flatmodel
|
||
|
push ebx
|
||
|
endif
|
||
|
push _si
|
||
|
push _di
|
||
|
ENDM
|
||
|
|
||
|
MACRO enter_c
|
||
|
push _bp
|
||
|
mov _bp,_sp
|
||
|
IFDIFI <LocalSize>,<0>
|
||
|
sub _sp,LocalSize
|
||
|
ENDIF
|
||
|
save_c_regs
|
||
|
ENDM
|
||
|
|
||
|
MACRO restore_c_regs
|
||
|
pop _di
|
||
|
pop _si
|
||
|
ifdef flatmodel
|
||
|
pop ebx
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO leave_c
|
||
|
restore_c_regs
|
||
|
cld
|
||
|
IFDIFI <LocalSize>,<0>
|
||
|
mov _sp,_bp
|
||
|
ENDIF
|
||
|
pop _bp
|
||
|
ENDM
|
||
|
|
||
|
MACRO use_ebx
|
||
|
ifdef flatmodel
|
||
|
push ebx
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO unuse_ebx
|
||
|
ifdef flatmodel
|
||
|
pop ebx
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macros for saving and restoring the value of DS,ES,FS,GS when it is to
|
||
|
; be used in assembly routines. This evaluates to nothing in the flat memory
|
||
|
; model, but is saves and restores DS in the large memory model.
|
||
|
|
||
|
MACRO use_ds
|
||
|
ifndef flatmodel
|
||
|
push ds
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO unuse_ds
|
||
|
ifndef flatmodel
|
||
|
pop ds
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO use_es
|
||
|
ifndef flatmodel
|
||
|
push es
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO unuse_es
|
||
|
ifndef flatmodel
|
||
|
pop es
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macros for loading the address of a data pointer into a segment and
|
||
|
; index register pair. The macro explicitly loads DS or ES in the 16 bit
|
||
|
; memory model, or it simply loads the offset into the register in the flat
|
||
|
; memory model since DS and ES always point to all addressable memory. You
|
||
|
; must use the correct _REG (ie: _BX) macros for documentation purposes.
|
||
|
|
||
|
MACRO _lds reg, addr
|
||
|
ifdef flatmodel
|
||
|
mov reg,addr
|
||
|
else
|
||
|
lds reg,addr
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO _les reg, addr
|
||
|
ifdef flatmodel
|
||
|
mov reg,addr
|
||
|
else
|
||
|
les reg,addr
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macros for adding and subtracting a value from registers. Two value are
|
||
|
; provided, one for 16 bit modes and another for 32 bit modes (the extended
|
||
|
; register is used in 32 bit modes).
|
||
|
|
||
|
MACRO _add reg, val16, val32
|
||
|
ifdef flatmodel
|
||
|
add e®&, val32
|
||
|
else
|
||
|
add reg, val16
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO _sub reg, val16, val32
|
||
|
ifdef flatmodel
|
||
|
sub e®&, val32
|
||
|
else
|
||
|
sub reg, val16
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro to clear the high order word for the 32 bit extended registers.
|
||
|
; This is used to convert an unsigned 16 bit value to an unsigned 32 bit
|
||
|
; value, and will evaluate to nothing in 16 bit modes.
|
||
|
|
||
|
MACRO clrhi reg
|
||
|
ifdef flatmodel
|
||
|
movzx e®&,reg
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO sgnhi reg
|
||
|
ifdef flatmodel
|
||
|
movsx e®&,reg
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macro to load an extended register with an integer value in either mode
|
||
|
|
||
|
MACRO loadint reg,val
|
||
|
ifdef flatmodel
|
||
|
mov e®&,val
|
||
|
else
|
||
|
xor e®&,e®&
|
||
|
mov reg,val
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macros to load and store integer values with string instructions
|
||
|
|
||
|
MACRO LODSINT
|
||
|
ifdef flatmodel
|
||
|
lodsd
|
||
|
else
|
||
|
lodsw
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
MACRO STOSINT
|
||
|
ifdef flatmodel
|
||
|
stosd
|
||
|
else
|
||
|
stosw
|
||
|
endif
|
||
|
ENDM
|
||
|
|
||
|
; Macros to provide resb, resw, resd compatibility with NASM
|
||
|
|
||
|
MACRO dclb count
|
||
|
db count dup (0)
|
||
|
ENDM
|
||
|
|
||
|
MACRO dclw count
|
||
|
dw count dup (0)
|
||
|
ENDM
|
||
|
|
||
|
MACRO dcld count
|
||
|
dd count dup (0)
|
||
|
ENDM
|
||
|
|
||
|
; Macros to provide resb, resw, resd compatibility with NASM
|
||
|
|
||
|
MACRO resb count
|
||
|
db count dup (?)
|
||
|
ENDM
|
||
|
|
||
|
MACRO resw count
|
||
|
dw count dup (?)
|
||
|
ENDM
|
||
|
|
||
|
MACRO resd count
|
||
|
dd count dup (?)
|
||
|
ENDM
|
||
|
|
||
|
; Macros to declare assembler stubs for function structures
|
||
|
|
||
|
MACRO BEGIN_STUBS_DEF name, firstOffset
|
||
|
begdataseg _STUBS
|
||
|
ifdef __NOU_VAR__
|
||
|
EXTRN name:DWORD
|
||
|
STUBS_START = name
|
||
|
else
|
||
|
EXTRN _&name&:DWORD
|
||
|
name EQU _&name&
|
||
|
STUBS_START = _&name
|
||
|
endif
|
||
|
enddataseg _STUBS
|
||
|
begcodeseg _STUBS
|
||
|
off = firstOffset
|
||
|
ENDM
|
||
|
|
||
|
MACRO DECLARE_STUB name
|
||
|
ifdef __NOU__
|
||
|
name:
|
||
|
PUBLIC name
|
||
|
else
|
||
|
_&name:
|
||
|
PUBLIC _&name
|
||
|
endif
|
||
|
jmp [DWORD STUBS_START+off]
|
||
|
off = off + 4
|
||
|
ENDM
|
||
|
|
||
|
MACRO SKIP_STUB name
|
||
|
off = off + 4
|
||
|
ENDM
|
||
|
|
||
|
MACRO DECLARE_STDCALL name,num_args
|
||
|
ifdef STDCALL_MANGLE
|
||
|
_&name&@&num_args&:
|
||
|
PUBLIC _&name&@&num_args&
|
||
|
else
|
||
|
name:
|
||
|
PUBLIC name
|
||
|
endif
|
||
|
jmp [DWORD STUBS_START+off]
|
||
|
off = off + 4
|
||
|
ENDM
|
||
|
|
||
|
MACRO END_STUBS_DEF
|
||
|
endcodeseg _STUBS
|
||
|
ENDM
|
||
|
|
||
|
MACRO BEGIN_IMPORTS_DEF name
|
||
|
BEGIN_STUBS_DEF name,4
|
||
|
ENDM
|
||
|
|
||
|
ifndef LOCAL_DECLARE_IMP
|
||
|
MACRO DECLARE_IMP name, numArgs
|
||
|
DECLARE_STUB name
|
||
|
ENDM
|
||
|
|
||
|
MACRO SKIP_IMP name
|
||
|
SKIP_STUB name
|
||
|
ENDM
|
||
|
|
||
|
MACRO SKIP_IMP2 name, numArgs
|
||
|
DECLARE_STUB name
|
||
|
ENDM
|
||
|
|
||
|
MACRO SKIP_IMP3 name
|
||
|
SKIP_STUB name
|
||
|
ENDM
|
||
|
endif
|
||
|
|
||
|
MACRO END_IMPORTS_DEF
|
||
|
END_STUBS_DEF
|
||
|
ENDM
|
||
|
|
||
|
MACRO LEA_L reg,name
|
||
|
lea reg,[name]
|
||
|
ENDM
|
||
|
|
||
|
MACRO LEA_G reg,name
|
||
|
lea reg,[name]
|
||
|
ENDM
|
||
|
|
||
|
endif
|
||
|
|