宏的语法规则与汇编指令没有关系,只是为了管理代码复杂度而设计的类似于高级语言的特性。NASM的宏语法格式如下:
%macro 宏名称 参数个数
宏的内容
%endmacro
宏的第一个参数用%1表示,第二个参数用%2表示,依次类推。
《Assembly Language step by step programming with linux》在第10章给出了一个使用宏版本的光标控制程序:

;  Executable name : eatmacro
;  Version         : 1.0
;  Created date    : 2/21/2009
;  Last update     : 2/23/2009
;  Author          : Jeff Duntemann
;  Description     : A simple program in assembly for Linux, using 
;		   : NASM 2.05, demonstrating the use of escape 
;		   : sequences to do simple "full-screen" text output
;		   ; through macros rather than procedures
;
;  Build using these commands:
;    nasm -f elf -g -F stabs eatmacro.asm
;    ld -o eatmacro eatmacro.o
;
;
section .data		; Section containing initialised data

	SCRWIDTH:	equ 80	; By default we assume 80 chars wide
	PosTerm:	db 27,"[01;01H"	; <ESC>[<Y>;<X>H
	POSLEN:		equ $-PosTerm	; Length of term position string
	ClearTerm:      db 27,"[2J"	; <ESC>[2J
	CLEARLEN	equ $-ClearTerm	; Length of term clear string
	AdMsg: 		db "Eat At Joe's!"	; Ad message
	ADLEN: 		equ $-AdMsg		; Length of ad message
	Prompt:		db "Press Enter: "	; User prompt
	PROMPTLEN:	equ $-Prompt		; Length of user prompt

; This table gives us pairs of ASCII digits from 0-80. Rather than 
; calculate ASCII digits to insert in the terminal control string, 
; we look them up in the table and read back two digits at once to 
; a 16-bit register like DX, which we then poke into the terminal 
; control string PosTerm at the appropriate place. See GotoXY.
; If you intend to work on a larger console than 80 X 80, you must
; add additional ASCII digit encoding to the end of Digits. Keep in
; mind that the code shown here will only work up to 99 X 99.
	Digits:	db "0001020304050607080910111213141516171819"
		db "2021222324252627282930313233343536373839"
		db "4041424344454647484950515253545556575859"
		db "606162636465666768697071727374757677787980"

SECTION .bss		; Section containing uninitialized data

SECTION .text		; Section containing code

;-------------------------------------------------------------------------
; ExitProg: 	Terminate program and return to Linux
; UPDATED:	4/23/2009
; IN: 		Nothing
; RETURNS:	Nothing
; MODIFIES: 	Nothing
; CALLS:	Kernel sys_exit
; DESCRIPTION:	Calls sys_edit to terminate the program and return
;		control to Linux

%macro	ExitProg 0
	mov eax,1		; Code for Exit Syscall
	mov ebx,0		; Return a code of zero	
	int 80H			; Make kernel call
%endmacro


;-------------------------------------------------------------------------
; WaitEnter: 	Wait for the user to press Enter at the console
; UPDATED:	4/23/2009
; IN: 		Nothing
; RETURNS:	Nothing
; MODIFIES: 	Nothing
; CALLS:	Kernel sys_read
; DESCRIPTION:	Calls sys_read to wait for the user to type a newline at
;		the console

%macro	WaitEnter 0
	mov eax,3		; Code for sys_read
	mov ebx,0		; Specify File Descriptor 0: Stdin	
	int 80H			; Make kernel call
%endmacro


;-------------------------------------------------------------------------
; WriteStr: 	Send a string to the Linux console
; UPDATED:	4/21/2009
; IN: 		String address in %1, string length in %2
; RETURNS:	Nothing
; MODIFIES: 	Nothing
; CALLS:	Kernel sys_write
; DESCRIPTION:	Displays a string to the Linux console through a 
;		sys_write kernel call

%macro WriteStr 2		; %1 = String address; %2 = string length
	push eax		; Save pertinent registers
	push ebx
	mov ecx,%1		; Put string address into ECX
	mov edx,%2		; Put string length into EDX
	mov eax,4		; Specify sys_write call
	mov ebx,1		; Specify File Descriptor 1: Stdout
	int 80H			; Make the kernel call
	pop ebx			; Restore pertinent registers
	pop eax
%endmacro


;-------------------------------------------------------------------------
; ClrScr: 	Clear the Linux console
; UPDATED:	4/23/2009
; IN: 		Nothing
; RETURNS:	Nothing
; MODIFIES: 	Nothing
; CALLS:	Kernel sys_write
; DESCRIPTION:	Sends the predefined control string <ESC>[2J to the
;		console, which clears the full display

%macro ClrScr 0
	push eax	  ; Save pertinent registers
	push ebx
	push ecx
	push edx
; Use WriteStr macro to write control string to console:
	WriteStr ClearTerm,CLEARLEN
	pop edx		  ; Restore pertinent registers
	pop ecx
	pop ebx
	pop eax	
%endmacro


;-------------------------------------------------------------------------
; GotoXY: 	Position the Linux Console cursor to an X,Y position
; UPDATED:	4/23/2009
; IN: 		X in %1, Y in %2
; RETURNS:	Nothing
; MODIFIES: 	PosTerm terminal control sequence string
; CALLS:	Kernel sys_write
; DESCRIPTION:	Prepares a terminal control string for the X,Y coordinates
;		passed in AL and AH and calls sys_write to position the
;		console cursor to that X,Y position. Writing text to the
;		console after calling GotoXY will begin display of text
;		at that X,Y position.

%macro GotoXY 2			; %1 is X value; %2 id Y value		
	pushad			; Save caller's registers
	xor edx,edx		; Zero EDX
	xor ecx,ecx		; Ditto ECX
; Poke the Y digits:
	mov dl,%2		     ; Put Y value into offset term EDX
	mov cx,word [Digits+edx*2]   ; Fetch decimal digits to CX
	mov word [PosTerm+2],cx	     ; Poke digits into control string
; Poke the X digits:
	mov dl,%1		     ; Put X value into offset term EDX
	mov cx,word [Digits+edx*2]   ; Fetch decimal digits to CX
	mov word [PosTerm+5],cx	     ; Poke digits into control string
; Send control sequence to stdout:
	WriteStr PosTerm,POSLEN
; Wrap up and go home:
	popad			; Restore caller's registers
%endmacro

;-------------------------------------------------------------------------
; WriteCtr: 	Send a string centered to an 80-char wide Linux console
; UPDATED:	4/23/2009
; IN: 		Y value in %1, String address in %2, string length in %3
; RETURNS:	Nothing
; MODIFIES: 	PosTerm terminal control sequence string
; CALLS:	GotoXY, WriteStr
; DESCRIPTION:	Displays a string to the Linux console centered in an
;		80-column display. Calculates the X for the passed-in 
;		string length, then calls GotoXY and WriteStr to send 
;		the string to the console

%macro WriteCtr 3	; %1 = row; %2 = String addr; %3 = String length
	push ebx	; Save caller's EBX
	push edx	; Save caller's EDX
	mov edx,%3	; Load string length into EDX
	xor ebx,ebx	; Zero EBX
	mov bl,SCRWIDTH	; Load the screen width value to BL
	sub bl,dl	; Calc diff. of screen width and string length
	shr bl,1	; Divide difference by two for X value
	GotoXY bl,%1	; Position the cursor for display
	WriteStr %2,%3	; Write the string to the console
	pop edx		; Restore caller's EDX
	pop ebx		; Restore caller's EBX
%endmacro


global 	_start			; Linker needs this to find the entry point!

_start:
	nop			; This no-op keeps gdb happy...

; First we clear the terminal display...
	ClrScr
; Then we post the ad message centered on the 80-wide console:
	WriteCtr 12,AdMsg,ADLEN
; Position the cursor for the "Press Enter" prompt:
	GotoXY 1,23
; Display the "Press Enter" prompt:
	WriteStr Prompt,PROMPTLEN	
; Wait for the user to press Enter:
	WaitEnter
; ...and we're done!
	ExitProg

makefile文件内容如下:

eatmacro: eatmacro.o
	ld -o eatmacro eatmacro.o
eatmacro.o: eatmacro.asm
	nasm -f elf -l eatmacro.lst -g -F stabs eatmacro.asm

程序逻辑与第一版光标控制程序是一样的,执行结果也相同。下面分析一下使用宏版本的反汇编的代码,只分析一小段,其余的都类似:
[root@bogon eatmacro]# objdump -d eatmacro

eatmacro:     file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
 8048080:       90                      nop   
 8048081:       50                      push   %eax // ClrScr宏展开
 8048082:       53                      push   %ebx
 8048083:       51                      push   %ecx
 8048084:       52                      push   %edx
 8048085:       50                      push   %eax // WriteStr ClearTerm,CLEARLEN宏展开是在这里
 8048086:       53                      push   %ebx
 8048087:       b9 8c 91 04 08          mov    $0x804918c,%ecx //$0x804918c是ClearTerm字节数组的地址
 804808c:       ba 04 00 00 00          mov    $0x4,%edx //$0x4是CLEARLEN
 8048091:       b8 04 00 00 00          mov    $0x4,%eax
 8048096:       bb 01 00 00 00          mov    $0x1,%ebx
 804809b:       cd 80                   int    $0x80
 804809d:       5b                      pop    %ebx
 804809e:       58                      pop    %eax // WriteStr宏在这里结束
 804809f:       5a                      pop    %edx
 80480a0:       59                      pop    %ecx
 80480a1:       5b                      pop    %ebx
 80480a2:       58                      pop    %eax // ClrScr宏结束
……
因此可以看出,NASM就是在预处理时,把宏展开,简单的进行了的代码替换。

Logo

更多推荐