2021-02-26

一、汇编语言基本组成部分

1.1 第一个汇编程序

汇编语言编程可能以晦涩难懂而著称,但我们喜欢用另一种方式来看待它,它是一种提供几乎全部信息的语言。你可以看到正在发生的一切,甚至在CPU的寄存器和标志!然而,有了这个强大的功能,您就有责任管理数据表示细节和指令格式。你的工作非常细致。要了解这是如何工作的,让我们看一个简单的汇编语言程序,它将两个数字相加并将结果保存在寄存器中。我们称之为AddTwo程序:

main PROC
	mov eax,5
	add eax,6

	INVOKE ExitProcess,0
main ENDP

上面的代码省略了很多部分,因此它无法运行,但是通过上面这种简略的形式,我们可以开始介绍一个汇编程序的基本组成部分:第1行开始主程序,即程序的入口点。第2行将整数5放入eax寄存器。第3行将EAX中的值加上6,得到一个新值11。第5行调用一个名为ExitProcess的Windows服务(也称为函数),该函数停止程序并将控制权返回给操作系统。第6行是主程序的结束标记

添加变量:

接下来我们把加法的结果保存在一个名为sum的变量中,让程序变得更有趣一些。为此,我们将添加几个标记或声明,用于标识程序的代码和数据区域:

.data
sum DWORD 0

.code
main PROC
	mov eax,5
	add eax,6
	mov sum,eax

	INVOKE ExitProcess,0
main ENDP

其中sum变量在第2行声明,我们使用DWORD关键字将其大小指定为32位。此外,我们提到的那些由.code和.data指令标记的代码和数据区域称为段。所以你有代码段和数据段。稍后,我们将看到名为stack的第三个段。

1.2 整数常量

整数常量(integer literal或integer constant)由可选的前导符号、一个或多个数字以及表示数字基数的可选基数字符组成:

[ { + | -} ] digits [ radix ]

注:其中的[]内的内容代表是可选的,而{}内的内容代表是从中选择一个,每一项都用|进行分隔。

例如,26是一个有效的整数常量。它没有基数,所以我们假设它是十进制格式。如果我们希望它是十六进制,我们就必须把它写成26h,同样地,数字1101会被认为是一个十进制值,直到我们在末尾加上一个b使它变成1101b(二进制)。以下是可能的基数值:
在这里插入图片描述
下面是一些整数常量的例子:

26			;十进制
26d			;十进制
1101001b	;二进制
42q			;八进制
42o			;八进制
iAh			;十六进制
0A3h		;十六进制

注意,以字母开头的十六进制表示必须有一个前置0,这样做的目的是避免编译器将数字解释为一个标识符。

1.3 整数表达式

常量整数表达式是包含整数常量和算术运算符的数学表达式。每个表达式的计算结果都是一个整数,可以用32位(0到ffffffffffh)进行存储。表3-1列出了算术运算符的优先顺序,从最高(1)到最低(4)。关于常量整数表达式,需要了解的重要一点是,它们只能在汇编时求值。从现在起,我们将把它们称为整数表达式。

二、程序例子:加法和减法

2.1 AddTwo程序

我们通过AddTwo程序来开始介绍:

.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.code
main PROC
	mov eax,5
	add eax,6

	INVOKE ExitProcess,0
main ENDP
END main

第四行包括了.386标号,这个表示表示一个32位的程序可以访问32位的寄存器和地址。第五行指示了程序的内存模型(平面模型),并且指明了子程序的调用规范是stdcall。第六行设置了运行时栈的大小为4096字节。第七行声明了ExitProcess函数的原型,这是一个标准的Windows服务。原型由函数名、PROTO关键字、逗号和输入参数列表组成。ExitProcess的输入参数名为dwExitCode。我们可以将它看作传递回窗口操作系统的返回值。返回值为零通常意味着我们的程序成功了。任何其他整数值通常表示错误代码。我们可以将汇编程序视为操作系统调用的子例程或进程。当您的程序准备完成时,它调用ExitProcess并返回一个整数,告诉操作系统您的程序运行正常。

接下来回到AddTwo程序,第16行使用end指令来标记程序的最后一行,并标识程序入口点(main)。main在第10行声明,它就是程序开始执行的地址。

3.2 汇编标号的复习

.model标号告诉编译器使用哪种内存模型:

.model flat,stdcall

在32位程序中,我们总是使用平面内存模型,这与处理器的保护模式有关。stdcall关键字告诉汇编程序在调用过程时如何管理运行时堆栈。这是一个复杂的问题,我们将在第8章讨论。

接下来,.stack标号告诉汇编程序要为程序的运行时堆栈保留多少字节的内存:

.stack 4096

值4096恰好对应于处理器系统中用于管理内存的内存页的大小。所有程序在调用子程序时都使用堆栈,首先保存传递的参数,其次保存调用函数的代码的地址。当函数调用完成时,CPU使用这个地址返回函数被调用的位置。此外,运行时堆栈可以保存局部变量,即在函数中声明的变量。

.CODE标号标记程序代码区域的开始,该区域包含可执行指令。通常在.CODE后面的下一行是程序入口点的声明,按照惯例,它通常是一个名为main的过程。程序的入口点是程序将执行的第一条指令的位置。我们用以下几行文字来传达这一信息:

.code 
main PROC

ENDP标号指明了一个例程的结束。例如main例程的结束可以这样进行表示:

main ENDP

最后,END标号指明了整个程序的结束,并且将指出程序开始执行的点:

END main

如果我们在END标号后的行添加任何指令,都会被编译器省略。所以我们可以在END标号后写很多辅助我们的东西,例如一些注释,一些我们代码的复制等。它们不会对我们的程序产生任何影响。

2.2 程序模板

汇编语言程序结构简单,变化小。当我们创建一个新的程序时,我们首先会启动一个壳程序并准备好所有的基本元素。我们可以通过填写缺少的部分并用新名称保存文件来避免重复键入。以下程序(template.asm)就是我们的程序模板。其中已经插入了注释,标记了应该添加我们自己的代码的点。关键字的大小写是可选的:

;Program template(Template.asm)

.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD

.data
	;在这里进行变量的声明

.code
	;在这里填写程序代码

	INVOKE ExitProcess,0
main ENDP
END main

三、汇编、链接并运行程序

一个利用汇编语言写成的程序是不能直接被机器执行的。这个程序必须被汇编为可执行文件。汇编器实际上和编译器非常类似(编译器的作用就是将C++或Java程序翻译成可执行文件)。

汇编器生成一个包含机器语言的文件,称为目标文件。这个文件无法直接执行。目标文件必须通过链接器才能转化为可执行文件。此时的可执行文件可以被机器执行。

3.1 生成可执行文件的过程

整个过程包含编写源程序、汇编、链接并最终进行执行。整个过程如图3.7所示:
在这里插入图片描述
下面是对每一步的详细描述:

  1. 在第一步,程序员编写源程序,这个程序在内存中以ASCⅡ码的形式进行存储,称为源文件
  2. 第二步时,汇编器读入源文件,产生目标文件。目标文件是用机器语言表示的源文件。汇编器可以选择性的生成一个列表文件(listing file)。在这一步如果发生了错误,那么需要返回到第一步修复源程序。
  3. 在第三步,链接器读取目标文件并检查程序是否包含对其他库中的过程的调用。此时链接器会从库中复制所需的过程,将它们与对象文件组合,并生成可执行文件。
  4. 操作系统加载程序(loader)将可执行文件读入内存,并将CPU分支到程序的起始地址,然后程序开始执行。

3.2 列表文件

列表文件包含程序源代码的拷贝、行号、每条指令的数字地址、每条指令的机器代码字节(十六进制)和符号表。符号表包含所有程序标识符、段和相关信息的名称。我们有时使用列表文件来获取有关程序的详细信息。图3-8显示了AddTwo程序的部分列表文件:
在这里插入图片描述
可以看到,其中第1-7行不包含可执行代码,因此它们直接从源文件复制而不做任何更改。第9行显示代码段的开头位于地址00000000(在32位程序中,地址显示为8位十六进制数字)。这个地址相对于程序内存占用的开始,但是当程序加载到内存中时,它将被转换成一个绝对内存地址。当发生这种情况时,程序可能从00040000h这样的地址开始。

第10行和第11行也显示相同的起始地址00000000,因为第一个可执行语句是第11行的MOV指令。注意第11行,地址和源代码之间出现了几个十六进制字节。这些字节(B8 00000005)表示机器代码指令(B8)和由该指令分配给EAX的恒定32位值(00000005):

11:  00000000  B8 00000005 mov eax,5

值B8也被称为操作码(operation code或opcode),因为它表示将32位整数移到eax寄存器中的特定机器指令。

第12行同样包含了一个可执行执行,这个指令的偏移量是00000005,偏移量代表距离程序开始的距离。

第14行包括了invoke标号。注意15行和16行,这两行都被插入进了我们的代码中。这是因为INVOKE标号会触发编译器生成15行和16行的PUSH和CALL语句。

上面的列表文件中,我们可以看到机器指令都是以整数序列的形式被加载进入内存的,例如如下的十六进制形式:B8 0000000583 C0 06

在这里插入图片描述

2021-03-26 20:54:33,596 - Model - INFO - Epoch 1 (1/200): 2021-03-26 20:57:40,380 - Model - INFO - Train Instance Accuracy: 0.571037 2021-03-26 20:58:16,623 - Model - INFO - Test Instance Accuracy: 0.718528, Class Accuracy: 0.627357 2021-03-26 20:58:16,623 - Model - INFO - Best Instance Accuracy: 0.718528, Class Accuracy: 0.627357 2021-03-26 20:58:16,623 - Model - INFO - Save model... 2021-03-26 20:58:16,623 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 2021-03-26 20:58:16,698 - Model - INFO - Epoch 2 (2/200): 2021-03-26 21:01:26,685 - Model - INFO - Train Instance Accuracy: 0.727947 2021-03-26 21:02:03,642 - Model - INFO - Test Instance Accuracy: 0.790858, Class Accuracy: 0.702316 2021-03-26 21:02:03,642 - Model - INFO - Best Instance Accuracy: 0.790858, Class Accuracy: 0.702316 2021-03-26 21:02:03,642 - Model - INFO - Save model... 2021-03-26 21:02:03,643 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 2021-03-26 21:02:03,746 - Model - INFO - Epoch 3 (3/200): 2021-03-26 21:05:15,349 - Model - INFO - Train Instance Accuracy: 0.781606 2021-03-26 21:05:51,538 - Model - INFO - Test Instance Accuracy: 0.803641, Class Accuracy: 0.738575 2021-03-26 21:05:51,538 - Model - INFO - Best Instance Accuracy: 0.803641, Class Accuracy: 0.738575 2021-03-26 21:05:51,539 - Model - INFO - Save model... 2021-03-26 21:05:51,539 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 我有类似于这样的一段txt文件,请你帮我写一段代码来可视化这些训练结果
02-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值