NASM汇编教程翻译11 第十一讲 数到10(整数转字符串)

英文原版地址:NASM Assembly Language Tutorials - asmtutor.com

那么为什么在第十讲的程序打印了一个冒号而不是数字10?让我们看下ASCII码表。可以看到冒号的ASCII码值是58。我们将48加到整数来转化为它们的ASCII码字符串表示,所以为了打印数字10真正传递的ASCII码值是数字1跟一个数字0,而不是传递给 sys_write的值58。传递4948是数字10的正确字符串表示。所以,我们不能简单的给数字加48进行转化,首先我们必须要从数字10中拆分出1和0,因为所有位置的值需要被单独转化。

本节课我们需要写两个新的子程序 iprint 和 iprintLF 。这些函数将在我们想要打印ASCII码字符串表示的数字时使用。通过将数字存入EAX传递参数来实现。接下来初始化一个计数器放入ECX。我们将反复地从除以10来分离数字,每次通过加48将余数转化为一个字符串,压到栈上以后使用。一旦我们不能再除以10,将进入第二个循环。在这个打印循环,我们将打印栈上现在已经转化为字符串表示并弹出它们。栈上弹出后移动ESP向栈上的下一个成员。每次打印一个值,计数器ECX将减一。一旦所有数字都被转化并打印,将返回我们的程序。

除法指令是如何工作的?

DIV和IDIV指令的工作原理是将EAX中的值除以传递给指令的值。商的部分留在EAX中,余数部分放入EDX中。

IDIV指令例子

mov     eax, 10         ; 10放入EAX
mov     esi, 10         ; 10放入ESI
idiv    esi             ; EAX除以ESI (EAX将等于1,EDX将等于0)
idiv    esi             ; EAX再次除以ESI (EAX将等于0,EDX将等于1)

如果只想存余数不会有问题么?

不,因为这些是整数,当你用一个数除以一个更大的数时,EAX中的商是0,余数是这个数自己。这是因为这个数除了0次,留下原本的值作为余数在EDX中。这有多好?

functions.asm文件

;------------------------------------------
; void iprint(Integer number)
; 打印整数函数 (itoa)
iprint:
    push    eax             ; 将EAX的值保存在栈上以便函数运行后恢复
    push    ecx             ; 将ECX的值保存在栈上以便函数运行后恢复
    push    edx             ; 将EDX的值保存在栈上以便函数运行后恢复
    push    esi             ; 将ESI的值保存在栈上以便函数运行后恢复
    mov     ecx, 0          ; 最后要打印多少字节的计数器
 
divideLoop:
    inc     ecx             ; 所有要打印的字节计数 —— 字符数
    mov     edx, 0          ; 清空EDX
    mov     esi, 10         ; 将10放入ESI
    idiv    esi             ; EAX除以ESI
    add     edx, 48         ; 转化EDX的值为它的ASCII码表示 —— 调用除法指令后EDX保存余数
    push    edx             ; 将EDX (一个整数的字符串表示) 压到栈上
    cmp     eax, 0          ; 这个整数还能再被除么?
    jnz     divideLoop      ; 如果EAX不是0 跳转到divideLoop标签
 
printLoop:
    dec     ecx             ; 反向计数我们放在栈上的每个字节
    mov     eax, esp        ; 将栈指针放入EAX用于打印
    call    sprint          ; 调用字符串打印函数
    pop     eax             ; 移除栈上最后一个字符来移动ESP指向
    cmp     ecx, 0          ; 我们已经打印了栈上所有字节了么?
    jnz     printLoop       ; ECX不是0跳转到printLoop标签
 
    pop     esi             ; 用开始放到栈上的值恢复ESI
    pop     edx             ; 用开始放到栈上的值恢复EDX
    pop     ecx             ; 用开始放到栈上的值恢复ECX
    pop     eax             ; 用开始放到栈上的值恢复EAX
    ret
 
;------------------------------------------
; void iprintLF(Integer number)
; 带有换行符的整数打印函数 (itoa)
iprintLF:
    call    iprint          ; 调用数字打印函数
 
    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

;------------------------------------------
; 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
 
nextNumber:
    inc     ecx
    mov     eax, ecx
    call    iprintLF        ; 注:调用新的数字打印函数 (itoa)
    cmp     ecx, 10
    jne     nextNumber
 
    call    quit

编译命令

nasm -f elf hello.asm

链接命令

ld -m elf_i386 hello.o -o hello

执行命令

./hello

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值