汇编语言学习笔记(全)

汇编语言分位数和标准,位数是根据cpu寄存器的位数发展而变化,而标准主要有两类:

  • AT&T 标准, 主要用于UNIX和 类UNIX系统
  • Intel 标准
    本文是基于16位因特尔标准

汇编语言基础

三种基本的汇编语言成员:

  • 指令:

    [name:] mnemonic [[dest][,src]][;comment]
    
  • 伪指令

    [name] directive [[operand][,operand,...]][;comment]
    
  • macros 宏

命名规则和基本的c/c++命名规则差不多,主要关注以下可以使用哪些符号

  • ? _ @ $ 可以使用
  • 长度不要超过31个字符。 第一个字符不能是数字。

数据类型:

类型 简写 大小
BYTE DB 1B
WORD DW 2B
DWORD DD 4B
FWORD DF 6B
QWORD DQ 8B
TBYTE DT 10B
REAL4 DD 4B
REAL8 DQ 8B
REAL10 DT 10B

汇编语言编译过程

src.asm ---(汇编器)---- c.obj ---(连接器)--- xx.exe

伪指令

变量声明

label WORD [值,‘?’]

算了,还是直接举例子把:

a  BYTE  'A'
b  WORD  1111H
C  BYTE  128
D  BYTE  1,2,3,4,5 ; 数组
E  BYTE  "i LOVE U",'$'  ;字符串结尾必须有'$'
F  BYTE  'I LOVE "U..zcdsadfasdft" '

DUP

a BYTE 30 DUP(5)  ;重复 30个5   

OFFSET 和 SEG

这是两个比较常用的,

MOV AX, OFFSET VAR1
MOV BX, SEG  VAR1

一个是取偏移地址,一个是取段地址。

算数运算

+,-,*,/ 。。。 比较弱的功能,好像只支持 常数之间的运算和变量之间的地址运算,如:

MOV DX, BLOCK +(6-1)*2
MOV CX, (VAR1-VAR2)/2

PTR

常用,用于类型转换

MOV AL, BYTE PTR var
MOV BYTE PTR [BX],10

SHORT

后面跳转会用到,不常用

EQU 和 =

都是复制,只不过EQU 类似于 c系语言的宏定义,不可改变, =类似于=,可以改变。

ORG

常用,用于将当前程序指针指向某一个位置,与$搭配使用。

ORG expression
ORG $ + expression ; $ 代表当前地址,
ORG $+1 ; 意味着跳转到下一行, 常用于 对齐存储方式。。。。。

汇编代码框架

.8086
.model small
.stack
.data
;你的变量声明
.code
main:
;...
end main
.Other 段

汇编指令

转移指令

MOV 指令

MOV DST, SRC

将数据从 SRC 拷贝到 DST。MOV的使用有以下限制:

  • SRC 和 DST 不能都在主存或者都是段寄存器。
  • 不能将立即数移动到段寄存器(可以用通用寄存器间接来)
  • 不能对 FLAG 寄存器做此操作。
  • 不能将任何数据移动到CS和IP寄存器。
  • 不能再不同位长的寄存器之间移动。

PUSH 和 POP

PUSH SRC

将数据压入栈,操作步骤一般为:

  • SP上移,即 SP ← SP -2
  • ((SP)+1,(SP) ) ← (SRC) 这里是小端对齐, 高位存在高地址。

另外,不要忘记, 可以将 PUSH 的对象可以为: 立即数,内存数,寄存器数(包括FLAG寄存器)都可以。

POP DST

将栈顶元素弹出到DST。操作顺序:

  • DST ← ((SP+1),(SP))
  • SP ← (SP) + 2

需要注意的是,DST不可以是CS寄存器。

加载地址指令

加载有效地址

LEA reg16/32, mem8/16/32

LEA: load effective address 加载有效地址到 寄存器。 有效地址就是偏移地址。例如:

if (DS) = 2000H, (BX) = 1234H. 则 LEA DI, [BX] 的执行结果为:

DI ← 1234H

if Symbol variable BUFFER is in 2000H:1234H. 则 LEA DI, BUFFER 的执行结果为:

DI ← 1234H

加载段地址 + 有效地址

LDS reg, mem
LES reg, mem
LFS reg, mem
LGS reg, mem
LSS reg, mem

这是 LEA 的 升级版本,但是, 千万要注意的是, 这些指令不会做像 LEA 一样的翻译的效果,而是直接将 mem内的数据载入 段寄存器和reg。例如:

if (DS) = 2000H, (BX) = 1000H。 内存中 (21000H,21001H,21002H,21003H)= (45H,D6H,00H,50H)

那么, LDS DI,[BX] 的效果为:

DI ← D645H

DS ← 5000H

交换指令

XCHG  A , B  

交换两者内部的值,值得注意的是:

  • 两者长度要一致。
  • 两者不能同时是段寄存器或者同时为内存数。

LAHF 和 SAHF

LAHF

Transfers Flag寄存器最右面的8位到AH寄存器。

SAHF

Transfers AH寄存器到Flag寄存器的最右面8位。

查表指令

XLAT 

将AL 寄存器的值转换成 一个表里面的值。

表的偏移地址必须事先存在 BX 寄存器中。并且表元素的类型是byte。例如:

MOV BX, OFFSET TABLE

MOV AL, 4

XLAT

执行结果: AL ← 34H 34H 是表中第四个元素,表从0开始计数。

I/O 指令

IN AL/AX , I/O DEV
OUT I/O DEV, AL/AX

与I/O 设备的交互。 IO设备的寻址有两种方式存在:

  • 固定端口寻址。 有的IO端口有固定的8bits的 端口地址。例如 IN AL, 52H
  • 变动端口寻址。 端口地址存在DX。 例如 OUT DX, AL

算数指令

加法指令

ADD DST, SRC

DST ← (DST) + (SRC) 需要注意的是:

  • 两个操作数位数要相同。
  • 可以两个寄存器,也可以是寄存器和立即数,寄存器和内存数,内存数和立即数。 但不能是两个内存数。
  • 更值得注意的是: 它的执行会影响:CF, SF, OF, PF ,ZF 和 AF。
ADC DST, SRC

DST ← (DST) + (SRC) + Carry。 考虑进位的加法。 同样会影响FLAG寄存器。

INC DST

DST ← (DST) + 1

需要注意:

  • 也可以是内存数。
  • 会对FLAG产生影响,但是! 不会对CF产生影响!!!!!!!!

减法指令

SUB DST, SRC

DST ← (DST) - (SRC) 需要注意的和 ADD 相同。

SBB DST, SRC

DST ← (DST) - (SRC) - Carry。

DEC  DST

自减。 不会对CF产生影响。

NEG DST

DST ← 0 - (DST) 效果, 取反并加一。 对于负数来说, 就是求补。

CMP DST, SRC

通过 (DST) - (SRC) 来比较 两个操作数。 但是, DST 不会被改变!!!!!!!!! 通过对FLAG的影响来得到结果。

无符号数结果的判断:

  • ZF = 1. 相等。
  • CF = 0. DST 大于等于 SRC。
  • CF = 1. DST 小于 SRC。

有符号数结果的判断:

  • ZF = 1. 相等。
  • SF = OF, DST 大于等于 SRC。
  • SF != OF, DST < SRC。

乘除法指令

MUL SRC     ; 无符号数
IMUL SRC    ; 有符号数
  • SRC 可以是 寄存器数也可以是内存数。 被乘数存在 AX/AL寄存器当中。
  • 对于8位数。 AX ← AL * SRC
  • 对于16位数。DX : AX ← AX * SRC

需要注意的是:

  • 结果的位数总是操作数的两倍
  • 这个指令对 CF 和 OF 有影响。
    • CF = OF = 1. 表明 结果的 高一半不是0
    • CF = OF = 0. 表明 结果的 高一半 是 0
  • 不会改变其他寄存器。
IMUL REG, SRC
IMUL REG, SRC, IMM

REG ← (REG) * (SRC) - (IMM) 注意, 在这里, 它们的尺寸是相同的。 16/32

DIV SRC
IDIV SRC

同MUL。被除数在AX里面。需要注意:

  • 不要除0
  • 商太大放不下。
  • 会改变FLAG的值
  • 商的位数是被除数位数的一半。 商存在低一半。 余数存在高一半。

还有就是不能乘除立即数。可以加减立即数。

符号扩展指令

CBW             ; AL to AX
CWD            ; AX to DX:AX
CWDE           ; AX to EAX
CDQ            ; EAX to EDX : EAX

BCD 码的调节

  • 加法结束后,1010B ~ 1111B 对于BCD码来说是没有意义的。因此需要调节。 即进位。
AAA 

例子:

MOV AL, 9H
MOV BL, 4H
ADD AL, BL    ; overflow
AAA           ; adjust to AH

所以最后, 整个结果扩展到 AH:AL。 并且改变 CF 和 AF 【= 1】(adjust flag 第三位有无进位)的值。

  • 减法结束后,的调节。
AAS

同上, AF = CF = 1. 且 将正确结果整合到了AL。

  • BCD 乘法结束后的调节
AAM         ; 和AAA 差不多
  • 除法结束后调节
AAD         ; 和 AAM 差不多

以上都是unpacked BCD 码的调节。 packed BCD 码如有需要,自查资料。

逻辑指令

与或非

AND  DST,SRC
OR   DST,SRC
XOR  DST,SRC        ; 异或

需要注意如下:

  • 结果存在DST当中
  • DST和SRC 都可以是MEM, 但不可同时是MEM。

它们的使用技巧:

  • AND 转换 ASCII 到 digit integer
  • OR 转换 digit integer 到 ASCII
  • XOR 某一位取反 例如 : XOR CL, 00100000B . 因为0/1 与0异或不会改变。 0/1 异或会取反。

TEST 指令

和 AND 的效果一样,但是不会改变 DST 的值。

注意:

  • AND, OR , XOR, TEST 会改变 FLAG 的值。
    • CF = 0
    • OF = 0
    • AF undefined
    • PF, SF, ZF 和结果有关。

NOT 指令

求反指令

MOV AL, 1
NOT AL   各个位 取反
INC AL   ; 取反加一?? 还记不记得 NEG

SHIFT 和 ROTATE

SHL DST, CNT    ; 逻辑左移
SHR DST, CNT        
SAL DST, CNT    ; 算数左移
SAR DST, CNT

值得注意的是:

  • 逻辑左移和算数左移没有什么区别
  • 逻辑右移左面补0,算数右移左面补符(最高位)。
  • DST 可以是MEM数或者寄存器。 CNT 是8位立即数,1,或者CL
  • SHIFT 操作将会改变 CF, OF ZF ,PF, AF。 注意OF, 当shift 一位的时候,如果结果的符号和原来一样,OF = 0. 否则为1.
ROL DST, CNT
ROR DST, CNT
RCL DST, CNT    ; 通过 CF 来移位
RCR DST, CNT    

注意:

  • ROL, ROR 只是将出的位,保留到CF一份, CF不会对结果产生影响
  • RCL, RCR 是移动到CF, 在将原来CF的值填到另一端。
  • 只会影响CF, OF(至对于一位ROTATE,如果符号变化,OF=1)

跳转指令

无条件跳转

JMP SHORT OPR    ;  OPR 可以是一个label 例如
JMP SHORT ADDT
.....
ADDT:  MOV AL , AH

SHORT 的范围是同一个段的8位, +127 ~ -128

JMP OPR     ; 16位。   +32767~-32768  当前段
JMP FAR PTR OPR ;   can be another 段   32bits
SO....
JMP WORD PTR OPR ;  16 near jmp
JMP DWORD PTR OPR ;  32  FAR JMP
JMP REG16/32

条件跳转

跳转指令 跳转条件
JE / JNE 是否相等(其实考察的是ZF)
JAE/ JGE A
JBE/ JLE A <= B
JC/JZ/JS/JO/JP 当相应的CF, ZF, SF, OF, PF 寄存器为1时跳转

跳转表

传言,switch 于 else if 的区别就是, switch 会被编译器编译成跳转表。跳转表的概念比较好理解但难解释,直接上实例吧!

.8086⇠
.MODEL small⇠
.STACK⇠
.DATA⇠
TABLE DW DAY_1⇠
      DW DAY_2⇠
      DW DAY_3⇠
      DW DAY_4⇠
      DW DAY_5⇠
      DW DAY_6⇠
      DW DAY_7⇠  ;      请忽略, 是vim 特效
.CODE
....
....
    DAY_1:⇠
        MOV AX, OFFSET MONDAY⇠
        PUSH AX⇠
        CALL PRINT⇠
        JMP EXIT⇠
    DAY_2:⇠
        MOV AX, OFFSET TUESDAY⇠
        PUSH AX⇠
        CALL PRINT⇠
        JMP EXIT⇠
    DAY_3:⇠
        MOV AX, OFFSET WEDNESDAY⇠
        PUSH AX⇠
        CALL PRINT⇠
        JMP EXIT⇠
    DAY_4:⇠
        MOV AX, OFFSET THURSDAY⇠
        PUSH AX⇠
        CALL PRINT⇠
        JMP EXIT⇠
....

意思就是指,数据段把相应的cluster 的名字写入,而具体的实现写在下面,跳转的时候:

    JMP TABLE[DI]⇠

就会跳转到表中该项对应的簇!

常用标志位的置位与复位

指令 作用
CLC Let CF = 0
CMC Let CF = ~CF
STC Let CF = 1
CLD Let DF = 0
STD Let DF = 1
CLI Let IF = 0
STI Let IF = 1

Loop

普通Loop

MOV CX, [circles]
AGAIN:
    XXXX
    LOOP AGAIN  

特殊Loop

  • 当相等的时候就LOOP
LOOPZ/E
  • 当不相等的时候就LOOP
LOOPNZ

串操作**

DI 和 SI 以及串操作思想

串操作是对一些列字节进行一次性操作,涉及到以下三个寄存器:

  • SI: 串操作源地址, 段地址参考 DS
  • DI: 串操作目的地址 , 段地址参考 ES
  • DF: 串操作的方向, DF = 0 , DI , SI 执行完一次会++, DF=1, DI,SI执行完会--

常用的串指令

  • LODS 指令, 从内存加载到寄存器AL,AX,EAX。。。 有LODSB, LODSW, LODSD, 最后一位是Byte,Word,DWord。 决定了执行完一次后,DI,SI加/减多少个Byte。

  • STOS 指令, 将AL,AX 存到DI 指向的内存空间。

    的指令都是默认执行一次,如果要重复,就需要:

LEA DI, BUFF ; BUFF is a array
MOV CX, 1000
MOV AL, 0
REP STOSB   ; init BUFF with 0
  • MOVS 常用,将 DS:SI 地址内容 移动(复制)到 ES:DI对应的地址, 同样需要配合 CX, 以及 REP指令

  • INS 与 OUTS 主要用于与IO设备的串交互,同样需要配合 CX, 以及 REP指令

  • SCAS 指令 串比较,将ES:DI指向的内容与AL,AX,EAX内容比较。 配合 REPNE(相等就停止) / REPE (不想等就停止),当然硬性条件是cx!=0.

  • CMPS 串指令, 将SI,DI指向的内容进行比较,和SCAS 类型,配合RExxP使用。

    令在汇编中作用很大,妥善使用能省去很多麻烦, 实验中有一个回文串的判断,我是用循环进行判断,十分麻烦,现在看来,利用STOSB将会非常简单,具体思路可能是:

MOVSB ,DF = 1, 将内容反向复制, 在利用CMPS 进行判断即可!

汇编过程的编写

LABEL PROC [NEAR/FAR,far常用于中断处理函数]
PUSH XX
....
POP XX
RET
LABEL ENDP
....
CALL LABEL / FAR PTR LABEL
; CALL reg/mem16 间接调用,不常用/ call dword ptr mem32/48

REP 指令

所有过程都需要,一般没有参数,直接调用即可,但是当函数有返回值时,就需要了解下面的用法了:

REP EXP

这个指令做了些啥呢,

IP <--- (SP+1,SP)
SP <--- SP+2
SP <--- SP + EXP

EXP 可以自己想办法用, 反正效果就是让SP多向下移动了EXP个字节而已。

函数传参

  • 通过寄存器传参,这个比较好弄
  • 通过堆栈传参,这个比较常用,需要掌握以下。 假如要传递两个个参数:
PUSH AX  ;PARAM1
PUSH BX ; param 2
CALL LABEL
...
LABEL PROC NEAR
PUSH BP
MOV BP, SP  ; 利用BP进行临时的堆栈操作
PUSH XXXXX.... ;保留现场
MOV SI , [BP+6]; 第一个参数,想想为什么时+6呢, 因为BP下面的参数内容为:
;REG1  <--- SP
;REG2  
...
;原来BP内容    <--- BP
;IP                BP+2
;PARAM2            BP+4
;PARAM1            BP+6

现在明白了把, 具体情况还要根据函数类型来呀,因为有些函数可不止默认保留IP,还有CS什么的。。

总结

我所学到的有关汇编的全部内容就到这里啦, 有疑问可以联系作者[email protected]

你可能感兴趣的:(汇编语言学习笔记(全))