汇编基础
机器语言:我们计算机能够识别的数据是二进制代码,机器指令也是二进制的代码组成的指令。所以用机器语言编写的程序,是计算机唯一能够直接识别运行的程序,其他语言编写的程序都需要经过翻译才能执行。所以,机器语言程序被称为目标程序。
汇编语言:为克服机器语言的缺点,人们采用助记符来表示操作码,变量来代替操作数的存放地址,这就是汇编语言。
高级语言:这类语言更接近人类自然语言,表达的更为直观。
汇编语言的特点:
- 汇编语言和处理器密切相关。
- 汇编语言程序效率比高级语言高。
- 汇编语言程序比高级语言程序烦琐。
- 调试汇编语言比高级语言程序困难。
汇编语言适用的场合:
- 程序执行需要很小存储容量,或需要较快速度。
- 程序和硬件密切相关,程序直接控制硬件时。
- 需要提高大型软件性能时。
- 没有合适的高级语言时。
汇编中的数值数据是分为两种
有符号数和无符号数,其中无符号数最高位表示的是数值,有符号数最高位表示的符号,有符号数有不同的编码方式,其中最常用的补码。
- 原码。其中最高位表示符号(正0负1),其他位表示数值位,称为有符号数的原码表示法。
- 反码。正数的反码和反码相同。负数的反码符号位为1不变,但其他数值位全部取反。
- 补码。正数的补码和反码源码相同。负数的补码是反码+1。
- 符号扩展。在数据处理时,当我们将8位的二进制数扩展为16位二进制数。当扩展的数是无符号数时,则在最高位前补8个0,若扩展的数是有符号数时,那么就要进行符号位的扩展,符号扩展后,其结果仍然是该数字的补码。
- 数据的表示范围。n位二进制数能表示的无符号整数范围是0 <= I <= 2n-1,n位二进制数能表示的有符号整数范围是 -2(n-1) <= I <= 2(n-1)-1
- 十六进制表示。我们使用十六进制数时,后加字母H。我们使用二进制数时,后加字母B。
ASC2码
标准ASC2码使用7位二进制数编码,共有128个。
计算机存储器中基本单位为8位,ASC2码的最高位做为奇偶校验,通常为0。
ASC2码中前32个和最后一个编码是不可显示的控制字符,用于做某种操作。
BCD码
就是进行十进制和二进制数转换的。一般就是用8421BCD码。
程序的本质
在32位的操作系统下,我们的每个进程有一个连续的内存空间,程序本身和其中需要的数据保存在这里。该空间的每个字节都可用一个32位UINT定位,每个字节有唯一的空间位置,我们称之为地址空间。值得注意的是,这里的内存不是我们口头说的硬件“内存”。 我们可以去想象,我们是用32位的无符号整型来记录每个空间位置的,那么假如他们之间是全映射,也就是每个UINT对应且仅对应一个地址空间。那么地址空间总大小很容易算出,是2的32次方个字节,大约是4G。 然而不是每个地址我们都可以用应用程序去调用,操作系统本身也需要很大的地址来调用它本身的API和保存数据。所以,在Windows环境下,用户实际可以直接控制的地址空间仅有2-3G。其他部分为系统所使用。 在CPU中,有一些内部的存储单元,用于处理程序逻辑,我们称之为寄存器Regiter。我们最常接触的寄存器有10个,EAX,EBX,ECX,EDX,ESI,EDI,ESP,EBP,EIP,PSW。前9个是32位的,PSW是16位的。 当我们CPU有电时,它就会从地址空间中取出一个地址上的数据加载到CPU的EIP寄存器中,这时EIP递增到下一个字节的地址处,按照指令逻辑做相应的操作。 我们的一个指令是分为三部分:操作,参数,参数定位方式。 操作:在汇编语言中,我们用一些字母来表示要做的操作,我们称之为助记符,比如MOV就是英文move的缩写,表示就是赋值。 参数:是可以指定寄存器或内存的,但某些改变寄存器的操作在汇编语言中是隐式表达在助记符中的,比如RET,它就会修改ESP以及EIP的值,但是在语句中我们并没有写明参数。 参数定位方式:就是控制参数从哪儿读取的,从寄存器还是内存地址?或是从当前指令的数据中读取?在汇编语言中,我们称之为寻址方式。比如MOV EAX,1就是说从指令中读取出一个参数,将“1”这个数字,将它赋值到EAX。而MOV EAX,[EDX]就是将EDX做为一个地址来看待,我们将EDX这个“内存地址”中的数据赋给EAX中。这两种寻址方式是用[]来区分的。 该10个寄存器中EAX,EBX,ECX,EDX是四个通用寄存器,实际大部分工作中他们是可以换用的,仅在部分情况下,EAX可用于无符号乘法,EDX可用于保存乘法结果中的高位数据,ECX用于循环计数或者移位指令。 在我们使用EAX寄存器时,我们可以仅使用它们的一半,此时,AX代表其低16位,而AX中的低8位代号为AL,而其高8位代号是为AH。当然在另外三个通用寄存器中也有同样的特性。 ESI,EDI,EBP这三个寄存器主要是储存内存地址的。实际大部分情况下,他们可以与通用寄存器互换,但ESI多被用于连续内存操作的源地址指针,EDI用于目标地址指针,EBP多用于堆栈上的数据指针。 同样,我们可以对它们进行操作低16位,其代号分别为SI,DI,BP。但没有办法独立操作抵8位。 我们对这7个寄存器没必要详细拘泥它们的用法,仅仅将其看做代码快速操作数据的临时变量就可以了。 EIP/ESP是特殊寄存器。 其中EIP是存储程序逻辑的,我们JMP XXXXX,就相当于MOV EIP,XXXXX。 ESP是堆栈指针,和EBP不同之处在于,EBP是指向堆栈中的数据,我们也可以使用别的通用寄存器取代它,而ESP,许多操作堆栈数据的汇编指令会隐式的改变它的值。 ESP中存放着进程的堆栈信息,当我们每开启一个线程时,就会用掉一定的堆栈空间。当我们的ESP用尽只后,就会引发堆栈溢出。PUSH,CALL这样的指令都会将一些数据存放到ESP指向的地址中,并且减少ESP的值,当我们使用POP,RET这样的指令则会清除ESP中的数据,使其值增大。 PSW是其中唯一的一个16位寄存器,他是按位来保存一些CPU内部状态的。其中有6个条件标志位。 其中OF是溢出标志。 当计算溢出时,此位置为1,反之为0。 SF是符号标志。当计算结果是负数时,此位置为1,反之为0。 ZF是零标志。当计算结果是0时,此位置为1,反之为0。 CF是进位标志。当计算结果产生了进位,此位置为1,反之为0。 AF是辅助进位标志。当运算时第三位产生进位,此位置为1,反之为0。 PF为奇偶标志。当操作数中1的个数为偶数时,此位置为1,反之为0。 =============8086CPU和寄存器组分类:============ AH+AL=AX属于EAX———- BH+BL=BX属于EBX – CH+CL=CX属于ECX ——>数据寄存器——– DH+DL=DX属于EDX———— SP属于ESP,代表堆栈指针——– BP属于EBP,代表基址指针——–>指针寄存器————–>通用寄存器 SI属于ESI,代表源地址==========>变址寄存器——– DI属于EDI,代表目标地址======== IP 指令指针——-> 控制寄存器 FLAGS 标准寄存器—- CS代码段—- DS数据段 - SS堆栈段 ->段寄存器 ES附加段—- ————-通用寄存器————————- 其中AX是累加器,用于算术,逻辑运算,和外设备传送信息。 其中BX是基址寄存器,用于存放存储器地址。 其中CX是计数器,一般在循环或串操作指令中的隐含计数器。 其中DX是数据寄存器,用来存放双字数据的高16位。 ————–变址指针寄存器——————— 此类包括SI,DI,SP,BP这4个16位寄存器,主要用于存放某个存储单元的偏移地址。 其中SI是源变址寄存器,DI是目的变址寄存器,在字符串操作中,SI和DI都具有自动增量或减量的功能。 SP做为堆栈指针寄存器,用于存放当前堆栈中栈顶的偏移地址;BP做为基址指针寄存器,用于存放堆栈段中某一存储单元的偏移地址。 (此处和上面资料中的解释略有出入,根据分析,认为此处解释更为合理,容易理解。也可能是两者分析的CPU类型不同) ——————-段寄存器—————— 8086CPU中的4个16位段寄存器都是段寄存器,他们用来确定该段在内存中的起始位置。 代码段是存放程序中指令序列,CS存放的是代码段的首地址,IP指示的是代码段中指令的偏移地址。 IP总是保存着下一次将从主存中取出指令的偏移地址字节数,在目标程序运行时,IP的内容由微处理器硬件自动设置,程序不可改变IP,但是一些指令却可改变IP值。 ——————标志寄存器——————— 他是很重要的16位寄存器,包含9个标志位。主要用于保存一条指令后,CPU所处状态信息以及运算结果的特性。 1:条件标志 其中OF是溢出标志。 当计算溢出时,此位置为1,反之为0。 SF是符号标志。当计算结果是负数时,此位置为1,反之为0。 ZF是零标志。当计算结果是0时,此位置为1,反之为0。 CF是进位标志。当计算结果产生了进位,此位置为1,反之为0。 AF是辅助进位标志。当运算时第三位产生进位,此位置为1,反之为0。 PF为奇偶标志。当操作数中1的个数为偶数时,此位置为1,反之为0。 2:状态控制标志 方向标志DF 中断允许标志IF 追踪标志TF 8086CPU的地址线是20位,这么说来,其最大的可寻址空间应当为2的20次方,1M,但是8086的CPU都是16位的,那么这一MB空间如何使用16位寄存器来表达呢?根据需求我们可以把1M字节地址空间划分为一些逻辑段,每个逻辑段必须满足两个条件:1,它的起始位置必须是16的倍数,2:逻辑段的最大长度应该是64K。那么1M的字节地址空间最少是划分为16个逻辑段。逻辑段和逻辑段之间是可以相连的,也可以不连,还可以重叠。 ========8086CPU的指令系统基本指令=========== 它包括数据传送类指令,算术运算类指令,位操作类指令,串操作类指令,控制转移类指令,处理机控制类指令。 其中大部分双操作数指令具有相同语句格式和操作规定。 语句格式为:[标号:]操作符 OPD,OPS [;注释] 如:F6C8:0033 8CC0 MOV AX,ES ;重定位 .EXE 文件 其中算术运算和位操作指令的部分单操作数指令又有一套语句格式和操作规定。 语句格式为:[标号:]操作符 OPD[;注释] 如:F6C8:0032 C3 RET ;执行原 .COM 文件 ============寻址方式================================ 一:是寄存器寻址。 寄存器寻址方式的操作数在指令指明的寄存器中。 汇编格式为:R (其中R表示的寄存器名。) 功能为:操作数直接存放在寄存器R中 我们看个例子: MOV AX,1234H MOV BX,5678H ADD AX,BX 其中第一句的MOV是数据传送指令操作符,第三句的ADD是加法指令操作符。 第一,二句中AX,BX是目的操作数地址,是寄存器寻址方式,我们将1234,5678这两个16位数存放到对应的寄存器中。
第三句中AX是目标操作数地址,BX是源操作数地址。我们使用寄存器寻址将AX,BX的操作数进行相加后存放到AX中,所以最后AX中操作数是AX+BX=1234+5678=68ACH
二:寄存器间接寻址 寄存器间接寻址中,寄存器的内容为操作数的偏移地址EA,操作数在存储器中。 汇编格式为:[R] 功能是将操作数存储到存储器中,而寄存器R仅仅存放的是操作数的偏移地址EA。 我们看题: 寄存器和存储器中内容分别为(AX)=0,(BP)=0030H,(SS)=2000H,(20030H)=1234H。 执行指令:MOV AX,[DP]
执行后:(AX)=?(BP)=?(SS)=?(20030H)=?
三:变址寻址 变址寻址方法操作数的偏移地址EA为寄存器的内容加位移量,操作数在存储器中。 汇编格式为:X[R] (其中X是表示位移量,是8位或16位的二进制补码表示的符号数)
功能:操作数存放在存储器,寄存器R的内容加位移量X为操作数的偏移地址EA。
四:基址加变址寻址 基址加变址寻址方式中,操作数的偏移地址EA是指令中基址寄存器内容,变址寄存器内容,位移量X三项之和,操作数在存储器中。 汇编格式:X[BR+IR]
功能:操作数存放在存储器中,BR内容+IR的内容+位移量X做为操作数的偏移地址EA。
五:立即寻址 立即寻址方式中,指令操作码和操作数都存放在存储器代码段中。 汇编格式:n (n为立即操作数) 功能:操作数存放在存储器中,指令下一单元的内容则为立即操作数n。 例如:MOV AX,10 此时源操作数为立即寻址方式,立即数为10,存放在指令的下一单元。
执行后:10->AX,(AX)=000AH
六:直接寻址 汇编格式:1:含有变量的地址表达式,2:段寄存器名:[EA] 功能:指令下一字单元的内容是操作书的偏移地址EA 例如:(AX)=1212H,BUF是数据段定义的变量,其偏移地址是2000H,(DS)=3000H,(32000H)=4545H 执行指令:MOV AX,BUF
执行后:(32000H)->AX,(AX)=4545H
=====跨度问题============
若我们选择用BP作间址基址寄存器,变址寄存器或者基址寄存器,那么操作数就在堆栈段,操作数的物理地址PA由堆栈段寄存器SS的内容左移动4位与偏移地址EA相加后形成;否则,操作数在数据段,操作数的物理地址PA由数据段寄存器DS的内容左移4位与偏移EA相加形成。这些都是规定的默认状态。假如我们要否定不使用默认状态,到其他段寻找操作数时,则必须用跨度前缀来指明操作数的段寄存器名。 汇编格式是:段寄存器名:操作数地址 例如 MOV AX,DS:[BP] MOV CX,SS:[SI] 在此例子中,DS:,SS:都是跨段前缀,此时默认的状态是无效的,操作数的物理地址PA由段寄存器内容左移4位+偏移EA形成。所以上面两个指令中的源操作数物理地址分别是 PA1 = (DS)左移4位+[BP] PA2 = (SS)左移4位+[SI]
====================================
============数据传送类指令===========
======1=通用数据传送指令=============== 1:传送指令MOV 语句格式是:MOV OPD,OPS 功能是:将源操作参数传送到目的地址,源地址内容不变。即(OPS)->OPD 存储器和寄存器间的数据传送 MOV AX,BUF ;BUF是变量,源操作数为直接寻址 MOV BH,[DI] ;源操作数为寄存器间接寻址 MOV DI,ES:3[SI];源操作数为变址存址,使用跨段前缀 MOV BP,3[BX+SI] ;源操作数为基址加变址寻址 MOV BUFA,DL ;BUFA是一个字节变量 MOV [BP],AX ;使用SS段寄存器 MOV DS:[BP],DL ;使用跨段前缀 MOV BUF,DS ;BUF是个字变量 MOV ES,BUF 2:数据交换指令XCHG 语句格式: XCHG OPD,OPS 功能:将源地址和目标地址中的内容互换,即(OPD)->OPS,(OPS)->OPD 例如: MOV AX,5678H ;(AX)=5678H MOV BX,0FFFFH ;(BX)=0FFFFH XCHG AX,BX ;(AX)=0FFFFH,(BX)=5678H 3:查表转换指令XLA 语句格式:XLA OPS或XLA 功能:将BX为首址AL为偏移量的字节存储单元中的数据送AL寄存器,即([BX+AL])->AL ==============2=堆栈操作指令=========== 1:进栈指令 语句格式:PUSH OPS 功能:将寄存器,段寄存器或者存贮器中的一个字节数据压入堆栈,堆栈指针减2 2:出栈指令 语句格式:POP OPD 功能:将栈顶元素弹出送至某一个寄存器,段寄存器或者存储器,堆栈指针+2。 ===========3=标志寄存器传送指令======= 1:标志送AH指令 语句格式:LAHF 功能:将标志寄存器中的低8位送到AH寄存器中 2:AH送标志指令 语句格式:SAHF 功能:将AH寄存器中的抵8位送到标志寄存器中 3:标志寄存器进栈指令 语句格式:PUSHF 功能:将标志寄存器中的内容压入堆栈 4:标志寄存器出栈指令 语句格式:POPF 功能:将栈顶内容弹出送到标志寄存器中 ========4=地址传送指令=========== 1:传送偏移地址LEA 2:传送偏移地址以及数据段首址指令LDS 3:传送偏移地址以及附加数据段指令LES