NASM汇编教程翻译10 第十讲 数到10

英文原版地址: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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 前言i 第1章简介1 1.1 数制. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1 十进制. . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.2 二进制. . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.3 十六进制. . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 计算机结构. . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.1 内存. . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.2 CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2.3 CPU 80x86系列. . . . . . . . . . . . . . . . . . . . . 5 1.2.4 8086 16位寄存器. . . . . . . . . . . . . . . . . . . . . 6 1.2.5 80386 32位寄存器. . . . . . . . . . . . . . . . . . . . 7 1.2.6 实模式. . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2.7 16位保护模式. . . . . . . . . . . . . . . . . . . . . . . 8 1.2.8 32位保护模式. . . . . . . . . . . . . . . . . . . . . . . 8 1.2.9 中断. . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3 汇编语言. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.1 机器语言. . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.2 汇编语言. . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.3 指令操作数. . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.4 基本指令. . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.5 指示符. . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.3.6 输入和输出. . . . . . . . . . . . . . . . . . . . . . . . 14 1.3.7 调试. . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.4 创建一个程序. . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.4.1 第一个程序. . . . . . . . . . . . . . . . . . . . . . . . 16 1.4.2 编译器依赖. . . . . . . . . . . . . . . . . . . . . . . . 18 1.4.3 汇编代码. . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.4 编译C代码. . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.5 连接目标文件. . . . . . . . . . . . . . . . . . . . . . . 20 1.4.6 理解一个汇编列表文件. . . . . . . . . . . . . . . . . . 20 3 4 目录 1.5 骨架文件. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 第2章基本汇编语言23 2.1 整形工作方式. . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.1.1 整形表示法. . . . . . . . . . . . . . . . . . . . . . . . 23 2.1.2 正负号延伸. . . . . . . . . . . . . . . . . . . . . . . . 25 2.1.3 补码运算. . . . . . . . . . . . . . . . . . . . . . . . . 28 2.1.4 程序例子. . . . . . . . . . . . . . . . . . . . . . . . . 29 2.1.5 扩充精度运算. . . . . . . . . . . . . . . . . . . . . . . 31 2.2 控制结构. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2.1 比较. . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2.2 分支指令. . . . . . . . . . . . . . . . . . . . . . . . . 33 2.2.3 循环指令. . . . . . . . . . . . . . . . . . . . . . . . . 36 2.3 翻译标准的控制结构. . . . . . . . . . . . . . . . . . . . . . . 36 2.3.1 If语句. . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.3.2 While循环. . . . . . . . . . . . . . . . . . . . . . . . . 37 2.3.3 Do while循环. . . . . . . . . . . . . . . . . . . . . . . 37 2.4 例子:查找素数. . . . . . . . . . . . . . . . . . . . . . . . . . . 37 第3章位操作41 3.1 移位操作. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.1.1 逻辑移位. . . . . . . . . . . . . . . . . . . . . . . . . 41 3.1.2 移位的应用. . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.3 算术移位. . . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.4 循环移位. . . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.5 简单应用. . . . . . . . . . . . . . . . . . . . . . . . . 43 3.2 布尔型按位运算. . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.2.1 AND运算符. . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.2 OR运算符. . . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.3 XOR运算. . . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.4 NOT运算. . . . . . . . . . . . . . . . . . . . . . . . . 45 3.2.5 TEST指令. . . . . . . . . . . . . . . . . . . . . . . . . 45 3.2.6 位操作的应用. . . . . . . . . . . . . . . . . . . . . . . 45 3.3 避免使用条件分支. . . . . . . . . . . . . . . . . . . . . . . . 47 3.4 在C中进行位操作. . . . . . . . . . . . . . . . . . . . . . . . . 49 3.4.1 C中的按位运算. . . . . . . . . . . . . . . . . . . . . . 49 3.4.2 在C中使用按位运算. . . . . . . . . . . . . . . . . . . 50 3.5 Big和Little Endian表示法. . . . . . . . . . . . . . . . . . . . 51 3.5.1 什么时候需要在乎Little和Big Endian . . . . . . . . . 52 3.6 计算位数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.1 方法一. . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.2 方法二. . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.3 方法三. . . . . . . . . . . . . . . . . . . . . . . . . . . 55 目录5 第4章子程序57 4.1 间接寻址. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.2 子程序的简单例子. . . . . . . . . . . . . . . . . . . . . . . . 57 4.3 堆栈. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.4 CALL和RET指令. . . . . . . . . . . . . . . . . . . . . . . . . 60 4.5 调用约定. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 4.5.1 在堆栈上传递参数. . . . . . . . . . . . . . . . . . . . 62 4.5.2 堆栈上的局部变量. . . . . . . . . . . . . . . . . . . . 66 4.6 多模块程序. . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 4.7 C与汇编的接口技术. . . . . . . . . . . . . . . . . . . . . . . 71 4.7.1 保存寄存器. . . . . . . . . . . . . . . . . . . . . . . . 71 4.7.2 函数名. . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.7.3 传递参数. . . . . . . . . . . . . . . . . . . . . . . . . 72 4.7.4 计算局部变量的地址. . . . . . . . . . . . . . . . . . . 73 4.7.5 返回值. . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.7.6 其它调用约定. . . . . . . . . . . . . . . . . . . . . . . 73 4.7.7 样例. . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.7.8 在汇编程序中调用C函数. . . . . . . . . . . . . . . . . 78 4.8 可重入和递归子程序. . . . . . . . . . . . . . . . . . . . . . . 78 4.8.1 递归子程序. . . . . . . . . . . . . . . . . . . . . . . . 79 4.8.2 回顾一下C变量的储存类型. . . . . . . . . . . . . . . 79 第5章数组83 5.1 介绍. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 5.1.1 定义数组. . . . . . . . . . . . . . . . . . . . . . . . . 83 5.1.2 访问数组中的元素. . . . . . . . . . . . . . . . . . . . 84 5.1.3 更高级的间接寻址. . . . . . . . . . . . . . . . . . . . 86 5.1.4 例子. . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.1.5 多维数组. . . . . . . . . . . . . . . . . . . . . . . . . 91 5.2 数组/串处理指令. . . . . . . . . . . . . . . . . . . . . . . . . 93 5.2.1 读写内存. . . . . . . . . . . . . . . . . . . . . . . . . 94 5.2.2 REP前缀指令. . . . . . . . . . . . . . . . . . . . . . . 95 5.2.3 串比较指令. . . . . . . . . . . . . . . . . . . . . . . . 96 5.2.4 REPx前缀指令. . . . . . . . . . . . . . . . . . . . . . . 96 5.2.5 样例. . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 第6章浮点103 6.1 浮点表示法. . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 6.1.1 非整形的二进制数. . . . . . . . . . . . . . . . . . . . 103 6.1.2 IEEE浮点表示法. . . . . . . . . . . . . . . . . . . . . 105 6.2 浮点运算. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 6.2.1 加法. . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 6.2.2 减法. . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 6 目录 6.2.3 乘法和除法. . . . . . . . . . . . . . . . . . . . . . . . 109 6.2.4 分支程序设计. . . . . . . . . . . . . . . . . . . . . . . 109 6.3 数字协处理器. . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.3.1 硬件. . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.3.2 指令. . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 6.3.3 样例. . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 6.3.4 二次方程求根公式. . . . . . . . . . . . . . . . . . . . 115 6.3.5 从文件中读数组. . . . . . . . . . . . . . . . . . . . . 118 6.3.6 查找素数. . . . . . . . . . . . . . . . . . . . . . . . . 120 第7章结构体与C++ 127 7.1 结构体. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 7.1.1 简介. . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 7.1.2 内存地址对齐. . . . . . . . . . . . . . . . . . . . . . . 128 7.1.3 位域s . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 7.1.4 在汇编语言中使用结构体. . . . . . . . . . . . . . . . 132 7.2 汇编语言和C++ . . . . . . . . . . . . . . . . . . . . . . . . . 134 7.2.1 重载函数和名字改编. . . . . . . . . . . . . . . . . . . 134 7.2.2 引用. . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 7.2.3 内联函数. . . . . . . . . . . . . . . . . . . . . . . . . 137 7.2.4 类. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 7.2.5 继承和多态. . . . . . . . . . . . . . . . . . . . . . . . 147 7.2.6 C++的其它特性. . . . . . . . . . . . . . . . . . . . . 153 附录A 80x86指令155 A.1 非浮点指令. . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 A.2 浮点数指令. . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 索引163
好的,以下是汇编语言 x86 32位体系结构的代码,用于实现任意两个10位以内十进制数求和并显示结果: ```assembly section .data num1 db 10 ; 第一个数,最大为10位 num2 db 10 ; 第二个数,最大为10位 sum db 12 ; 存放两数之和,最大为11位 nl db 10 ; 换行符 section .text global _start _start: ; 读取num1 mov eax, 3 ; 系统调用号:读取标准输入 mov ebx, 0 ; 文件句柄:标准输入 mov ecx, num1 ; 存放输入值的内存地址 mov edx, 10 ; 输入值的最大长度 int 0x80 ; 执行系统调用 ; 读取num2 mov eax, 3 ; 系统调用号:读取标准输入 mov ebx, 0 ; 文件句柄:标准输入 mov ecx, num2 ; 存放输入值的内存地址 mov edx, 10 ; 输入值的最大长度 int 0x80 ; 执行系统调用 ; 将num1和num2转换为十进制整数 mov eax, num1 ; 将num1的地址存入eax mov ebx, 0 ; 用ebx存放结果 call ascii_to_int ; 调用ascii_to_int函数,将num1转换为十进制整数 mov eax, ebx ; 将结果存入eax mov ebx, 0 ; 清空ebx mov ecx, num2 ; 将num2的地址存入ecx call ascii_to_int ; 调用ascii_to_int函数,将num2转换为十进制整数 add eax, ebx ; 将num1和num2的和存入eax ; 将和转换为ASCII码 mov ebx, eax ; 将和存入ebx mov eax, 0 ; 用eax存放结果 mov ecx, sum ; 将sum的地址存入ecx call int_to_ascii ; 调用int_to_ascii函数,将和转换为ASCII码 ; 输出和 mov eax, 4 ; 系统调用号:输出到标准输出 mov ebx, 1 ; 文件句柄:标准输出 mov ecx, sum ; 存放输出值的内存地址 mov edx, 11 ; 输出值的长度 int 0x80 ; 执行系统调用 ; 输出换行符 mov eax, 4 ; 系统调用号:输出到标准输出 mov ebx, 1 ; 文件句柄:标准输出 mov ecx, nl ; 存放输出值的内存地址 mov edx, 1 ; 输出值的长度 int 0x80 ; 执行系统调用 ; 退出程序 mov eax, 1 ; 系统调用号:退出程序 xor ebx, ebx ; 返回值:0 int 0x80 ; 执行系统调用 ; 将ASCII码转换为十进制整数 ; 输入: ; eax - 存放ASCII码的内存地址 ; 输出: ; ebx - 十进制整数 ascii_to_int: mov ebx, 0 ; 用ebx存放结果 mov edx, 0 ; 用edx存放位权 .loop: movzx ecx, byte [eax] ; 将ASCII码转换为整数 cmp ecx, 0x0A ; 判断是否到达数字末尾 je .exit ; 如果到达数字末尾,跳出循环 imul edx, edx, 10 ; 计算当前位数的位权 add ebx, ecx ; 将当前位数的值累加到结果中 inc eax ; 指向下一个数字 jmp .loop ; 继续循环 .exit: ret ; 返回 ; 将十进制整数转换为ASCII码 ; 输入: ; ebx - 十进制整数 ; 输出: ; eax - 存放ASCII码的内存地址 int_to_ascii: mov edi, ecx ; 将存放输出值的内存地址存入edi add ecx, edx ; 计算存放输出值的内存地址的末尾 mov byte [ecx], 0 ; 在末尾处添加字符串结束标志 .loop: xor edx, edx ; edx清零 div dword 10 ; 将ebx除以10,余数存入edx,商存入ebx add dl, '0' ; 将余数转换为ASCII码 mov byte [ecx], dl ; 将ASCII码存入内存 dec ecx ; 指向下一个内存地址 test ebx, ebx ; 判断商是否已经为0 jnz .loop ; 如果商不为0,则继续循环 mov eax, edi ; 将存放输出值的内存地址存入eax ret ; 返回 ``` 这段代码使用了系统调用来读取用户输入和输出结果,同时还包含了两个子函数 `ascii_to_int` 和 `int_to_ascii`,用于将ASCII码转换为十进制整数和将十进制整数转换为ASCII码。你可以将上述代码保存到一个文件中(例如 `sum.asm`),然后使用汇编器将其转换为可执行文件,例如: ``` $ nasm -f elf32 sum.asm -o sum.o $ ld -m elf_i386 sum.o -o sum ``` 然后运行可执行文件即可开始使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值