




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、浮点寄存器栈的栈顶指针在状态寄存器的(OZ (OZ 。共 位合计 个数值,分别为 ,也就是 ,指示当前的栈顶位置在哪个浮点寄存器上。假如为 ,则浮点寄存器栈顶位于 ,68 。执行一条 ,2*,相当于 执行一条 6;9. 指令,状态寄存器的 (OZ (OZ 减 ,变成了 ,这时 :56 指针指向 ,68 。 再把值写入 9: 条件码() ) ): 的条件码作用相当于 中的+LRGMY 寄存器的条件码的作用。栈顶指针(:56):栈顶指针 是 浮点寄存器的索引值。共 个值,分别指示 ,68 ,68 ,在汇编语法层,9:( )表示栈顶浮点仿真库的实现与 ) 浮点库反汇编分析之六 ,6; 编程 ,6;
2、编程前面几章都是讲述计算原理,在 )6; 支持的基本整型的基础建立了几个类型模板,支持通用用的 (也即没有长度或精度限制的)整型计算、定点数计算、浮点数计算、基本函数计算,实际上是实现了个 ) 仿真库。这些内容虽然对理解计算原理非常重要,但 除了)-;可能在领域有点价值之外,其他的几个模板应用价值均不大。目前,支持 /+ 标准的浮点硬件已经普及。通用 )6; 均已内置了浮点单元,最大可支持扩展双精度类型,足以满足一般计算的需要。特殊环境 下的开发(例如系统)即使没有浮点硬件,编译器也可能提供基本的仿真库。毕竟,浮点运算已经是 ) ) 不可或缺的一部分。而且,在编译器中实现一 两种浮点类型(例如
3、单精度类型和双精度类型),技术难度和工作量并不大(本书的代码就是一个证明),,58:84 早在几十年前就是这么做的。因此,在实 际的开发工作中,重要的不是自己实现已经实现的东西,而且如何更好地控制浮点硬件,从而得到更稳定、更高效的代码。自然,正如 )6; 一样,每一个浮点硬件系统都是不一样的,且不提运算过程的处理细节,即使是编程 模型、指令集也会有很大的区别,因此讲述所有的浮点硬件是不现实的。不过,它们一般都遵循 /+ 标准,这意味着在逻辑层面,它们是大同小异的,因此讲述 所有的浮点硬件也是没有必要的。+2 的 系列的 )6; 是目前 6) 的主流,它内置的浮点处理单元 ,6;(早年是 数学协
4、处理器)因而也成了最常用的浮点硬件。本章就以 ,6; 为例讲述浮点硬件编程,而后面的几章将反汇编分析建立在 ,6; 之上的 ) 浮点库,从而得到计算编程系统的完整印象。 ,6; 简史早在 位的 年代, 就作为 的数学协处理器出现。当时的 是一个与 分离的的浮点指令,但浮点指令与其他 指令混,它使用的浮点指令是合在一起执行。后来,在 中, ,6; 出现了,取代了原先的协处理器,这是 ,6;在 / 体系中的首次出现。 ,6; 延续了协处理器的浮点指令集。 编程环境 ,6; 有的指令和配套的寄存器。它的指令是 浮点指令,配套的寄存器有: 个 位的数据寄存器、 个 位的控制寄存器、 个 位的状态寄存
5、器、 个 位的标志寄存器、 个最后指令地址寄存器、 个最后操作数寄存器、 个操作码寄存器。其中,最后 个寄存器用于最后一次操作的信息,主要 用于异常处理。编程主要是围绕数据寄存器堆栈、状态寄存器和控制寄存器进 行的,其他部分则多用于调试支持。 数据寄存器(*GZG 8KMOYZKX),6; 有 个 位 的寄存器( ),使用扩展双精度格式操作数。当内存中的数据载入数据寄存器时,如果数据格式不是扩展双精度格式,则在载入过程中进行格式转换。 个寄存器组成一个循环堆栈,栈顶保存于状态寄存器中,相当于堆 栈指针。每次压栈(,2* 指令载入数据),堆栈指针就减 ,在 之间循环。代码并不直接使用这个指针操作
6、这些寄存器,而是使用 9: d9: 表示。9: 指栈顶,即状态寄存器中栈顶指针指示的那个寄存器。假设 当前状态寄存器中的栈顶指针是 4(即4 的寄存器位顶),则 9: O 对应的寄存器是:4 O SUJ 例如初始化时状态寄存器是 ,,2* 指令装载了一个数据以后,指针减小 是 ,则 9: 即是寄存器 ,而 9: 对应的寄存器是: SUJ # 这些计算实际上是以 )6; 管理寄存器的方式这 个寄存器组成的堆栈,比较晦涩。式 不是从 )6; 角度理解,而是从指令角度(也就是编程角度)理解,直接将还有9: d9: 看成一个堆栈,9: 是栈顶,每次压栈,数据向上移动一 个寄存器。例如载入一个数据,这个
7、数据在 9: 中,再次载入一个数据,则当前数据在 9: 中,而上次载入的数据在 9: 中。这种理解方式相对 简易。 ,6; 寄存器是相对独立的,不受过程调用的影响。程、进程切换时,操作系统也保存这些寄存器,因此它们也不受线程、进程切换的影响。这意味着这些寄存 器是个存放数据和传送参数的好地方,例如 的数据寄存器与 ,6; 的数据寄存器名字不同,实际上却是通过别名机制共用数据寄存器。这意味着,33 指令和 ,6; 指令存在资源共享问题,不可同时使用。 状态寄存器(9Y 8KMOYZKX) ,6; 有一个 位寄存器显示当前的状态:空闲标志( 位)、条件判断标志( 位)、数据寄存器堆栈指针( 位)、
8、异常标志( 位)、全局错误标志( 位)、堆栈错误标志( 位)。一般在一个操作完成以后,通过检测状态寄存 器控制流程。在一般编程中,异常标志和条件标志是最重要的。其他部分仅在出现错误时才需要关注,详情清参阅文献A C( )指令状态寄存器是只读的,因此与它有关的指令只有一条(但有同步和不同步两个版本,参见 节),即 ,9:9=。这条指令将状态寄存器中的内容传送至 寄存器,位次序不变。由于状态寄存器共 位,因此正好填满 。通过检测 的对应位,即到状 态控制器的相关信息,例如前面检测条件位 ) 的代码:,9:9=!传送至 4*+ .!测试 ) # %043?4+: !9: 9: ( )堆栈指 针位 至
9、位 是堆栈指针(共 位),其值在 和 之间循环。与常规 指令使用寄存器模式不同,浮点指令使用堆栈模式(更确切地说,是寄存器模式和堆栈 模式的混合),即一般的浮点指令从栈顶取操作数,结果也顶,例如,*6 指令就是如此,它的操作数来自 9: 和 9: ,结果于 9: 。一般情形下,用户只需保证堆栈不出现错误(例如溢出),无需 特别在意它的指针。但在特殊情形下,移动堆栈指针可能有利于提高效率。例如正在进行一个复杂的计算,中间结果占据了多个数据寄存器::56 $假 设当前的堆栈指针位于 JGZG 处,而却需要计算 YOT JGZG ,那么如何实现呢?常规方法(不直接操作堆栈指针)代码:,9:6 JGZ
10、G ,9:6 JGZG ,9:6 JGZG ,9/4 JGZG ,2* JGZG ,2* JGZG ,2* JGZG 如果直接操作堆栈指针(使用 ,/4)9:6 指令),那么:,/4)9:6,/4)9:6,/4)9:6JGZG JGZG JGZG JGZG JGZG JGZG JGZG JGZG ,9/4,*+)9:6,*+)9:6,*+)9:6可以看出,在复杂计算中,常规方法的代价就是在数据寄存器和内存之间进行多次数据传送,降低了 执行效率,而直接操作堆栈指针可以避免这些。当然,这个例子是编造的,实际代码可能没有这么,而且操作堆栈指针相当(清空堆栈时很容易造成堆栈溢出)。除非迫不得已,一般不
11、建议这么做。( )异常位第 章已经提及 /+ 定义了 种异常,每种异常均对应一个位,共 个位。除此而外,还有一个弱规范数异常,当操作数中出现弱规范数时,这个异常位就会被设 置(但扩展双精度格式例外)。在这些硬件基础上,可以建立两种异常处理模 式:正常模式和安静模式。正常的异常处理是硬件触发异常,被内核(操作系 统代码)捕获,然后由内核传递至用户层,交由指定的异常代码处理。这种处理模式的特点是,当一切正常、没有异常触发时,异常处理代码不会被激活,如同不存 在一样,因此任务代码执行效率较高。但是一旦触发了异常,内核代码和用户代码均被激活,处理代码的执行效率较低,系统受到较大干扰。安静模式就是一切异
12、常,但在任务代码执行过程中,在一些关键处(例如结果出来时)进行异常 检测。如果检测到异常就进行异常处理,否则继续执行任务代码。这种模式即使在异常出现时也不会激活内核代码,对系统干扰较小,异常处理代码的效率也高。但 是,大量的检测代码即使在没有异常时也需要运行(否则怎么知道是否发生异常了呢),从而导致任务代码效率低下。两种模式各有优缺点,选择的关键在于异常被触发的频率。如果频率较低,那么正常模式有优势;反 之,安静模式有优势。幸运的是,在浮点运算中,这两种模式你都可以根据情形选用。=OTJUY 标准的异常处理模式就是正常模式,常见的ZX_ KIKVZ 块和ZX_ IGZIN块就是这种模式在 )
13、) 中的对应物。与此同时,) 的浮点数学库却使用安静模式处理可能出现的异 常,例如如果想处理 GYOT 可能出现的定义域错误(操作数学函数异常的一种情形),那么只需提供一个ESGZNKXX 即可。不过这种模式仅用于 ) 的数学函数库,对普通的浮点代码无效() 浮点库没有提供异常检测函数)。(4)条件位状态寄存器有4个条件位,虽然最常见的用处是给出逻辑比较的结果,但这只是它们在逻辑比较指令中 的作用,在别的指令中,它们还有其他作用,例如在 FXAM 指令中它们给出操作数的类型、在 FPREM 指令中它们返回商的最低3个位。考虑到这些,更确切的 名字应该是指示位。按返回结果方式的不同,x87 FP
14、U 有两类逻辑比较指令:一类指令直接将结果设置到EFLAGS 寄存器中,例如 FCOM 指令;一类则将结果设置在状态寄存器的条件位中,例如FCOMI 指令。两者在使用上也有差异,例如比较两个数的大小,使用 FCOMI 指令是:FLDQWORD PTRESIFLDQWORD PTRESI+8FCOMIST(0), ST(1);占用 EFLAGS 部分JBMYNEXT2需要将两个数都载入寄存器,但检测结果比较方便。而 FCOM 指令正好相反:FLDQWORD PTRESIFCOMQWORD PTRESI+8FSTSW;占用 AXANDEAX,100H;测试 C0=1?JNZMYNEXT2;ST0
15、ST1只需载入一个即可,但检测结果比较麻烦,而且会破环 AX。我 喜欢使用 FCOMI 指令(此时无需关心 C0,C1,C2,C3的意义),但在 VC6浮点库中没有见到 FCOMI 指令。这里有一个重要的原因,那就是 FCOMI 指令是 P6系列才引入 IA-32体系的,先前的 CPU 不支持。因此,如果需要考虑兼容性,FCOM 指令是唯一的选择。C0,C1,C2,C3的 意义参见附录 B 指令说明部分。另一个需要特别注意的细节是 ,)53/ 指令 的特性,它只设置 +,2-9 寄存器的 ,、), 和 6,,没有设置 9, 和 5,,因此 ,)53/ 指令的结果类似无符号整型的结果,这意味着紧
16、跟的分支指令应 该使用 0 0+ 0+ 0(+ 0(,而不要使用 0- 0-+ 0+ 02+ 02。否则,结果会出错。 控制寄存器()UTZXUR 8KMOYZKX)在浮点运算中,需要控制的一般就是计算精度、舍入模式和异常,因此控制寄存器非常简洁。唯一比猜测多出的是无穷控制位(/TLOTOZ_ )UTZXUR),这是 数学协处理器的遗迹(我始终没有弄明白, 数学协处理器怎么用这个位),在 / 体系中已没有用处。编写控制寄存器的操作代码需要非常一个域,例如 下列代码:,最常见的错误是在一个域时无意中改变了另IUTZXURY # +3E68+)/! 精度异常EEGYS ,2*)= IUTZXURY
17、 设置这段代码的目的是精度异常,但实际上它也同时清除了对其他异常的以及精度设置和舍入模式 设置。更为麻烦的是,改变精度设置和舍入模式导致的计算精度不够或错误是很难察觉的。避免这种错误的基本方法就是对控制寄存器操作提供标准函数,不允许其他代码操作控制寄存器。参考,更为通用的代码将在第 章 ) 浮点函数库中给出。本节给出的代码( )指令控制 寄存器可以读写,有两条指令,即载入指令 ,2*)= 和指令 ,9:)=,两者的操作数均是 位内存单元。代码示例如下:TYOMTKJ YNUXZ IUTZXURY!EEGYS ,2*)= IUTZXURYEEGYS ,9:)= IUTZXURYEEGYS ,2*
18、)= =58* 6:8 A+96 C( )异常位控制寄存器的低 位用于控制 种异常的作,可以定义一组常量:,设置相应位即可异常。为了方便操 JKLOTK +3E391 , JKLOTK +3E/42/*E56+8:/54 JKLOTK +3E*+45832E56+84* JKLOTK +3E+85E*/*+ JKLOTK +3E5E*5;(2+E 使用扩展双精度的设置代码: TYOMTKJ YNUXZ IUTZXURY!EEGYS ,9:)= IUTZXURY 获取控制寄存器内容IUTZXURY # d6)E391! 清零 否则可能设置错误 IUTZXURY b# 6)E+E*5;(2+E !
19、 位 精度EEGYS ,2*)= IUTZXURY 设置( )舍入模式 ,6; 均支持 节曾式的选择:的 种舍入模式,控制寄存器的位 和位 控制舍入模同样可以定 义常量: JKLOTK 8)E391 ) JKLOTK 8)E4+8+9: 舍入模式8)最近舍入 (向 舍入 (向 舍入 (向 舍入(截断舍入) (精度6)/+ 单精度 位 (保留 (/+ 双精度 位 (/+ 扩展双精度 位 ( JKLOTK 8)E*5=4 JKLOTK 8)E;6 JKLOTK 8)E:8;4):+使用最近舍入模式的设置代码: ) TYOMTKJ YNUXZ IUTZXURY!EEGYS ,9:)= IUTZXUR
20、Y 获 取控制寄存器内容IUTZXURY # d8)E391! 8) 位清零即是最近舍入EEGYS ,2*)= IUTZXURY 设置 其他寄存器除 了控制寄存器和状态寄存器, ,6; 还有几个寄存器,这些寄存器的主要作用在或调试而非一般编程的接口。于( ) 标志寄存器(:GM 8KMOYZKX)在操作数据寄存器堆栈时,如果寄 存器为空(即没有初始化过),那么一般浮点指令将出错;如果寄存器已经有数据,那么 ,2* 指令将出错。这么规定的目的是在出现误操作时通过触发异常(堆栈 溢出),避免计算错误,提高系统的稳定性。但是,仅仅根据数据寄存器的内容(即数据本身)无法判断该寄存器是否为空,于是就出现
21、了标志寄存器。标记是否为空只需 位,但是每个标记使用了 位,这样就有 种可能的标记:可见,虽然标记的最初目的是用 于标记是否为空,但兼顾了优化计算的目的。除了空之外的 个标记显然是按计算特性划分的。一般有限数需要进行计算, 可以快速处理,而特殊值不能参与计 算,只能特殊处理。( )调试寄存器标记说明 一般有限数 特殊值,例如格式、4G4、弱 规范数 空,即没有初始化过为 了进一步支持调试, ,6; 还实现了最后指令地址寄存器、最后操作数地址寄存器和操作码寄存器。浮点计算过程中,当异常被触发时,调试最需要知道的自然是哪儿触发了这个异常的以及在干什么时触 发的。最后指令地址寄存器给出异常指令的地址
22、,如果这条指令使用内存操作数,那么最后操作数地址寄存器给出操作数的内存地址。所给出的地址是 位全局地址,即选择子 偏移形式,例如 )9 +/6 或 *9 +9/。相关资料 可以参阅文献A C或 保护模式编程。这些寄存器最可能的用户是 调试器,一般用户不大可能编写指令级异常处理程序,即使是调试错误也是在调试器的支持下进行的,因此知道即可,无需关心细节。 ,6; 指令 指令简介绝大部分浮点指令都是同步指 令,即当指令返回时,功能已经完成,但有部分控制指令除了同步指令外,还有一个非同步指令的形式,例如状态寄存器指令 ,9:9= 就有一个非同步指令 ,49:9=。其实,同步指令实际上是两条指令,例如
23、,9:9= 指令实际 上是:,=/:,49:9=在 编程的其他领域,有时也有非同步代码(一般称异步代码,例如 =OTO 6/ 的=XOZK,ORK 接口就有异步调用方式),使用它们的主要目的是使的硬件设备并行,从而提高效率,但在 ,6; 编程中,使用非同步指令的主要目的是避免异常检查。也就是说,同步指令在功能完成即将返回之前会进行异常检查(即检查状态寄存器的异常位),如果发现有异常位被设置且控制寄存器对应的异常位没有设置,那么将触发异常,而非同步指令不做这种检查,因此即使有异常发生而且没有被,也不会触发异 常。 指令分类 ,6; 指令集大约有近 条指令,下面对这些指令作简要介绍,更进一步的资料
24、请参阅附录 浮点指令说明或文献A C。(1)数据传 输指令这些指令类似 x86指令集中的 MOV 指令,它们将数据在数据寄存器 之间、数据寄存器和内存之间进行传输。数据格式随指令不同,可能是浮点数,也可能是整数或 BCD 码。当源操作数和目的操作数的数据格式不一致时,还会进行 数据格式转换。某些指令(例如FCMOVcc 指令)有兼容性问题,P6系列以前的 CPU 不支持。(2)常量装载指令浮点运算中常用到一些常量,例如1.0和 ,x87 FPU 内置了这些数据方便使用。由于数据寄存器使用扩展双精度格式,因此这些内置数据的精度至少达到了64位的精度。(3)基本算术指令常说的四则运算及其他一些相关
25、指令。不过,不对称的双目运算符(例如减法和除法)引入了反向运算,这些指令完成同样的功能,但参数的位置正 好颠倒了。引入这些指令的目的显然是减少数据寄存器操作,方便使用。FADD/FADDP/FIADD加法FSUB/FSUBP/FISUB减法FSUBR/FSUBRP/FISUBR反向减法FMUL/FMULP/FIMUL乘法FDIV/FDIVP/FIDIV除法FDIVR/FDIVRP/FIDIVR反向除法FPREM/FPREM1计算部分余数,由于取整不同有两种。FABS计算绝对值FLD1载入+1.0FLDZ载入+0.0FLDPI载入FLDL2E载入 log2eFLDLN2载入 loge2,即 ln
26、2FLDL2T载入 log210FLDLG2载入 log102FLD/FILD/FBLD将内存或数据寄存器中的数据载入数据堆栈FST/FSTP/FIST/FISTP/FBSTP将 ST(0)中的数据保存到指定的内存或数据寄存器FXCH交换两个数据寄存器中的内容FCMOVcc满足条件 cc 则将指定的数据寄存器拷贝到 ST(0)( )比较指令比 较两个数的大小,即常说的逻辑运算。( )函 数指令计算一些常见的函数值,主要是三角函数、反三角函数和对数函 数。某些函数可能有操作数大小限制(例如 9OTK 函数),即使定义域是整个实数域,需要特别注意。( )控制指令这 些指令控制 ,6; 的各个方面,
27、例如读写控制寄存器、读状态寄存器等。,/4)9:6 ,*+)9:6增减堆栈指针,8+清空数据寄存器,/4/: ,4/4/:初始化 ,6;,)2+ ,4)2+清除异常标志,9:)= ,49:)=读控制寄存器,9/49OTK 函数,)59)UYOTK 函数,9/4)59一次完成 9OTK 函数和)UYOTK 函数的计算,6:4部分正切函数,6:4部分反正切函数, 3 计算 ,?2 计算 _RUM ,?2 6 计算 _RUM 66比较大小,结果在 ) 、) 和 ) 中66比较大小,结果在 ) 、) 和 ) 中6整数比较大小,结果在 ) 、) 和 ) 中/6比较大小,结果在+,2-9 中/6比较大小,
28、结果在+,2-9 中,:9:与 比较,结果在 ) 、) 和 ) 中,3判别数据类型,结果在 ) 、) 和 ) 中,).9改变符号,即计算相反数,84*取整运算,9)2+将操作数放大 的幂次方,例如_ T,978:平方根运算,:8):将浮点数分解为指数和尾数 指令使用( ) 设备初始化初始化硬件只有一条指令 ,/4/:,执行 ,/4/: 指令之 后, ,6; 硬件复位。控制寄存器值是 ,,即所有异常被、最近舍入、 位运算精度。状态寄存器清零,即没有异常、堆栈指针是 。标志寄存器是 ,,即 个数据寄存器中全空,没有有效数据,但实际上, 个数据寄存器的内容没有改变。,/4/: 指令在复位硬件前会检查
29、状态寄存器中,如果有异常发生且控制寄存器没有该异常则触发该异常,,4/4/:的异常状 态指令完成与 ,/4/: 一样的初始化功能,但不进行异常检测。( )系统 设置,/4/: 指令实际上也作了缺省设置,不过,它的设置可能不符合你的 需要。 ,6;的设置一般有三个方面:异常处理、计算精度和舍入模式,实际上就是控制寄存器的设置。具体操作参见 节和 节。状态寄存器和标志寄存器是只读的,一般不会改写,但可以通过特殊指令改写(,9+和 ,89:58 指令)。( )计算计算 过程中需要特别注意的是浮点堆栈。由于浮点堆栈不用于程序控制,出了问题以,因此相当隐秘。在 ) ) 后不会立即来,即使来也不一定会导致
30、程序,2*)=写控制寄存器,9:+4 ,49:+4保存 ,6; 执行环境,不包括数据寄存器,2*+4载入 ,6; 执行环境,不包括数据寄存器,9+ ,49+保存 ,6; 信息,包括数据寄存器,89:58载入 ,6; 信息,包括数据寄存器,9:9= ,49:9=读状态寄存器,=/:等待,456空操作等高级语言编程过程中,编译器处理了这个问题,但在汇编语言中需要自己处理这个问题。在代码编写过程中,要清楚自己使用的数据寄存器内容及个数,一旦不需要就及时清空。数据寄存器只 有 个,因此除非做好了详细计划,一般不要使用它们长期数据。减少寄存器使用个数的关键在于挑选合适的指令并且安排好指令序列,尽量减少中
31、间结果的个数。例如实现 G H I 的代码可以是 ,2* G,2* H,2J I,*6 9: 9: ,*6 9: 9: 最多使用了 个数据寄存器,换个形式:,2* G,* H,* I只使用了 个数据寄存器。( )设备信息的现代操作系统多是多用户系统,有多任务(进程或线程)调度功能,用户任务经常被切换。假设任务 正在进行浮点运算, ,6; 正在执行一个指令序列,此时发生了线程切换,线程 ( 取代线程 获得了对 ,6; 的控制,线程 ( 自然也可能使用 ,6;,那么显然线程 的中间结果(正在数据寄存器堆栈中)和设置将被破坏。当线程 再度获得对 ,6; 的控制时,此时的 ,6; 已是线程 ( 使用时
32、的状态,线程 的计算将出现错误。同样,线程 ( 也是一团糟。这个问题实际上是个简单的共享信息问题,解决之道就是在任务切换时进行 信息的保存和恢复,从而使各任务的信息互不干扰。 ,6; 为了支持这一功能,提供了两条指令,即 ,9+ 指令和 ,89:58 指令。当任务被挂起时,操作系统任务调度模块使用 ,9+ 指令将 ,6; 保存于任务数据块中。当任务被恢复时,使用 ,89:58 指令将 ,9+ 指令保存的数据载入 ,6;。如此,计算将不受任务切换的影响。如果你想自己处理浮点异常,那么就需要熟悉 ,9+ 指令保存的信息格式,因为在=OTJUY 中,当异常发生时,内核正是通过 ,9+ 指令将 ,6;
33、 的信息保存起来传递给异常处理代码。当然,在这个层次处理异常通常是操作系统和浮点库的任务。除非你想创建一个浮点库并且该浮点库完善到自己进行异 常处理,否则你不需要熟悉 ,9+ 指令和,89:58 指令。 代码示例为测试方便,本节代码使用了 ) 的汇编,但使用了 TGQKJ 函数修饰符,尽量汇编的特征。由于 ) )8: 的初始化代码在程序启动时就已经初始化了 避免,6; 以支持 JUHRK 类型,而本节代码也使精度类型,因此无需再初始化 ,6;,相关的设置也被忽略。实际上,本节只需注意 ,6; 的指令和寄存器的使用即可。数组求和 ,RUGZ9S 演示基本 的浮点操作,冒泡排序 ,RUGZ9UXZ
34、 演示浮点比较操作,9OTK 函数表 ,RUGZ9OT 演示堆栈管理。所有函数均使用 ) ) 缺省的 EEIJKIR 调用约定,因此返回时无需堆栈。为了尽量减少代码,所 有函数都没有进行输入值检测等工作,实际代码应该做这项工作以提高稳定性。 数组求和数组求和是非常简单的常用代 码:JUHRK ,RUGZ9S JUHRK VJ IUTZ aJUHRK YS # !LUX O # ! OIUTZ! O YS # VJAOC!XKZXT YS!c将上述逻辑转 换为汇编代码,除了常规的循环控制之外,只使用了 ,2* 和 ,* 两条浮点指令以及 9: 寄存器。由于 ) 将 9: 作为浮点返回值,因此浮点
35、堆很简单,几乎无需注意。代码如下:栈指针问题也EEJKIRYVKI TGQKJ JUHRK ,RUGZ9S JUHRK VJ IUTZ aEEGYSa6;9.+9/,2* 9: 清 零35+9/ A+96 C VJ35 A+96 C IUTZ3?2556 0+) 3?+/:,*7=58* 6:8 A+9/C 相加*+)+) 计数器*+9/ 地址递增0363?25563?+/: 656+9/8+: 结 果在 9: 中返回cc 冒泡排序这个程序将给定的数组按从小到大的次序排列,逻辑要复杂一些,) 代码是:UOJ ,RUGZ9UXZ JUHRK VJ IUTZ aH)UTZOTK # O Q!JUHRK ZKSV!Q # IUTZ!NORK H)UTZOTK aQ !H)UTZOTK # !LUX O# ! OQ! O aOL VJAOC $ VJAO C aZKSV # VJAOC!VJAOC #
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论