当前位置: 首页 > news >正文

基于x86_64汇编语言简单教程6: 变量,常量,与运算

目录

变量

为未初始化的数据分配存储空间

多重初始化

常量

equ指令

%assign 指令

%define指令

算数指令

inc 自增指令

dec指令

ADD和SUB指令

imul/mul和idiv/div

实践:我们来写一个简单的一位数加法器

NASM逻辑指令

AND 指令

OR 指令

XOR 指令

TEST 指令

NOT 指令

实践:测试一个数是技术还是偶数


变量

我们下面来聊一聊变量这个概念。NASM提供了各种定义指令来为变量保留存储空间。define assembler指令用于分配存储空间。它可以用于保留以及初始化一个或多个字节。

var_name dw 1234, 2345, ...

这就是一个经典的定义变量的方式。当然,dw可以换成这些内容:

指令目的储存空间
DB定义字节分配1个字节
DW定义字分配2个字节
DD定义双字分配4个字节
DQ定义四字分配8个字节
DT定义十个字节分配10个字节
  • 字符的每个字节均以十六进制形式存储为其ASCII值。

  • 每个十进制值都将自动转换为其等效的16位二进制数,并以十六进制数形式存储。这是在预处理阶段就做好的!

  • 处理器使用小尾数字节顺序。

  • 负数将转换为其2的补码表示形式。

  • 短浮点数和长浮点数分别使用32位或64位表示。

为未初始化的数据分配存储空间

reserve指令用于为未初始化的数据保留空间。reserve指令采用单个操作数,该操作数指定要保留的空间单位数。每个define指令都有一个相关的reserve指令。

指令目的
RESB保留一个字节
RESW保留字
RESD保留双字
RESQ保留四字
REST保留十个字节

多重初始化

TIMES指令允许多次初始化为相同的值。例如,可以使用以下语句定义一个大小为9的标记的数组并将其初始化为零-

marks  TIMES  9  DW  0

常量

NASM提供了几个定义常量的指令。在前面的章节中,我们已经使用过EQU指令。我们将特别讨论三个指令-

  • equ

  • %assign

  • %define

equ指令

equ指令用于定义常量。EQU指令的语法如下-

CONSTANT_NAME EQU expression
TOTAL_STUDENTS equ 50

然后,您可以在代码中使用此常量值,例如

mov  ecx,  TOTAL_STUDENTS 
cmp  eax,  TOTAL_STUDENTS

equ语句的操作数可以是表达式-

LENGTH equ 20
WIDTH  equ 10
AREA   equ length * width

%assign 指令

%assign 指令可以用来定义数字常量像EQU指令。该指令允许重新定义。例如,您可以将常量TOTAL定义为-

%assign TOTAL 10

在代码的后面,您可以将其重新定义为-

%assign  TOTAL  20

注意 - 指令区分大小写。

%define指令

%define 指令允许定义数值和字符串常量。该指令类似于C中的#define。例如,您可以将常量PTR定义为-

%define PTR [EBP+4]

上面的代码用[EBP + 4]替换了PTR。

该指令还允许重新定义,并且区分大小写。

算数指令

我们知道,存储的数据必须能够被运算,这才有意义。我们下面来看看一些常见的运算指令。

inc 自增指令

inc指令(increase)用于将操作数加1。它对可以在寄存器或内存中的单个操作数起作用。

; 完全等价于 t++, 在C编译器就会翻译成inc指令
inc destination

目标操作数可以是8位,16位或32位操作数。

INC EBX      ;  32-bit 寄存器 自增1
INC DL       ;  8-bit 寄存器 自增1
INC [count]  ;  变量count  自增1

dec指令

dec(decline)指令实际上就是对操作数-1,inc指令类似!

DEC destination

ADD和SUB指令

ADDSUB指令用于对字节,字和双字大小的二进制数据进行简单的加/减,即分别用于添加或减去8位,16位或32位操作数。ADD和SUB指令具有以下语法

ADD/SUB destination, source

ADD / SUB指令可以发生用在

  • 寄存器 -> 寄存器

  • 内存 -> 寄存器

  • 寄存器 -> 内存

  • 寄存器 -> 常量数据

  • 内存 -> 常量数据

但是,像其他指令一样,使用ADD / SUB指令也无法进行存储器到存储器的操作。ADD或SUB操作设置或清除溢出和进位标志。也就是说,我们可以直接因此而检查EFLAGS完成一些条件动作。

imul/mul和idiv/div

mul 指令用于无符号整数的乘法,处理时,通常被乘数在 EAX 中,而乘数可以是任何寄存器或内存位置。乘法的结果会存储在 EDX:EAX 中,EAX 保存低32位结果,EDX 保存高32位结果。这种设计是为了处理可能超出32位的乘法结果。例如,若 EAX 存储值 5,EBX 存储值 3,执行 mul ebx 后,EAX 将变为 15,EDX 将为 0,表示没有高位。

imul 指令也用于乘法,但支持有符号整数。与 mul 不同,imul 可以接受一个或两个操作数。如果只有一个操作数,imul 会将该操作数与 EAX 相乘,结果仍然存储在 EDX:EAX 中。如果有两个操作数,第一个操作数通常在 EAX 中,第二个是乘数。例如,如果 EAX 中存储 -5,执行 imul eax, 3 后,EAX 将变为 -15。使用两个操作数时,如果 EAX 是 -5,EBX 是 3,执行 imul ebx 后,EAX 也会变为 -15。

情况描述
当两个字节相乘时被乘数在AL寄存器中,而乘数在存储器或另一个寄存器中为一个字节。结果放到AX。乘积的高8位存储在AH中,低8位存储在AL中。
当两个单字值相乘时被乘数应位于AX寄存器中,并且乘数是内存或其他寄存器中的一个字。例如,对于MUL DX之类的指令,必须将乘数存储在DX中,将被乘数存储在AX中。结果乘积是一个双字,将需要两个寄存器。高阶(最左侧)部分存储在DX中,而低阶(最右侧)部分存储在AX中。
当两个双字值相乘时当两个双字值相乘时,被乘数应位于EAX中,并且该乘数是存储在存储器或另一个寄存器中的双字值。生成的乘积存储在EDX:EAX寄存器中,即,高32位存储在EDX寄存器中,低32位存储在EAX寄存器中。

在除法方面,div 用于无符号整数的除法。被除数是 EDX:EAX,因此在执行之前,通常需要将 EDX 清零,以避免意外的高位影响。除数可以是寄存器或内存,执行后,商将存储在 EAX 中,余数存储在 EDX 中。例如,如果 EAX 是 15,EBX 是 3,执行 div ebx 后,EAX 将变为 5,而 EDX 将为 0,表示没有余数。

idiv 用于有符号整数的除法,其操作方式与 div 类似,被除数依然是 EDX:EAX。同样,在执行之前,EDX 需要被清零,以确保结果正确。商存储在 EAX 中,余数存储在 EDX 中。如果 EAX 是 -15,而 EBX 是 3,执行 idiv ebx 后,EAX 将变为 -5,EDX 将为 0。idiv 可以处理负数,正确计算出符号。

情况描述
当除数为1个字节时假定被除数位于AX寄存器(16位)中。除法后,商进入AL寄存器,其余部分进入AH寄存器。
当除数为1个单字时假定分红为DX:AX寄存器中的32位长。高位16位在DX中,低位16位在AX中。除法后,16位的商进入AX寄存器,而16位的余数进入DX寄存器。
当除数是双字假定在EDX:EAX寄存器中分红为64位长。高阶32位在EDX中,低阶32位在EAX中。除法后,32位的商进入EAX寄存器,而32位的余数进入EDX寄存器。

实践:我们来写一个简单的一位数加法器

为了方便起见,我们来尝试写一个一位数的加法器试试看。

> ./result 
Input your first digit:> 1
Input your second digit:> 2
the result is:> 3
>

请写一个程序,完成上面的动作。下面是一些提示:

  1. 首先,你需要至多三个变量,两个存储输入,一个存储输出。笔者出于编程效率,选择三个变量!省事!

  2. 从键盘获取到的是ASCII字符,你需要做点转换!

  3. 输出的时候,记得也需要是ASCII字符,否则你会发现你的控制台啥也没有~

写好了?答案揭晓:

; --------------------------------------------------
;   Program written in 10.20 2024
;   Author:             Charlie chen
;   Functionality:      Make Add for digit! 
; --------------------------------------------------; help announce a typical string
%macro ANNOUNCE_STRING 2%1 db %2%1_LEN equ $ - %1
%endmacro; fast use of common value
%define MY_STDOUT       1
%define MY_SYS_WRITE    4
%define MY_STDIN        0
%define MY_SYS_READ     3; print string in a simple way
%macro PRINT_STRING 2mov edx, %2mov ecx, %1mov ebx, MY_STDOUTmov eax, MY_SYS_WRITEint 0x80
%endmacro%macro EASY_PRINT_STRING 1PRINT_STRING %1, %1_LEN 
%endmacro; exit program
%macro EXIT 0mov ebx, 0mov eax, 1int 0x80
%endmacro; transfer from ascii to value
%macro GAIN_NUMBER 1mov al, [%1]sub al, '0'mov [%1], al
%endmacro; read ascii from console
%macro READ_SINGLE_BYTE_FROM_CONSOLE 1mov edx, 2mov ecx, %1mov ebx, MY_STDINmov eax, MY_SYS_READint 0x80
%endmacro%macro PRINT_SLASH 0mov edx, 1mov ecx, SLASHmov ebx, MY_STDOUTmov eax, MY_SYS_WRITEint 0x80
%endmacrosection .dataANNOUNCE_STRING TELL_INPUT_NUM_1,    "Input your first digit:> "ANNOUNCE_STRING TELL_INPUT_NUM_2,    "Input your second digit:> "ANNOUNCE_STRING RESULT_STR,          "the result is:> "SLASH db 0xAsection .bssnum_1   resb 2  ; other byte for \nnum_2   resb 2  ; other byte for \nresult  resb 1section .textglobal _start
_start:EASY_PRINT_STRING               TELL_INPUT_NUM_1READ_SINGLE_BYTE_FROM_CONSOLE   num_1EASY_PRINT_STRING               TELL_INPUT_NUM_2READ_SINGLE_BYTE_FROM_CONSOLE   num_2GAIN_NUMBER                     num_1GAIN_NUMBER                     num_2mov eax, [num_1]add eax, [num_2]add eax, '0'mov [result], eaxEASY_PRINT_STRING               RESULT_STRPRINT_STRING                    result, 1PRINT_SLASHEXIT

NASM逻辑指令

处理器指令集提供指令AND,OR,XOR,TEST和NOT布尔逻辑,它们根据程序的需要测试,设置和清除位。

指令格式
ANDAND 操作数1,操作数2
OROR 操作数1,操作数2
XORXOR 操作数1,操作数2
TESTTEST 操作数1,操作数2
NOTNOT 操作数1

在所有情况下,第一个操作数都可以在寄存器或内存中。第二个操作数可以是寄存器/内存,也可以是立即数(常数)。但是,内存到内存操作是不可能的。这些指令比较或匹配操作数的位,并设置CF,OF,PF,SF和ZF标志。

AND 指令

AND 指令用于通过执行按位AND运算来支持逻辑表达式。如果两个操作数的匹配位均为1,则按位AND运算返回1,否则返回0。它可用于清除一个或多个位。例如,假设BL寄存器包含00111010。如果您需要将高阶位清除为零,则将其与0FH进行“与”运算。

AND     BL,   0FH   ; This sets BL to 0000 1010

让我们来看另一个例子。如果要检查给定数字是奇数还是偶数,一个简单的测试将是检查数字的最低有效位。如果为1,则数字为奇数,否则为偶数。

假设数字在AL寄存器中,我们可以写-

and   	al, 01H     ; ANDing with 0000 0001
jz		tell_is_even_number

OR 指令

OR 指令用于通过执行按位或运算来支持逻辑表达式。如果来自任何一个或两个操作数的匹配位为1,则按位OR运算符将返回1。如果两个位均为零,则返回0。

或运算可用于设置一个或多个位。例如,假设AL寄存器包含0011 1010,则需要设置四个低阶位,您可以将其与值0000 1111(即FH)进行或运算。

OR BL, 0FH                   ; This sets BL to  0011 1111

下面的示例演示OR指令。让我们将值5和3分别存储在AL和BL寄存器中,然后是指令,

OR AL, BL

XOR 指令

XOR 指令实现按位XOR运算。当且仅当来自操作数的位不同时,XOR运算将结果位设置为1。如果来自操作数的位相同(均为0或均为1),则将结果位清除为0。将操作数与自身进行XOR会将操作数更改为0。这用于清除寄存器。

XOR     EAX, EAX

TEST 指令

TEST 指令与AND运算的工作原理相同,但与AND指令不同的是,它不会更改第一个操作数。因此,如果我们需要检查寄存器中的数字是偶数还是奇数,我们也可以使用TEST指令执行此操作,而无需更改原始数字。

TEST    AL, 01H
JZ      EVEN_NUMBER

NOT 指令

NOT 指令实现按位NOT运算。NOT操作将操作数中的位取反。操作数可以在寄存器中,也可以在存储器中。

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

实践:测试一个数是技术还是偶数

现在,你需要测试一个4位数是技术还是偶数:

charliechen@Charliechen:~/demo/demo9$ ./result 
Input your first digit:> 1234
the result is that the number: 1234 is even

老样子,先写,我给一些提示:

  1. 这是超前的:你可以使用jmp跳转到一个标签,我来写一个简单的范例:

    ...
    section .dataANNOUNCE_STRING WORK_FLOW_STR, {"Workflow here!", 0xA}section .textglobal _start
    _RunFlow_here:EASY_PRINT_STRING WORK_FLOW_STRjmp _exit_start:jmp _RunFlow_here
    _exit:EXIT
    charliechen@Charliechen:~/demo/demo9$ ./result 
    Workflow here!

    可以看到我们可以使用jmp指令在标签之间跳转执行,是的,这就是goto的原型!

  2. 回到开头,你可以是用test或者是and,然后使用JZ或者是JNZ来完成跳转。

写好了?答案揭晓:

; --------------------------------------------------
;   Program written in 10.20 2024
;   Author:             Charlie chen
;   Functionality:      test if a number is even or odd
; --------------------------------------------------; help announce a typical string
%macro ANNOUNCE_STRING 2%1 db %2%1_LEN equ $ - %1
%endmacro; fast use of common value
%define MY_STDOUT       1
%define MY_SYS_WRITE    4
%define MY_STDIN        0
%define MY_SYS_READ     3; print string in a simple way
%macro PRINT_STRING 2mov edx, %2mov ecx, %1mov ebx, MY_STDOUTmov eax, MY_SYS_WRITEint 0x80
%endmacro%macro EASY_PRINT_STRING 1PRINT_STRING %1, %1_LEN 
%endmacro; exit program
%macro EXIT 0mov ebx, 0mov eax, 1int 0x80
%endmacro; read ascii from console
%macro READ_FROM_CONSOLE 2mov edx, %2mov ecx, %1mov ebx, MY_STDINmov eax, MY_SYS_READint 0x80
%endmacro%define NUM_LIMIT_MAX 5section .dataANNOUNCE_STRING TELL_INPUT_NUM,    "Input your first digit:> "ANNOUNCE_STRING RESULT_STRING,     "the result is that the number: "ANNOUNCE_STRING ODD_STRING,        {" is odd!", 0xA}ANNOUNCE_STRING EVEN_STRING,       {" is even", 0xA}section .bssnum   resb NUM_LIMIT_MAX  ; other byte for \nsection .textglobal _start_tell_is_even:EASY_PRINT_STRING EVEN_STRINGjmp _to_exit_tell_is_odd:EASY_PRINT_STRING ODD_STRINGjmp _to_exit_to_exit:EXIT_start:EASY_PRINT_STRING TELL_INPUT_NUMREAD_FROM_CONSOLE num, NUM_LIMIT_MAXmov eax, dword [num]sub eax, '0'test al, 0x1EASY_PRINT_STRING RESULT_STRINGPRINT_STRING num, 4jz _tell_is_oddjmp _tell_is_even

http://www.mrgr.cn/news/54725.html

相关文章:

  • 视频网站开发:Spring Boot框架的高效实现
  • 智融SW5106 无线充电发射端全集成 SOC
  • 曲线与平面曲线 | 正则曲线、弧长参数、切线方程曲率
  • 贪心day3
  • zotero文献管理学习
  • golang的net包
  • Axure中继器时间筛选
  • 将 centos7 的根分区由非逻辑卷转换成使用逻辑卷
  • halcon的intensity算子到底是Mean灰度均值最大表示清晰度最高,还是Deviation灰度偏差最大表示清晰度最高?
  • 深入了解 Flannel(2):vxlan模式下的跨主机pod通信
  • Vue框架基础知识
  • 电能表预付费系统-标准传输规范(STS)(14)
  • 【数据结构】【单调栈】视野总和
  • 动态规划之打家劫舍
  • Python 类
  • 【计网】从零开始理解TCP协议 --- 拥塞控制机制,延迟应答机制,捎带应答,面向字节流
  • 【OD】【E卷】【真题】【100分】最大利润贪心的商人(PythonJavaJavaScriptC++C)
  • Docker无法拉取镜像解决办法
  • 程序员数学:用Python学透线性代数和微积分 中文目录
  • #P3014. 数字游戏
  • STM32F1+HAL库+FreeTOTS学习18——任务通知
  • Comfyui如何快速选出图像的高光和阴影 _ layerstyle节点
  • 动态规划58道算法题
  • 【Modern C++】特性学习与补漏
  • 作业2-线性回归的Matlab代码实现
  • SQL入门