汇编语言学习(5)

更好的阅读体验 YinKai 's Blog。

常量

​ NASM 提供了几个定义常量的指令,在上面我们使用过的有 EQU 指令,下面我们来重点介绍三个指令:

  • EQU
  • %assign
  • %define
EQU 指令

​ EQU 指令常用于定义常量,其语法如下:

CONSTANT_NAME EQU expression

​ 例如:

MY_NAME equ 'YinKai1'

​ 然后我们可以在代码中使用这个常量值,例如:

mov ecx, MY_NAME

​ EQU 语句的操作数也可以是表达式,如下:

length equ 20
width equ 10
AREA equ length * width

​ 下面示例演示了 EQU 指令的使用:

SYS_EXIT  equ 1
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1section .datamsg1 db 'Hello, programmers!', 0xA, 0xDlen1 equ $ - msg1msg2 db 'Welcome to the world of,', 0xA, 0xDlen2 equ $ - msg2msg3 db 'Linux assembly programming!', 0xA, 0xDlen3 equ $ - msg3section .textglobal _start_start:mov eax, SYS_WRITEmov ebx, STDOUTmov ecx, msg1mov edx, len1int 0x80mov eax, SYS_WRITEmov ebx, STDOUTmov ecx, msg2mov edx, len2int 0x80mov eax, SYS_WRITEmov ebx, STDOUTmov ecx, msg3mov edx, len3int 0x80mov eax, SYS_EXITint 0x80

​ 上述程序会输出:

Hello, programmers!
Welcome to the world of,
Linux assembly programming!
%assign 指令

​ %assign 指令是在预处理阶段中用于定义数字常量的汇编指令。它类似于 EQU 指令,与 EQU 指令不同的是,%assign 允许在后续代码中重新定义常量的值,这对于在程序的不同部分或不同文件中使用相同的符号名但不同的值很有用。

​ 下面是一个使用样例:

%assign TOTAL 10

​ 然后在代码的后面,还可以再次定义:

%assign TOTAL 20
%define 指令

​ %define 指令是在汇编语言预处理阶段用于定义宏的指令,类似于 C 语言中的 #define 预处理指令,允许定义数字、字符串常量以及宏。

​ 使用示例如下,它会将 PTR 替换成 [EBP+4]

%define PTR [EBP+4]section .textmov eax, PTR

:::warning

​ 上述三种指令都是区分大小写的。

:::

算术指令

INC 指令

​ INC 指令用于将操作数加一,它适用于可以位于寄存器或内存中的单个操作数。

​ 语法如下:

INC destination

​ destination 是要增加的操作数,可以是 8 位、16 位 或 32 位的寄存器或内存地址。

​ 使用示例:

INC EBX		; 增加 32 位寄存器 EBX 中的值
INC DL		; 增加 8 位寄存器 DL 中的值
INC [count]	; 增加存储在变量 count 的内存位置中的值
DEC 指令

​ DEC 指令用于将操作数减一,它适用于可以位于寄存器或内存中的单个操作数。

​ 语法如下:

DEC destination

​ destination 是要减小的操作数,可以是 8 位、16 位 或 32 位的寄存器或内存地址。

​ 使用示例:

segment .datacount dw  0        ; 16位的变量 count,初始化为0value db  15       ; 8位的变量 value,初始化为15segment .textinc word [count]   ; 将变量 count 的值增加1dec byte [value]   ; 将变量 value 的值减少1mov ebx, count     ; 将变量 count 的地址存入寄存器 ebxinc word [ebx]     ; 通过寄存器 ebx 增加变量 count 的值mov esi, value     ; 将变量 value 的地址存入寄存器 esidec byte [esi]     ; 通过寄存器 esi 减少变量 value 的值
ADD 和 SUB 指令

​ ADD 和 SUB 指令用于执行字节、字和双字大小的二进制数据的加法和减法,它们分别用于8位、16位或32位操作数的加法和减法。

​ 语法如下:

ADD/SUB destination, source

destination 是目标操作数,source 是源操作数。这两个操作数可以是寄存器、内存地址或常数。

​ 使用示例:

section .datavalue1 dd 10       ; 双字(32位)大小的变量,初始化为10value2 dw 5        ; 字(16位)大小的变量,初始化为5result db 0        ; 字节(8位)大小的变量,用于存储结果section .text; 32位加法mov eax, [value1]   ; 将value1的值加载到寄存器eaxadd eax, 20         ; 将20加到eax中mov [value1], eax   ; 将结果存回value1; 16位减法mov bx, [value2]    ; 将value2的值加载到寄存器bxsub bx, 3           ; 从bx中减去3mov [value2], bx    ; 将结果存回value2; 8位加法mov al, 30          ; 将30加载到寄存器aladd al, 15          ; 将15加到al中mov [result], al    ; 将结果存入result

​ 下面再给一个复杂一点的例子:

; 定义系统调用号
SYS_EXIT  equ 1
SYS_READ  equ 3
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1section .data ; 提示用户输入第一个数字的消息msg1 db "Enter a digit ", 0xA,0xD len1 equ $- msg1 ; 提示用户输入第二个数字的消息msg2 db "Please enter a second digit", 0xA,0xD len2 equ $- msg2 ; 提示计算结果的消息msg3 db "The sum is: "len3 equ $- msg3section .bss; 存储用户输入的第一个数字num1 resb 2 ; 存储用户输入的第二个数字num2 resb 2 ; 存储计算结果res resb 1    section	.textglobal _start    ; 声明为gcc使用的程序入口_start:; 输出提示信息,要求用户输入第一个数字mov eax, SYS_WRITE         mov ebx, STDOUT         mov ecx, msg1         mov edx, len1 int 0x80                ; 从标准输入读取用户输入的第一个数字,存储在num1中mov eax, SYS_READ mov ebx, STDIN  mov ecx, num1 mov edx, 2int 0x80            ; 输出提示信息,要求用户输入第二个数字mov eax, SYS_WRITE        mov ebx, STDOUT         mov ecx, msg2          mov edx, len2         int 0x80; 从标准输入读取用户输入的第二个数字,存储在num2中mov eax, SYS_READ  mov ebx, STDIN  mov ecx, num2 mov edx, 2int 0x80        ; 输出提示信息,指示即将显示计算结果mov eax, SYS_WRITE         mov ebx, STDOUT         mov ecx, msg3          mov edx, len3         int 0x80; 将第一个数字转换为数字并存储在eax寄存器中mov eax, [num1]sub eax, '0'; 将第二个数字转换为数字并存储在ebx寄存器中mov ebx, [num2]sub ebx, '0'; 将eax和ebx相加,得到和,存储在res中add eax, ebx; 将和转换为ASCII并存储在res中add eax, '0'mov [res], al; 使用SYS_WRITE将计算得到的和输出到标准输出mov eax, SYS_WRITE        mov ebx, STDOUTmov ecx, res         mov edx, 1        int 0x80exit:    ; 使用SYS_EXIT系统调用退出程序mov eax, SYS_EXIT   xor ebx, ebx int 0x80

​ 上述代码向用户询问两个数字,分别将数字存储在 EAX 和 EBX 寄存器中,将值相加,将结果存储在内存位置 res 中,最后显示结果。

​ 编译运行后,输出如下:

Enter a digit
4
Please enter a second digit
5
9

​ 再来一个带有硬编码的例子:

section	.textglobal _start    ;must be declared for using gcc_start:             ;tell linker entry pointmov	eax,'3'sub     eax, '0'mov 	ebx, '4'sub     ebx, '0'add 	eax, ebxadd	eax, '0'mov 	[sum], eaxmov	ecx,msg	mov	edx, lenmov	ebx,1	;file descriptor (stdout)mov	eax,4	;system call number (sys_write)int	0x80	;call kernelmov	ecx,summov	edx, 1mov	ebx,1	;file descriptor (stdout)mov	eax,4	;system call number (sys_write)int	0x80	;call kernelmov	eax,1	;system call number (sys_exit)int	0x80	;call kernelsection .datamsg db "The sum is:", 0xA,0xD len equ $ - msg   segment .bsssum resb 1

​ 上述代码编译运行后会输出:

The sum is:
7
MUL/IMUL 指令

​ 有两条指令用于将二进制数据相乘,MUL 指令处理无符号数据,IMUL 指令处理有符号数据。

​ 语法如下:

MUL/IMUL multiplier

​ 两种情况下的被乘数都位于累加器中,具体取决于被乘数和乘数的大小,并且生成的乘积也取决于操作数的大小,存储在两个寄存器中。

​ 下面我们来看看三种不同情况下的 MUL 指令:

序号场景
1当两个字节相乘时
被乘数在 AL 寄存器中,乘数是内存中或另一个寄存器中的一个字节。结果存放在 AX 寄存器中,乘积的高 8 位存放在 AH 中,乘积的低 8 位存放在 AL 中
2当两个单字值相乘时
被乘数应该在 AX 寄存器中,乘数是内存或另一个寄存器中的一个字。
生成的结果是双字,需要两个寄存器。高位(最左边)部分存储在 DX 中,低位(最右边)部分存储在 AX 中
3当两个双字相乘时
当两个双字值相乘时,被乘数应位于 EAX 中,乘数是存储在内存或另一个寄存器中的双字值。
生成的乘积存储在 EDX:EAX 寄存器中,即高位 32 位存储在 EDX 寄存器中,低位 32 位存储在 EAX 寄存器中。

​ 示例:将 3 与 2 相乘,并显示结果:

section .textglobal _start    ; 必须声明为gcc使用的程序入口_start:; 将字符 '3' 转换为数字并存储在 AL 中mov al, '3'sub al, '0'; 将字符 '2' 转换为数字并存储在 BL 中mov bl, '2'sub bl, '0'; 使用 MUL 指令将 AL 和 BL 相乘,结果存储在 AX 中mul bl; 将结果转换为字符并存储在 AL 中add al, '0'; 将结果存储在内存位置 res 中mov [res], al; 准备输出消息mov ecx, msgmov edx, lenmov ebx, 1       ; 文件描述符 (stdout)mov eax, 4       ; 系统调用号 (sys_write)int 0x80         ; 调用内核输出消息到标准输出; 准备输出计算结果mov ecx, resmov edx, 1mov ebx, 1       ; 文件描述符 (stdout)mov eax, 4       ; 系统调用号 (sys_write)int 0x80         ; 调用内核输出计算结果到标准输出; 退出程序mov eax, 1       ; 系统调用号 (sys_exit)int 0x80         ; 调用内核退出程序section .datamsg db "The result is:", 0xA,0xD    ; 输出消息len equ $- msgsection .bssres resb 1       ; 存储计算结果的空间

​ 编译输出结果如下:

The result is:
6
DIV/IDIV 指令

​ 除法运算也有两个指令,DIV(除法)指令用于无符号数据,IDIV(整数除法)指令用于有符号数据。

:::warning

​ 除法运算生成两个元素 - 一个和一个余数。 在乘法的情况下,不会发生溢出,因为使用双倍长度寄存器来保存乘积。 然而,在除法的情况下,可能会发生溢出。 如果发生溢出,处理器会产生中断。

:::

​ 使用的语法如下:

DIV/IDIV	divisor

​ 下面根据不同操作数大小分为三种不同的情况:

序号场景
1当除数为1 byte时 −假定被除数位于 AX 寄存器(16 位)中。 除法后,商存入 AL 寄存器,余数存入 AH 寄存器。
2当除数为 1 个 word 时 −假定被除数为 32 位长,位于 DX:AX 寄存器中。 高 16 位在 DX 中,低 16 位在AX中。 除法后,16 位商进入 AX 寄存器,16 位余数进入 DX 寄存器。
3除数为 doubleword 时 −假设被除数为 64 位长并位于 EDX:EAX 寄存器中。 高阶 32 位在 EDX 中,低阶 32 位在 EAX 中。 除法后,32 位商进入 EAX 寄存器,32 位余数进入 EDX 寄存器。

​ 使用示例:

section .textglobal _start    ; 必须声明为gcc使用的程序入口_start:; 将字符 '8' 转换为数字并存储在 AX 中mov ax, '8'sub ax, '0'; 将字符 '2' 转换为数字并存储在 BL 中mov bl, '2'sub bl, '0'; 使用 DIV 指令将 AX 除以 BL,商存储在 AL 中,余数存储在 AH 中,默认操作累加器(通常为 AX)div bl; 将商(AL)转换为字符并存储在 AX 中add ax, '0'; 将结果存储在内存位置 res 中	mov [res], ax; 准备输出消息mov ecx, msgmov edx, lenmov ebx, 1       ; 文件描述符 (stdout)mov eax, 4       ; 系统调用号 (sys_write)int 0x80         ; 调用内核输出消息到标准输出; 准备输出计算结果mov ecx, resmov edx, 1mov ebx, 1       ; 文件描述符 (stdout)mov eax, 4       ; 系统调用号 (sys_write)int 0x80         ; 调用内核输出计算结果到标准输出; 退出程序mov eax, 1       ; 系统调用号 (sys_exit)int 0x80         ; 调用内核退出程序section .datamsg db "The result is:", 0xA,0xD    ; 输出消息len equ $- msgsection .bssres resb 1       ; 存储计算结果的空间

​ 编译运行后的结果如下:

The result is:
4

逻辑指令

​ 处理器指令集提供了 AND、OR、XOR、TEST、NOT 这几个布尔逻辑指令,以满足程序的需求,其中包括对位进行测试、设置和清除。

​ 这些指令的使用格式如下:

序号说明格式
1ANDAND operand1, operand2
2OROR operand1, operand2
3XORXOR operand1, operand2
4TESTTEST operand1, operand2
5NOTNOT operand1

​ 在这些指令中,第一个操作数可以是内存或寄存器中的内容,第二个操作数可以是内存或寄存器或立即数(常量)。

:::warning

​ 直接在内存之间的操作是不被允许的!!!

:::

​ 这些指令比较或匹配操作数的位,并根据结果设置 CF(进位标志)、OF(溢出标志)、PF(奇偶校验标志)、SF(符号标志)和 ZF(零标志),这样使得程序能够进行灵活的逻辑运算,同时提供了对操作结果的状态标志进行监测的能力。

AND 指令

​ AND 指令是用于执行按位 AND(与)运算的指令。按位与运算返回一个结果并将结果存储到第一个操作数中,其中仅当两个操作数的相应位都为 1 时,结果位才为 1;反之则结果位为 0.

使用示例:假设 BL 寄存器包含 0011 1010,我们想要把高位清零,可以使用 AND 指令与 OFH(0000 1111)进行运算,就可以达到高位清零的效果:

AND BL, OFH

​ 如果我们想要检验一个数是奇数还是偶数,可以通过对 AL 寄存器中的数字与 01H(0000 0001) 进行 AND 运算,可以有效检查低位:

AND AL, 01H ; AND 操作,检查最低有效位
JZ  EVEN_NUMBER ; 如果结果为零,跳转到 EVEN_NUMBER(偶数)

JZ:条件跳转指令,含义是 ‘Jump if Zero’,即如果结果为零(ZF 标志位为 1),则执行跳转。

​ 下面再来看一个完整的示例:演示了如何使用 AND 指令来检查和显示奇偶性

section .text
global _start_start:; 将数字 8 放入寄存器 axmov   ax, 8h; 对寄存器 ax 与 1 进行 AND 操作,检查最低位and   ax, 1; 如果结果为零,跳转到 evnn(偶数)jz    evnn; 显示奇数消息mov   eax, 4         ; 系统调用号(sys_write)mov   ebx, 1         ; 文件描述符(stdout)mov   ecx, odd_msg   ; 要写入的消息mov   edx, len2      ; 消息长度int   0x80           ; 调用内核jmp   outprogevnn:   ; 显示偶数消息mov   ah,  09hmov   eax, 4         ; 系统调用号(sys_write)mov   ebx, 1         ; 文件描述符(stdout)mov   ecx, even_msg  ; 要写入的消息mov   edx, len1      ; 消息长度int   0x80           ; 调用内核outprog:; 退出程序mov   eax, 1          ; 系统调用号(sys_exit)int   0x80           ; 调用内核section .data
even_msg  db  'Even Number!' ; 显示偶数消息
len1  equ  $ - even_msg odd_msg db  'Odd Number!'    ; 显示奇数消息
len2  equ  $ - odd_msg
OR 指令

​ OR 指令用于执行按位或运算,支持逻辑表达式。按位或运算的规则是,如果任一操作数的相应位为1,或者两个操作数的相应位都为1,则结果位为1。如果两个位都为0,则结果位为0。同样,运算的结果是存储在第一个操作数中,操作数可以是寄存器或内存中的值。

​ 下面来看一个完整的使用样例:我们将 3 与 5 进行 OR 操作,并输出结果

section .bssres resb 1  ; 用于存储 OR 运算的结果section .textglobal _start_start:; 将值5存储在寄存器AL中mov al, 5; 将值3存储在寄存器BL中mov bl, 3; 对AL和BL寄存器执行按位或运算,结果应为7or al, bl; 将结果转换为ASCII码add al, byte '0'; 将结果存储在res变量中mov [res], al; 调用sys_write系统调用,将结果写入stdoutmov eax, 4mov ebx, 1mov ecx, resmov edx, 1int 0x80outprog:; 调用sys_exit系统调用,退出程序mov eax, 1int 0x80

​ 上述代码编译运行后,输出的结果如下:

7
XOR 指令

​ XOR(异或)指令用于执行按位异或运算。按位异或的规则是,当且仅当操作数中的位不同时,结果位被设置为1。如果操作数的位相同(均为0或均为1),则结果位被清零为0。

​ 对于指令 XOR EAX, EAX,它将寄存器 EAX 中的值与自身进行异或操作,这实际上会将寄存器清零。因为任何值与自身进行异或运算的结果都是0。

TEST 指令

​ TEST 指令的工作方式类似于 AND 指令,但与 AND 指令不同的是,它不会更改第一个操作数的内容。该指令执行按位与运算,并根据结果设置条件标志(CF、OF、PF、SF 和 ZF)。这使得 TEST 指令非常适合用于检查某个值的特定位状态,而不更改该值。

​ 使用示例:

TEST AL, 01H
JZ EVEN_NUMBER

​ 上述代码演示了如何使用 TEST 指令来检查 AL 寄存器中的数字是否为偶数。如果 AL 寄存器中的值与 01H 进行按位与运算后结果为零(ZF 标志被设置),则跳转到标签 EVEN_NUMBER。

NOT 指令

​ NOT 指令执行按位 NOT 运算,即反转操作数中的每一位。操作数可以位于寄存器或存储器中。

​ 示例:

Operand1:    0101 0011
After NOT -> Operand1:    1010 1100

​ 上述示例展示了如何使用 NOT 指令对二进制数字进行按位取反操作。

条件执行

​ 在汇编语言中,实现条件执行的机制主要通过多个循环和分支指令完成,这些指令能够改变程序的控制流程。

​ 条件执行一般分为两种情况:

  1. 无条件跳转:

    无条件跳转是通过 JMP 指令实现的,在这种情况下,条件执行涉及将程序的控制转移到不是紧随当前正在执行指令的指令的地址上。这种跳转转移可以是向前的,以执行一组新的指令,也可以是向后的,以程序执行相同的步骤。

  2. 条件跳转

    条件跳转是通过一组跳转指令 j 来实现的,其中条件是根据特定条件而定。这些条件指令通过中断正常的指令执行流程来转移控制,通过修改指令指针寄存器(IP)中的偏移值来实现。

CMP 指令

​ 在讨论条件指令前,我们先来看看 CMP 指令。

​ CMP 指令是一种用于比较两个操作数的指令,通常在条件执行中使用。该指令主要通过对一个操作数与另一个操作数进行减法运算来实现比较,以确定这两个操作数是否相等。

​ 值得注意的是,CMP 指令执行比较操作,但不会影响目标或源操作数的值。

​ 语法如下:

CMP destination, source

​ 其中目标操作数可以是位于寄存器或内存中,而源操作数可以是常量(立即数)数据、寄存器或内存。

​ 使用示例:

CMP DX, 00
JE	L7
...
L7: ...

​ 比较 DX 寄存器的值与零,如果相等,则跳转到标签 L7.

​ CMP 指令通常用于比较计数器值是否达到执行循环所需的次数,以下是一个经典应用的示例:

INC EDX
CMP EDX, 10
JLE LP1

​ 比较计数器 EDX 是否达到 10,如果未达到 10 则跳转到 LP1 标签。

无条件跳转

​ 无条件跳转是通过 JMP 指令实现的,该指令使程序控制流立即转移到指定标签的地址。

​ JMP 指令的语法如下:

JMP label

​ 使用示例:

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		; 重复执行上述语句

​ 上面程序中的 SHL 指令用于将二进制数向左移指定的位数,以达到倍增的效果。在这个例子中,JMP 指令用于无条件跳转到标签 L20,从而创建一个无限循环,反复执行 MOV、ADD 和 SHL 指令。

条件跳转

​ 条件跳转是指在程序执行过程中,根据特定条件的满足与否,控制流会转移到指定的目标指令。

​ 条件跳转指令的选择取决于不同的条件和数据状态。

​ (1)以下是用于有符号数据上的算术运算的条件跳转指令,以及它们所检查的标志:

说明描述已测试标志
JE/JZJump Equal or Jump Zero当零标志位(ZF)被设置时跳转
JNE/JNZJump not Equal or Jump Not Zero当零标志位(ZF)未被设置时跳转
JG/JNLEJump Greater or Jump Not Less/Equal当溢出标志(OF)、符号标志(SF)和零标志(ZF)符合条件时跳转
JGE/JNLJump Greater/Equal or Jump Not Less当溢出标志(OF)和符号标志(SF)符合条件时跳转
JL/JNGEJump Less or Jump Not Greater/Equal当溢出标志 (OF) 和符号标志 (SF) 符合条件时跳转。
JLE/JNGJump Less/Equal or Jump Not Greater当溢出标志 (OF) 、符号标志 (SF) 和零标志 (ZF) 符合条件时跳转。

(2)以下是用于无符号数据的逻辑运算的条件跳转指令,以及它们所检查的标志:

说明描述已测试标志
JE/JZJump Equal or Jump Zero当零标志位 (ZF) 被设置时跳转。
JNE/JNZJump not Equal or Jump Not Zero当零标志位 (ZF) 未被设置时跳转。
JA/JNBEJump Above or Jump Not Below/Equal当进位标志 (CF) 和零标志 (ZF) 符合条件时跳转。
JAE/JNBJump Above/Equal or Jump Not Below当进位标志 (CF) 符合条件时跳转。
JB/JNAEJump Below or Jump Not Above/Equal当进位标志 (CF) 符合条件时跳转。
JBE/JNAJump Below/Equal or Jump Not Above当辅助进位标志 (AF) 和进位标志 (CF) 符合条件时跳转。

​ (3)另外,以下条件跳转指令具有特殊用途并检查相应标志的值:

说明描述已测试标志
JXCZJump if CX is Zero当 CX 寄存器的值为零时跳转。
JCJump If Carry当进位标志 (CF) 被设置时跳转。
JNCJump If No Carry当进位标志 (CF) 未被设置时跳转。
JOJump If Overflow当溢出标志 (OF) 被设置时跳转。
JNOJump If No Overflow当溢出标志 (OF) 未被设置时跳转。
JP/JPEJump Parity or Jump Parity Even当奇偶标志 (PF) 被设置时跳转。
JNP/JPOJump No Parity or Jump Parity Odd当奇偶标志 (PF) 未被设置时跳转。
JSJump Sign (negative value)当符号标志 (SF) 被设置时跳转。
JNSJump No Sign (positive value)当符号标志 (SF) 未被设置时跳转。

​ 先来看一个简单的示例:

CMP AL, BL
JE EQUAL
CMP AL, BH
JE EQUAL
CMP AL, CL
JE EQUAL
NON_EQUAL:...
EQUAL:...

​ 上述代码在执行过程中会根据 AL 寄存器和 BL、BH、CL 寄存器的比较结果,若相等则跳转到标签 EQUAL,否则执行标签 NON_EQUAL 后续的指令。

示例

​ 下面的程序通过比较三个两位数变量,找到其中的最大值,并将其结果输出到标准输出:

section	.textglobal _start         ; 必须声明为使用gcc_start:	                 ; 告诉链接器入口点mov   ecx, [num1]     ; 将num1的值加载到ecx寄存器cmp   ecx, [num2]     ; 将ecx与num2的值比较jg    check_third_num ; 如果ecx大于num2,跳转到check_third_num标签mov   ecx, [num2]     ; 如果跳转到check_third_num,将num2的值加载到ecx寄存器check_third_num:cmp   ecx, [num3]     ; 将ecx与num3的值比较jg    _exit            ; 如果ecx大于num3,跳转到_exit标签mov   ecx, [num3]     ; 如果跳转到_exit,将num3的值加载到ecx寄存器_exit:mov   [largest], ecx   ; 将最大值存储在largest变量中mov   ecx, msg         ; 将消息字符串的地址加载到ecx寄存器mov   edx, len         ; 将消息字符串的长度加载到edx寄存器mov   ebx,1            ; 文件描述符(标准输出)mov   eax,4            ; 系统调用号(sys_write)int   0x80             ; 调用内核mov   ecx, largest     ; 将存储最大值的变量地址加载到ecx寄存器mov   edx, 2           ; 将输出的字节数加载到edx寄存器mov   ebx,1            ; 文件描述符(标准输出)mov   eax,4            ; 系统调用号(sys_write)int   0x80             ; 调用内核mov   eax, 1           ; 将系统调用号设置为退出程序int   80h              ; 调用内核section	.datamsg db "The largest digit is: ", 0xA,0xD ; 定义包含消息字符串的数据段len equ $- msg        ; 计算消息字符串的长度num1 dd '47'          ; 定义包含两位数值的数据段num2 dd '22'          ; 定义包含两位数值的数据段num3 dd '31'          ; 定义包含两位数值的数据段segment .bsslargest resb 2        ; 定义一个字节的空间,用于存储最大值

​ 上面提供了对每个指令和标签的解释,帮助理解代码的功能和执行流程。

​ 程序编译运行后会输出结果如下:

he largest digit is:
47

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/234850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java】【SQL】sql中 DATE_FORMAT函数详解

在实际应用开发中,使用sql语句也属于开发者的一部分,这次来说说DATE_FORMAT函数。 引言:实际上在使用Java开发过程中,有很多业务场景下,都有时间类型的参数参与。前后端进行交互的时候,针对时间类型的格式…

数据结构的基本概念

可以想象,将一大堆杂乱无章的数据交给计算机处理是很不明智的,结果是计算机处理的效率非常低,有时甚至根本无法进行处理。于是人们开始考虑如何更有效地描述、表示、存储数据,这就是数据结构需要解决的问题。 ▶1.数据结构的发展…

GBASE南大通用读取数据库数据

通过GBASE南大通用 ADO.NET 接口读取GBASE南大通用Server 数据需要下面的步骤: 1) 使用 GBASE南大通用Connection 创建数据库连接对象 2) 使用 GBASE南大通用Command 创建命令对象 3) 使用连接对象打开连接 4) 设置命令对象的 CommandText 属性,指明…

1.基本数据类型与变量

1.基本数据类型与变量 1.1 基本数据类型 Java属于强类型语言,强类型语言必须为每一个变量声明一种类型。 Java中基本数据类型也称做简单数据类型,包括以下八种 1、整型 byte 、short 、int 、long 2、浮点型 float 、 double 3、字符型 char 4…

贪吃蛇(三)绘制蛇身

绘制蛇身的逻辑不难,存储上面使用结构体。 第一行和第十九行绘制--其它行,绘制|,分别在头尾处。 (1) 扫描蛇身,如果扫描到则绘制[]。 (2) 扫描蛇身,如果扫描不到则绘制空白。 #include"curses.h"struct Sn…

文件操作入门指南

目录 一、为什么使用文件 二、什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 三、文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 四、文件的顺序读写 ​编辑 🌻深入理解 “流”: 🍂文件的顺序读写函数介绍: …

爬虫实战案例 -- 爬取豆瓣读书网页内容

进入网站检查信息 , 确定请求方式以及相关数据 找到爬取目标位置 开始敲代码 # 链接网站 def url_link(url):res requests.get(url,headers headers)response res.textparse_data(response)# 爬取信息 def parse_data(data):msg <li\sclass"media\sclearfix…

处理器管理

(1) 为什么程序并发执行会产生间断性特征&#xff0c;并失去封闭性和可再现性&#xff1f; 解&#xff1a; 之所以产生间断性特征是因为多个程序在并发执行时&#xff0c;需要为了完成同一项任务而相互合作&#xff0c;并发执行的程序间的这种相互制约导致了“暂停—执行—暂…

Java Web Day03_CSS样式

一、超级链接伪类 <a href”#”>超级链接</>a:link 未点击前 a:hover 鼠标悬停 a:active 鼠标正在点击&#xff08;左键按着不放&#xff09; a:visited 点击后 加载顺序一般我们是按照&#xff1a;link -> visited -> hover -> active 列表样式 list-…

07-微服务架构之问题和解决方案的探讨

文章目录 前言一、服务发现与治理二、分布式数据管理三、分布式事务管理四、跨服务调用管理五、版本控制与发布管理六、运维管理 总结 前言 在上一章中&#xff0c;我们已经详细探讨了微服务设计的基本原则和步骤。现在&#xff0c;我们将开始实际运用微服务架构&#xff0c;然…

在 CentOS 上使用 Docker 运行 RabbitMQ

在 CentOS 上使用 Docker 运行 RabbitMQ 使用Docker来运行RabbitMQ非常方便&#xff0c;以下是一个简单的步骤&#xff0c;以YAML配置文件方式创建和运行RabbitMQ容器。 构建容器 创建Docker Compose文件 创建一个docker-compose.yml文件&#xff0c;内容如下&#xff1a; …

C# 将 Word 转化分享为电子期刊

目录 需求 方案分析 相关库引入 关键代码 Word 转 Pdf Pdf 转批量 Jpeg Jpeg 转为电子书 实现效果演示 小结 需求 曾经的一个项目&#xff0c;要求实现制作电子期刊定期发送给企业进行阅读&#xff0c;基本的需求如下&#xff1a; 1、由编辑人员使用 Microsoft Word…

KL散度、CrossEntropy详解

文章目录 0. 概述1. 信息量1.1 定义1.2 性质1.3 例子2. 熵 Entropy2.1 定义2.2 公式2.3 例子3. 交叉熵 Cross Entropy3.1 定义3.2 公式3.3 例子4. KL 散度(相对熵)4.1 公式

vue之全局请求loading

场景&#xff1a;我们往往在项目中会因为表单重复提交而烦恼&#xff0c;往往都会想到很高大上的两个词语——防抖、节流。但网上的很多方法都还是不够灵活、通用&#xff0c;那么下面介绍一下我的方法&#xff08;在最后&#xff09; 页面loading 往往都是一个页面写一堆loa…

MyBatis-Plus如何 关闭SQL日志打印

前段时间公司的同事都过来问我&#xff0c;hua哥公司的项目出问题了&#xff0c;关闭不了打印sql日记&#xff0c;项目用宝塔自己部署的&#xff0c;磁盘满了才发现大量的打印sql日记&#xff0c;他们百度过都按照网上的配置修改过不起作用&#xff0c;而且在调试时候也及为不方…

docker查看日志

1、查看容器所有日志 docker logs <容器名称或ID> 2、查看容器最新日志 docker logs -f <容器名称或ID> 3、查看指定时间范围内的容器日志 docker logs --since<开始时间> --until<结束时间> <容器名称或ID> docker logs --since2022-01-0…

mysql的asc和desc全称

原文&#xff1a;http://t.csdnimg.cn/BJ2sUhttp://t.csdnimg.cn/BJ2sU

[AutoSar]基础部分 RTE 02 S/R Port 显式/隐式

目录 关键词平台说明一、显式&#xff08;Explicit&#xff09;和隐式&#xff08;Implicit&#xff09;1.1 显式模式1.1.1code 二、隐式模式2.1 code 三、区别 关键词 嵌入式、C语言、autosar、EcuM、Rte 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语…

字符串逆序输出

逆序输出就是本来abc输出的&#xff0c;然后我想让他输出成cba&#xff0c;那么我们还是要用到for循环&#xff0c;只不过原先是从零开始往上加&#xff0c;这回呢&#xff0c;是从上面往下减 我们观察上面这个图片&#xff0c;我们想要输出olleh&#xff0c;那么我们就要从4开…

2024山东养老展,2024济南养老用品展览会5月27日开幕

飞鲨展览第六届中国&#xff08;济南&#xff09;国际养老服务业展览会&#xff0c;将于2024年5月27-29日举办&#xff1b; CSOLDE 2024第6届中国&#xff08;济南&#xff09;国际养老服务业展览会&#xff08;CSOLDE山东老博会&#xff09; CSOLDE 2024 sixth China (Jinan)…