在NASMX中使用局部变量。

理论:

  在我的这个贴里http://bbs.pediy.com/showthread.php?t=96753是使用全局变量实现的,特别是对结构体都是在数据段内先声明再使用。全局变量的使用使得程序会占用更多的内存,而且会影响程序的结构化和数据隐藏。所以局部变量是结构化程序设计数据隐藏的唯一方法。下面我将使用局部变量重新实现一个标准的窗口。

  NASMX本身为我们提供了对一般数据类型实现局部变量的方法:

locals
  local param, 数据类型
endlocals

  但是在这个框架下只是实现了一些基本的数据类型的局部变量声明和使用,如byte,word,dword,qword。所以如果想要实现支持像结构体、数组等数据类型的话,就要修改一下nasmx.inc这个宏文件了。它里面都是用宏定义的高级语法特性,对这些特性都了解了也就能基本理解NASMX的语法了。我们需要将下面这段内容修改一下:

%imacro LOCAL 1-2
  %ifidni %2, qword
    %assign %$locnt 8+%$locnt
  %elifidni %2, dword
    %assign %$locnt 4+%$locnt
  %elifidni %2, word
    %assign %$locnt 2+%$locnt
  %elifidni %2, byte
    %assign %$locnt 1+%$locnt
  %else            
    %assign %$locnt %2+%$locnt            
  %endif
  %1 EQU %$locnt
%endmacro

  %else              
    %assign %$locnt %2+%$locnt
  这两行就是实现支持任意长度局部变量的宏定义。

  locals/endlocals块内所声明局部变量的使用:分两种情况,一、如果是取局部变量值的话,需要使用var()宏来实现。二、如果是取局部变量的实际地址,则需要使用lea命令来实现,这一点一定要注意分清!

  下面看一下重新编写的标准窗口程序:

%include '..\inc\nasmx.inc'
%include '..\inc\win32\windows.inc'
%include '..\inc\win32\kernel32.inc'
%include '..\inc\win32\user32.inc'

entry    start

[section .bss]
    hInstance:   resd 1
  lpCommandLine: resd 1

[section .data]
    szTitle:    db    "My First Window", 0x0
    szClass:    db    "FirstWindow", 0x0

[section .code]
proc    start

    invoke   GetModuleHandleA, dword NULL    
    mov      [hInstance], eax          
  
  invoke  GetCommandLineA            
  mov    [lpCommandLine], eax

    invoke   WinMain, dword [hInstance], dword NULL, dword lpCommandLine, dword SW_SHOWNORMAL    
    invoke   ExitProcess, dword NULL      
    ret

endproc

proc    WinMain
hinst    argd        ; Current instance handle
hpinst   argd        ; Previous instance handle
cmdln    argd        ; Command line arguments
dwshow   argd        ; Display style

  locals
    local  wc, WNDCLASSEX_size        ;以数据结构长度声明一个结构体,自己把nasm.inc中的local宏改了一下,      
    local  message, MSG_size        ;让它可以支持任意长度的数据    
    local  hWnd, dword
  endlocals

    mov     var(wc + WNDCLASSEX.cbSize), dword WNDCLASSEX_size            ;WNDCLASSEX.cbSize
  mov    var(wc + WNDCLASSEX.style), dword CS_HREDRAW | CS_VREDRAW        ;WNDCLASSEX.style
  mov     var(wc + WNDCLASSEX.lpfnWndProc), dword WndProc              ;WNDCLASSEX.lpfnWndProc
  mov    var(wc + WNDCLASSEX.cbClsExtra), dword NULL                ;WNDCLASSEX.cbClsExtra
  mov    var(wc + WNDCLASSEX.cbWndExtra), dword NULL                ;WNDCLASSEX.cbWndExtra
  push  dword [hInstance]
  pop    dword var(wc + WNDCLASSEX.hInstance)                  ;WNDCLASSEX.hInstance
    mov     var(wc + WNDCLASSEX.hbrBackground), dword COLOR_WINDOW + 1        ;WNDCLASSEX.hbrBackground
  mov    var(wc + WNDCLASSEX.lpszMenuName), dword NULL              ;WNDCLASSEX.lpszMenuName
    mov     var(wc + WNDCLASSEX.lpszClassName), dword szClass            ;WNDCLASSEX.lpszClassName
  invoke  LoadIconA, dword NULL, IDI_APPLICATION
  mov    var(wc + WNDCLASSEX.hIcon), eax                      ;WNDCLASSEX.hIcon
    mov     var(wc + WNDCLASSEX.hIconSm), eax                    ;WNDCLASSEX.hIconSm
  invoke  LoadCursorA, dword NULL, IDC_ARROW            
  mov    var(wc + WNDCLASSEX.hCursor), eax                    ;WNDCLASSEX.hCursor

    lea    eax, var(wc)                ;获取结构体wc的实际地址传入RegisterClassExA函数
  invoke   RegisterClassExA, eax
  
    invoke   CreateWindowExA, dword NULL, dword szClass, dword szTitle, dword WS_OVERLAPPEDWINDOW + WS_VISIBLE, dword CW_USEDEFAULT, dword CW_USEDEFAULT, dword CW_USEDEFAULT, dword CW_USEDEFAULT, dword NULL, dword NULL, dword argv(hinst), dword NULL
    mov      var(hWnd), eax

    invoke   ShowWindow, dword var(hWnd), dword argv(dwshow)      
    invoke   UpdateWindow, dword var(hWnd)              

    .WHILE:                            
        lea     ebx, var(message)            ;获取结构体message的实际地址
    invoke   GetMessageA, ebx, dword NULL, dword NULL, dword NULL
        cmp      eax, dword 0
        je       .ENDW        
    invoke   TranslateMessage, ebx
        invoke   DispatchMessageA, ebx
        jmp      .WHILE
    .ENDW:

    mov      eax, dword var(message + MSG.wParam)      ;MSG.wParam
    ret

endproc

proc    WndProc
hwnd    argd        ; Window handle
umsg    argd        ; Window message
wparam  argd        ; wParam
lparam  argd        ; lParam

  if argv(umsg), ==, dword WM_DESTROY                 
    invoke  PostQuitMessage, dword NULL
    
  else 
    invoke  DefWindowProcA, dword argv(hwnd), dword argv(umsg), dword argv(wparam), dword argv(lparam)
    
  endif
  
  ret

endproc
  

分析:

  locals
    local  wc, WNDCLASSEX_size        ;以数据结构长度声明一个结构体,自己把nasm.inc中的local宏改了一下,      
    local  message, MSG_size        ;让它可以支持任意长度的数据    
    local  hWnd, dword
  endlocals

  上面这一段是变量声明,实际上就是分配内存空间。local的使用方法和上面说明的是一样的,但是一定要注意在“数据类型”中必须是数值否则的话无法编译通过,这一点宏里没有加以限制,所以只能靠自己来遵守。WNDCLASSEX_size是WNDCLASSEX数据结构的长度。
  local  wc, WNDCLASSEX_size
  这句的意思就是声明一个叫wc的WNDCLASSEX数据结构实例。

  mov     var(wc + WNDCLASSEX.cbSize), dword WNDCLASSEX_size            ;WNDCLASSEX.cbSize
  mov    var(wc + WNDCLASSEX.style), dword CS_HREDRAW | CS_VREDRAW        ;WNDCLASSEX.style
  mov     var(wc + WNDCLASSEX.lpfnWndProc), dword WndProc              ;WNDCLASSEX.lpfnWndProc
  mov    var(wc + WNDCLASSEX.cbClsExtra), dword NULL                ;WNDCLASSEX.cbClsExtra
  mov    var(wc + WNDCLASSEX.cbWndExtra), dword NULL                ;WNDCLASSEX.cbWndExtra
  push  dword [hInstance]
  pop    dword var(wc + WNDCLASSEX.hInstance)                  ;WNDCLASSEX.hInstance
    mov     var(wc + WNDCLASSEX.hbrBackground), dword COLOR_WINDOW + 1        ;WNDCLASSEX.hbrBackground
  mov    var(wc + WNDCLASSEX.lpszMenuName), dword NULL              ;WNDCLASSEX.lpszMenuName
    mov     var(wc + WNDCLASSEX.lpszClassName), dword szClass            ;WNDCLASSEX.lpszClassName
  invoke  LoadIconA, dword NULL, IDI_APPLICATION
  mov    var(wc + WNDCLASSEX.hIcon), eax                      ;WNDCLASSEX.hIcon
    mov     var(wc + WNDCLASSEX.hIconSm), eax                    ;WNDCLASSEX.hIconSm
  invoke  LoadCursorA, dword NULL, IDC_ARROW            
  mov    var(wc + WNDCLASSEX.hCursor), eax

  上面是对wc进行初始化。注意var()的使用。

  lea    eax, var(wc)                ;获取结构体wc的实际地址传入RegisterClassExA函数
  invoke   RegisterClassExA, eax

  因为RegisterClassExA函数的参数要求是传入一个WNDCLASSEX结构的指针,所以我们要先取得wc的实际地址,这就需要使用lea    eax, var(wc)这一句了。

    lea     ebx, var(message)            ;获取结构体message的实际地址
    invoke   GetMessageA, ebx, dword NULL, dword NULL, dword NULL
        cmp      eax, dword 0
        je       .ENDW        
    invoke   TranslateMessage, ebx
        invoke   DispatchMessageA, ebx

  上面几句也是同理。

  invoke   ShowWindow, dword var(hWnd), dword argv(dwshow)      
    invoke   UpdateWindow, dword var(hWnd)

  这两句则不同,因为ShowWindow和UpdateWindow这两个函数都是要传入其本身的值,所以直接使用var()宏就可以了。