英文原版地址:NASM Assembly Language Tutorials - asmtutor.com
对于像”Hello world“这样的命令行程序,换行符时必不可少的。一旦我们开始构建要求用户输入的程序,它们变得更重要。但是换行符维护起来可能会变得麻烦。有时想在字符串包含它们,有时也想删去它们。如果你继续通过在声明消息文本后添加 0AH 的方式在你的变量中硬编码换行符,它将变成一个问题。如果这是代码中一处我们不想输出这个变量的换行符的地方,需要去写一些额外逻辑在运行时从字符串中删去。
如果我们写一个子程序输出我们的消息后打印一个换行符,对于程序的维护性是有好处的。这样,当我们需要换行符时调用这个子程序,不需要时就调用现在的sprint子程序。
调用 sys_write 需要我们传递指向想输出的字符串在内存中地址的指针,所以我们不能只传一个换行符给我们的打印函数。我们也不想仅为了保存换行符去创建另一个变量,所以用栈来代替。
这个方法是通过将换行符移到EAX寄存器中运作的。我们将EAX压到栈上然后通过扩展栈指针寄存器ESP获取地址。ESP是另一个寄存器。当ESP指向一个字符在内存中的地址,sys_write将被允许使用它去打印。
functions.asm文件
;------------------------------------------
; int slen(String message)
; 字符串长度计算函数
slen:
push ebx
mov ebx, eax
nextchar:
cmp byte [eax], 0
jz finished
inc eax
jmp nextchar
finished:
sub eax, ebx
pop ebx
ret
;------------------------------------------
; void sprint(String message)
; 字符串打印函数
sprint:
push edx
push ecx
push ebx
push eax
call slen
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
;------------------------------------------
; void sprintLF(String message)
; 打印字符串和换行符函数
sprintLF:
call sprint
push eax ; 当我们在这个函数中使用EAX时通过将EAX压入栈来进行保护
mov eax, 0AH ; 将0AH移到EAX中 - 0AH是换行符的ascii码
push eax ; 将换行符放到栈上,以便我们获取地址
mov eax, esp ; 将当前栈指针的地址放到EAX寄存器中给sprint函数
call sprint ; 调用sprint函数
pop eax ; 从栈上移除换行符
pop eax ; 恢复调用函数前EAX原本的值
ret ; 返回程序
;------------------------------------------
; void exit()
; 退出程序并复原资源
quit:
mov ebx, 0
mov eax, 1
int 80h
ret
hello.asm文件
%include 'functions.asm' ; 包含我们的外部文件
SECTION .data
msg1 db 'Hello, brave new world!', 0h ; 注:已经移除了换行符
msg2 db 'This is how we recycle in NASM.', 0h ; 注:已经移除了换行符
SECTION .text
global _start
_start:
mov eax, msg1 ; 将第一个字符串的地址给EAX
call sprintLF ; 注:调用带有换行符的字符串打印函数
mov eax, msg2 ; 将第二个字符串的地址给EAX
call sprintLF ; 注:调用带有换行符的字符串打印函数
call quit ; 调用退出函数
编译命令
nasm -f elf hello.asm
链接命令
ld -m elf_i386 hello.o -o hello
执行命令
./hello