汇编 - 宏

编写宏是确保汇编语言模块化编程的另一种方法。

  • 宏是一系列指令,由名称指定,可以在程序中的任何位置使用。

  • 在 NASM 中,宏是使用 %macro%endmacro 指令定义的。

  • 宏以 %macro 指令开始,以 %endmacro 指令结束。

宏定义的语法 −

%macro macro_name  number_of_params
<macro body>
%endmacro

其中,number_of_params指定参数数量,macro_name指定宏的名称。

通过使用宏名称以及必要的参数来调用宏。 当您需要在程序中多次使用某些指令序列时,可以将这些指令放入宏中并使用它而不是一直写说明。

例如,程序一个很常见的需求就是在屏幕上写入一串字符。 要显示字符串,您需要以下指令序列 −

mov	edx,len	    ;message length
mov	ecx,msg	    ;message to write
mov	ebx,1       ;file descriptor (stdout)
mov	eax,4       ;system call number (sys_write)
int	0x80        ;call kernel

在上面显示字符串的示例中,寄存器 EAX、EBX、ECX 和 EDX 已被 INT 80H 函数调用使用。 因此,每次需要在屏幕上显示时,都需要将这些寄存器保存在堆栈中,调用INT 80H,然后从堆栈中恢复寄存器的原始值。 因此,编写两个宏来保存和恢复数据可能会很有用。

我们观察到,一些指令,如 IMUL、IDIV、INT 等,需要将一些信息存储在某些特定的寄存器中,甚至需要在某些特定的寄存器中返回值。 如果程序已经使用这些寄存器来保存重要数据,那么这些寄存器中的现有数据应该保存在堆栈中,并在指令执行后恢复。

示例

以下示例显示了定义和使用宏 −

; A macro with two parameters
; Implements the write system call
   %macro write_string 2 
      mov   eax, 4
      mov   ebx, 1
      mov   ecx, %1
      mov   edx, %2
      int   80h
   %endmacro
 
section	.text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   write_string msg1, len1               
   write_string msg2, len2    
   write_string msg3, len3  
	
   mov eax,1                ;system call number (sys_exit)
   int 0x80                 ;call kernel

section	.data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1			

msg2 db 'Welcome to the world of,', 0xA,0xD 
len2 equ $- msg2 

msg3 db 'Linux assembly programming! '
len3 equ $- msg3

当上面的代码被编译并执行时,会产生以下结果 −

Hello, programmers!
Welcome to the world of,
Linux assembly programming!