基本语法
BYTE 1
WORD 2
DWORD 4
QWORD 8
TBYTE 10
汇编程序分为三段
- data 声明初始化的数据和常数
- bss 声明变量 未初始化或者初始化为0的全局变量和静态变量
- text 代码段
section .text
global _start
section .data
section .bss
注释
- 汇编程序注释以‘’;‘’开头,
add eax, abx ;这是注释
汇编语言声明
汇编语言包含三种类型语句
- 可执行指令活说明
- 汇编程序指令或伪操作
- 宏 (代码替换)
汇编语言语法
[label] mnemonic [operands] [;comment]
方括号中的字段是可选的。基本指令包括两段,第一段是要执行的指令(或助记符)的名称,第二段是命令的操作数或参数。
典型汇编语言示例
INC COUNT ; 增加内存变量COUNT
MOV TOTAL, 48 ; 将值48转移到
; 内存变量TOTAL
ADD AH, BH ; 添加寄存器BH内容
; 到AH寄存器
AND MASK1, 128 ; 对变量MASK1和128
; 执行AND操作
ADD MARKS, 10 ; 将10加到变量MARKS
MOV AL, 10 ; 将值10传送到AL寄存器
汇编指令
可执行指令
- int 中断指令
汇编语言中的中断指令,用于触发软件中断,从而调用操作系统的系统调用
伪指令
- db
表示“define byte”(定义字节)。它告诉汇编器在内存中分配一个或多个字节,并将这些字节初始化为指定的值。
- equ
表示“equal”(等于)。它用于定义一个符号常量
特殊符号
- $
这是一个特殊符号,表示当前汇编器处理到的当前位置(即当前指令的地址)
汇编语言 内存段
section关键字也可以替换成segment,会得到相同结果
segment .text
global _start
_start:
mov edx,len
mov ecx.msg
mov ebx,1
mov eax,4
int 0x80
mov eax,1
int 0x80
segment .data
msg db 'Hello,world!',0xa
len equ $ - msg
数据段
- .data段
全局变量,初始化的变量
- .bss 段
静态内存部分,未初始化全局变量
代码段
- .text
存储指令区域
堆栈
该段包含传递给程序中函数和过程的数据值
寄存器
处理器操作主要涉及处理数据。该数据可以存储在存储器中并从其访问。然而,从存储器中读取数据并将数据存储到存储器中会减慢处理器的速度,因为这涉及到通过控制总线发送数据请求并进入存储器存储单元并通过同一通道获取数据的复杂过程。为了加速处理器的运行,处理器包括一些内部存储器存储位置,称为寄存器。寄存器存储要处理的数据元素,而不必访问存储器。处理器芯片中内置了数量有限的寄存器。
通用寄存器
- 数据寄存器
- 指针寄存器
- 索引寄存器
其中一些数据寄存器在算术运算中有特定用途。
AX是主要的累加器; 它用于输入/输出和大多数算术指令。例如,在乘法运算中,根据操作数的大小,将一个操作数存储在EAX或AX或AL寄存器中。
BX被称为基址寄存器,因为它可以用于索引寻址。
CX被称为计数寄存器,因为ECX,CX寄存器在迭代操作中存储循环计数。
DX被称为数据寄存器。它也用于输入/输出操作。它还与AX寄存器以及DX一起使用,用于涉及大数值的乘法和除法运
指针寄存器
- 堆栈指针(SP) -16位SP寄存器提供程序堆栈内的偏移值。与SS寄存器(SS:SP)关联的SP是指程序堆栈中数据或地址的当前位置
- 基本指针(BP) -16位BP寄存器主要帮助参考传递给子例程的参数变量。SS寄存器中的地址与BP中的偏移量相结合,以获取参数的位置。BP也可以与DI和SI组合用作特殊寻址的基址寄存器。
- 指令指针(IP) -16位IP寄存器存储要执行的下一条指令的偏移地址。与CS寄存器关联的IP(作为CS:IP)给出了代码段中当前指令的完整地址。
控制寄存器
通用标志位是:
溢出标志(OF) -指示有符号算术运算后数据的高阶位(最左位)的溢出。
方向标记(DF) -它确定向左或向右移动或比较字符串数据的方向。DF值为0时,字符串操作为从左至右的方向;当DF值为1时,字符串操作为从右至左的方向。
中断标志(IF) -确定是否忽略或处理外部中断(例如键盘输入等)。当值为0时,它禁用外部中断,而当值为1时,它使能中断。
陷阱标志(TF) -允许在单步模式下设置处理器的操作。我们使用的DEBUG程序设置了陷阱标志,因此我们可以一次逐步执行一条指令。
符号标志(SF) -显示算术运算结果的符号。根据算术运算后数据项的符号设置此标志。该符号由最左位的高位指示。正结果将SF的值清除为0,负结果将其设置为1。
零标志(ZF) -指示算术或比较运算的结果。非零结果将零标志清零,零结果将其清零。
辅助进位标志(AF) -包含经过算术运算后从位3到位4的进位;用于专业算术。当1字节算术运算引起从第3位到第4位的进位时,将设置AF。
奇偶校验标志(PF) -指示从算术运算获得的结果中1位的总数。偶数个1位将奇偶校验标志清为0,奇数个1位将奇偶校验标志清为1。
进位标志(CF) -在算术运算后,它包含一个高位(最左边)的0或1进位。它还存储移位或旋转操作的最后一位的内容。
下表列出了16位标志寄存器中标志位的位置
段寄存器
段是程序中定义的特定区域,用于包含数据,代码和堆栈。有三个主要部分-
代码段 - 它包含所有要执行的指令。16位代码段寄存器或CS寄存器存储代码段的起始地址。
数据段 - 它包含数据,常量和工作区。16位数据段寄存器或DS寄存器存储数据段的起始地址。
堆栈段 - 它包含数据或过程或子例程的返回地址。它被实现为“堆栈”数据结构。堆栈段寄存器或SS寄存器存储堆栈的起始地址
系统调用
系统调用是用户空间和内核空间之间接口的API
您可以在汇编程序中使用Linux系统调用。您需要执行以下步骤才能在程序中使用Linux系统调用-
寻址模式
大多数汇编语言指令都需要处理操作数。操作数地址提供了要处理的数据存储的位置。。有些指令不需要操作数,而另一些指令则需要一个,两个或三个操作数。当一条指令需要两个操作数时,第一个操作数通常是目的地,它在寄存器或存储器位置中包含数据,第二个操作数是源。源包含要传递的数据(立即寻址)或数据的地址(在寄存器或存储器中)。通常,操作后源数据保持不变。
寻址的三种基本模式是-
- 寄存器寻址
- 立即寻址
- 内存寻址
寄存器寻址
在这种寻址模式下,寄存器包含操作数。根据指令,寄存器可以是第一个操作数,第二个操作数或两者。
MOV DX, TAX_RATE ; 寄存器是第一个操作数
MOV COUNT, CX ; 寄存器是第二个操作数
MOV EAX, EBX ; 两个操作数都是寄存器
由于寄存器之间的数据处理不涉及内存,因此可以最快地处理数据
立即寻址
立即数操作数具有常量值或表达式。当具有两个操作数的指令使用立即寻址时,第一个操作数可以是寄存器或存储器位置,而第二个操作数是立即数。第一个操作数定义数据的长度
BYTE_VALUE DB 150 ; 一个字节值被定义
WORD_VALUE DW 300 ; 一个字值被定义
ADD BYTE_VALUE, 65 ; BYTE_VALUE加一个立即操作数65
MOV AX, 45H ; 立即常数45H转移到AX
直接内存寻址
在存储器寻址模式下指定操作数时,通常需要直接访问主存储器,通常是数据段。。这种寻址方式导致数据处理变慢。为了找到数据在内存中的确切位置,我们需要段起始地址(通常在DS寄存器中找到)和偏移值。此偏移值也称为有效地址。在直接寻址模式下,偏移量值直接作为指令的一部分指定,通常由变量名指示。
ADD BYTE_VALUE, DL ; 将寄存器添加到存储位置
MOV BX, WORD_VALUE ; 将内存中的操作数添加到寄存器中
直接偏移寻址
此寻址模式使用算术运算符修改地址。例如,查看以下定义数据表的定义-
BYTE_TABLE DB 14, 15, 22, 45 ; 字节表
WORD_TABLE DW 134, 345, 564, 123 ; 字表
以下操作将数据从内存中的表访问到寄存器中-
MOV CL, BYTE_TABLE[2] ; 获取BYTE_TABLE的第三个元素
MOV CL, BYTE_TABLE + 2 ; 获取BYTE_TABLE的第三个元素
MOV CX, WORD_TABLE[3] ; 获取WORD_TABLE的第4个元素
MOV CX, WORD_TABLE + 3 ; 获取WORD_TABLE的第4个元素
间接内存寻址
此寻址模式利用计算机的Segment:Offset寻址功能。通常,在方括号内编码的基址寄存器EBX,EBP(或BX,BP)和索引寄存器(DI,SI)用于内存引用。
MY_TABLE TIMES 10 DW 0 ; 分配10个字(2个字节),每个字都初始化为0
MOV EBX, [MY_TABLE] ; EBX中MY_TABLE的有效地址
MOV [EBX], 110 ; MY_TABLE[0] = 110
ADD EBX, 2 ; EBX = EBX +2
MOV [EBX], 123 ; MY_TABLE[1] = 123
MOV指令
MOV destination, source
MOV指令可能具有以下五种形式之一-
MOV 寄存器, 寄存器
MOV 寄存器, 立即数
MOV 寄存器, 内存
MOV 内存, 立即数
MOV 内存, 寄存器
汇编语言 变量
NASM提供了各种定义指令来为变量保留存储空间。define assembler指令用于分配存储空间。它可以用于保留以及初始化一个或多个字节。
为初始化数据分配存储空间
初始化数据的存储分配语句的语法为-
[variable-name] define-directive initial-value [,initial-value]...
其中,变量名是每个存储空间的标识符。汇编器为数据段中定义的每个变量名称关联一个偏移值
define指令有五种基本形式-
指令 目的 储存空间
DB 定义字节 分配1个字节
DW 定义字 分配2个字节
DD 定义双字 分配4个字节
DQ 定义四字 分配8个字节
DT 定义十个字节 分配10个字节
以下是一些使用define指令的示例-
choice DB 'y'
number DW 12345
neg_number DW -12345
big_number DQ 123456789
real_number1 DD 1.234
real_number2 DQ 123.456
请注意
字符的每个字节均以十六进制形式存储为其ASCII值。
每个十进制值都将自动转换为其等效的16位二进制数,并以十六进制数形式存储。
处理器使用小尾数字节顺序。
负数将转换为其2的补码表示形式。
短浮点数和长浮点数分别使用32位或64位表示
为未初始化的数据分配存储空间
reserve指令用于为未初始化的数据保留空间。reserve指令采用单个操作数,该操作数指定要保留的空间单位数。每个define指令都有一个相关的reserve指令。
保留指令有五种基本形式-
指令 目的
RESB 保留一个字节
RESW 保留字
RESD 保留双字
RESQ 保留四字
REST 保留十个字节
多种定义
一个程序中可以有多个数据定义语句。
choice DB 'Y' ;ASCII of y = 79H
number1 DW 12345 ;12345D = 3039H
number2 DD 12345679 ;123456789D = 75BCD15H
多重初始化
TIMES指令允许多次初始化为相同的值。例如,可以使用以下语句定义一个大小为9的标记的数组并将其初始化为零-
marks TIMES 9 DW 0
section .text
global _start ;must be declared for linker (ld)
_start: ;tell linker entry point
mov edx,9 ;message length
mov ecx, stars ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
stars times 9 db '*'
汇编常量
NASM提供了几个定义常量的指令。在前面的章节中,我们已经使用过EQU指令。我们将特别讨论三个指令-
- EQU
- %assign
- %define
EQU指令
EQU指令用于定义常量。EQU指令的语法如下-
CONSTANT_NAME EQU expression
%assign 指令
在%assign 指令可以用来定义数字常量像EQU指令。该指令允许重新定义。例如,您可以将常量TOTAL定义为-
%assign TOTAL 10
%define指令
在 %define 指令允许定义数值和字符串常量。该指令类似于C中的#define。例如,您可以将常量PTR定义为-
%define PTR [EBP+4]
算数指令
INC 指令
INC指令用于将操作数加1。它对可以在寄存器或内存中的单个操作数起作用。
INC指令具有以下语法
INC destination
DEC指令
DEC指令用于将操作数减1。它对可以在寄存器或内存中的单个操作数起作用。
DEC指令具有以下语法-
DEC destination
ADD & SUB指令
DD和SUB指令用于对字节,字和双字大小的二进制数据进行简单的加/减,即分别用于添加或减去8位,16位或32位操作数。
ADD和SUB指令具有以下语法-
ADD/SUB destination, source
ADD / SUB指令可以发生在-
寄存器 to 寄存器
内存 to 寄存器
寄存器 to 内存
寄存器 to 常量数据
内存 to 常量数据
MUL / IMUL指令
有两条指令用于将二进制数据相乘。MUL(乘法)指令处理未签名的数据,而IMUL(整数乘法)则处理签名的数据。两条指令都影响进位和溢出标志。
MUL/IMUL multiplier
DIV / IDIV指令
除法运算生成两个元素-商和余数,如果是乘法,则不会发生溢出,因为使用了双倍长度寄存器来保持乘积
DIV/IDIV divisor
逻辑指令
指令 格式
AND AND 操作数1,操作数2
OR OR 操作数1,操作数2
XOR XOR 操作数1,操作数2
TEST TEST 操作数1,操作数2
NOT NOT 操作数1
在所有情况下,第一个操作数都可以在寄存器或内存中。第二个操作数可以是寄存器/内存,也可以是立即数(常数)。但是,内存到内存操作是不可能的。这些指令比较或匹配操作数的位,并设置CF,OF,PF,SF和ZF标志。
AND 指令
AND 指令用于通过执行按位AND运算来支持逻辑表达式。如果两个操作数的匹配位均为1,则按位AND运算返回1,否则返回0。例如-
TEST 指令
与AND运算的工作原理相同,但与AND指令不同的是,它不会更改第一个操作数。因此,如果我们需要检查寄存器中的数字是偶数还是奇数,我们也可以使用TEST指令执行此操作,而无需更改原始数字
TEST AL, 01H
JZ EVEN_NUMBER
NOT 指令
NOT 指令实现按位NOT运算。NOT操作将操作数中的位取反。操作数可以在寄存器中,也可以在存储器中。
例如,
条件指令
汇编语言中的条件执行是通过几个循环和分支指令来完成的。这些指令可以更改程序中的控制流。在两种情况下观察到条件执行
- 无条件跳转 - 这是通过JMP指令执行的。条件执行通常涉及将控制权转移到不遵循当前执行指令的指令的地址。控制权的转移可以是前进的(执行新的指令集),也可以是后退的(重新执行相同的步骤)。
- 无条件跳转 - 这取决于条件由一组跳转指令j 执行。条件指令通过中断顺序流程来转移控制,而它们通过更改IP中的偏移值来进行控制。
让我们在讨论条件指令之前先讨论CMP指令
CMP指令
CMP 指令比较两个操作数。它通常用于条件执行中。该指令基本上从另一个操作数中减去一个操作数,以比较操作数是否相等。它不会干扰目标或源操作数。它与条件跳转指令一起用于决策
CMP destination, source
CMP 通常用于比较计数器值是否已达到需要运行循环的次数。考虑以下典型条件-
INC EDX
CMP EDX, 10 ; 比较计数器是否达到10
JLE LP1 ; 如果它小于或等于10,则跳转到LP1
无条件跳转
如前所述,这是通过JMP指令执行的。条件执行通常涉及将控制权转移到不遵循当前执行指令的指令的地址。控制权的转移可以是前进的(执行新的指令集),也可以是后退的(重新执行相同的步骤)。
句法
JMP 指令提供了一个标签名称,控制流将立即转移到该标签名称。JMP指令的语法是-
JMP label
以下代码段说明了JMP指令
MOV AX, 00 ; 将AX初始化为0
MOV BX, 00 ; 将BX初始化为0
MOV CX, 01 ; 初始化CX为1
L20:
ADD AX, 01 ; 增量AX
ADD BX, AX ; 将AX添加到BX
SHL CX, 1 ; 向左移动CX,这反过来使CX的值翻倍
JMP L20 ; 重复的语句
条件跳转
如果在条件跳转中满足某些指定条件,则控制流将转移到目标指令。根据条件和数据,有许多条件跳转指令。
以下是用于算术运算的有符号数据的条件跳转指令-
指令 描述 标志测试
JE/JZ 跳转等于或跳转零 ZF
JNE/JNZ 跳转不等于或跳转不为零 ZF
JG/JNLE 跳转大于或跳转不小于/等于 OF,SF,ZF
JGE/JNL 跳转大于/等于或不小于跳转 OF,SF
JL/JNGE 跳转小于或不大于/等于 OF,SF
JLE/JNG 跳少/等于或跳不大于 OF,SF,ZF
以下是对用于逻辑运算的无符号数据使用的条件跳转指令-
指令 描述 标志测试
JE/JZ 跳转等于或跳转零 ZF
JNE/JNZ 跳转不等于或跳转不为零 ZF
JA/JNBE 跳转向上或不低于/等于 CF,ZF
JAE/JNB 高于/等于或不低于 CF
JB/JNAE 跳到以下或跳到不高于/等于 CF
JBE/JNA 跳到下面/等于或不跳到上方 AF,CF
以下条件跳转指令有特殊用途,并检查标志的值-
指令 描述 标志测试
JXCZ 如果CX为零则跳转 没有
JC 如果携带则跳 CF
JNC 如果不携带则跳转 CF
JO 溢出时跳转 OF
JNO 如果没有溢出则跳转 OF
JP/JPE 跳校验或偶校验 PF
JNP/JPO 跳转无奇偶校验或跳转奇偶校验 PF
JS 跳跃符号(负值) SF
JNS 跳转无符号(正值) SF
循环指令
JMP指令可用于实现循环。例如,以下代码段可用于执行循环主体10次。
MOV CL, 10
L1:
<LOOP-BODY>
DEC CL
JNZ L1
但是,处理器指令集包括一组用于实现迭代的循环指令。基本的LOOP指令具有以下语法-
LOOP label
函数
过程或子例程在汇编语言中非常重要,因为汇编语言程序往往会很大。程序由名称标识。在此名称之后,将描述执行明确定义的作业的过程主体。该过程的结束由return语句指示。
以下是定义过程的语法-
proc_name:
procedure body
...
ret
通过使用CALL指令从另一个函数调用该过程。CALL指令应将被调用过程的名称作为参数,如下所示-
CALL proc_name
被调用过程通过使用RET指令将控制权返回给调用过程。
堆栈数据结构
堆栈是内存中类似数组的数据结构,可以在其中存储数据并从称为“堆栈顶部”的位置删除数据。需要存储的数据被“推送”到堆栈中,要检索的数据从堆栈中“弹出”。堆栈是一种LIFO数据结构,即首先存储的数据最后被检索。汇编语言为堆栈操作提供了两条指令:PUSH和POP。这些指令的语法如下-
PUSH operand
POP address/register
堆栈段中保留的内存空间用于实现堆栈。寄存器SS和ESP(或SP)用于实现堆栈。SS:ESP寄存器指向堆栈的顶部,该顶部指向插入到堆栈中的最后一个数据项,其中SS寄存器指向堆栈段的开头,而SP(或ESP)将偏移量设置为堆栈段。
堆栈实现具有以下特征-
只能将字或双字保存到堆栈中,而不是字节。
堆栈朝反方向增长,即朝着较低的存储器地址增长
堆栈的顶部指向插入堆栈中的最后一个项目。它指向插入的最后一个字的低字节。
正如我们所讨论的,在将寄存器的值用于某些用途之前将其存储在堆栈中;它可以通过以下方式完成-
; 将AX和BX寄存器内容保存在堆栈中
PUSH AX
PUSH BX
;将寄存器用着其他用途
MOV AX, VALUE1
MOV BX, VALUE2
...
MOV VALUE1, AX
MOV VALUE2, BX
;使用完之后恢复寄存器原始值
POP BX
POP AX
汇编 递归
汇编 宏
编写宏是确保使用汇编语言进行模块化编程的另一种方法。
宏是由名称分配的指令序列,可以在程序中的任何位置使用。
在NASM中,宏使用%macro和%endmacro指令定义。
宏以%macro指令开头,以%endmacro指令结尾。
%macro macro_name number_of_params
<macro body>
%endmacro
8813

被折叠的 条评论
为什么被折叠?



