英文原版地址:NASM Assembly Language Tutorials - asmtutor.com
背景知识
汇编中的按数字计数并不像你想的那样简单。首先你需要通过sys_write输出内存中的一个地址,因此,我们不能仅加载保存数字的寄存器来调用打印函数。其次,汇编中数字和字符串是有很大区别的。字符串相当于被调用的ASCII码的值。ASCII意思是美国信息交换标准代码。ASCII被创造出是为了使所有计算机上的字符串的表示标准化。
记住,不能打印一个数字,必须打印一个字符串。为了实现计数到10,我们需要将数字转化成是在ASCII码中的标准数字代表。看ASCII码表中发现,数字1实际是用49表示的。实际上,将48加到数字上是我们将数字转化为ASCII码代表的全部工作。
写代码
我们将使用ECX寄存器来完成从1数到10的程序。将48加到计数器来转化成ASCII码中的数字。之后,我将这个值压到栈上然后调用打印函数通过ESP作为内存地址打印出来。一旦我们完成计数到10就退出技术循环并调用退出函数
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 .text
global _start
_start:
mov ecx, 0 ; ECX初始化为0
nextNumber:
inc ecx ; ECX的值加一
mov eax, ecx ; 将数字放入EAX中
add eax, 48 ; 将48加到数字转化数字为ASCII码来打印
push eax ; EAX的值压入栈
mov eax, esp ; 获取字符在栈上的地址
call sprintLF ; 调用打印函数
pop eax ; 清理栈空间,就不会有不需要的字节占用空间
cmp ecx, 10 ; 已经到10了么? 将计数器和十进制的10比较
jne nextNumber ; 如果不相等就跳转并继续计数
call quit
编译命令
nasm -f elf hello.asm
链接命令
ld -m elf_i386 hello.o -o hello
执行命令
./hello