本文转载自厉害的 孔姐姐博客

由一只孔姐姐的小迷弟负责排版和整理~

一、基本概念

1.指令:单个的 CPU 操作,通知 CPU 执行某种操作的 “命令”
  • 指令集:所有指令的集合

  • 机器指令:用二进制序列(0、1)代码书写。硬件只能识别、存储和运行机器指令

  • 符号指令:用字符串形式的序列(包含字符串形式的操作码、操作数)

2.指令的组成:操作码 + 操作数
  • 指令长度:机器指令长度为 1~16 字节

  • 指令地址:多字节指令会占用连续的内存单元,存放指令第一个字节的内存地址,称为 指令地址

  • 指令存放:

    • 多字节的操作数连续存放,规则遵循 小端法
    • 低字节存放在低地址单元,高字节存放在高地址单元。
  • 指令格式:

3.操作数:指令的操作对象(输入数据、输出数据)----> 存放于 CPU 寄存器、计算机的存储器、接口电路中的端口

(1)寄存器操作数:存放于通用寄存器(8 个)中

(2)段寄存器和程序指针: CS:IP 决定取哪条指令

(3)标志存储器:分 状态标志控制标志

  • 状态标志

C:最高位产生 借位或进位标志。进位 C=1

O:溢出标志。溢出 O=1

Z:零标志。结果为 0 则 Z=1

S:符号标志。就是结果的符号位。

P:奇偶标志。低 8 位中 1 的个数为偶数 P=1

A:辅助进位标志。低半字节向高半字节有进位或借位,A=1

  • 控制标志

D:方向标志。D=1 时串操作时自动减量

I:中断标志。I=1 时允许 CPU 接收外部的中断请求

T:陷阱标志。T=1 时进入单步调试状态。

注意 O 标为 0,加数与被加数是 1,结果也是 1,相同。O 标为 0

但是,在讨论是否溢出的时候,就要分情况讨论了。(有 O,无 C)

二、寻址方式

1.操作数的存在方式(4 种)
  • 立即数:包含在指令中 ------------ 立即寻址

  • 寄存器操作数:存放于 CPU 的某个寄存器中 ---------------- 寄存器寻址

  • 内存操作数:存放于存储器中 ------------------ 存储器寻址

  • I/O 端口操作数:存放于 I/O 端口中

2.立即寻址:
MOV AL,OFFSET BUF ;(是不是可以理解成,取出了偏移量,就取得了操作数)
MOV CX,0A234H     ;( A~F 开头的数字,加上 0 作为前缀)
3.寄存器寻址:存放在寄存器中
MOV EAX,12345678H
INC  SI
4.存储器寻址(内存操作数寻址): 5 种

使用段页式管理部件,将指令中的逻辑地址转化为对应的物理地址,(对应第二章实模式下,存储地址空间)再通过总线系统访问实际的物理存储单元。

  • 段寄存器名称 : 偏移地址表达式

段寄存器名称,也称为段超越前缀,是存放操作数的存储单元所在的逻辑段:如 CS、DS、SS、ES。而这些段寄存器中存放逻辑段的段基址(逻辑段在存储空间中的位置)。 16 位

偏移地址表达式,给出偏移地址,也称偏移量,相对于逻辑段段首单元的地址偏移量。16 位

  • 操作数的物理地址 = 段基址 * 16 + 偏移地址

(1)直接寻址:偏移地址 用数值、变量名表示

ADD AL,DS:[45H]        ;这里段寄存器名称一定要加
MOV AX,ES:[1000H]
MOV AX,DS:BUF          ;也可以写成 MOV AX,BUF
INC BUF+2

用变量名代表偏移地址,变量名在汇编的时候,会给出实际的偏移地址,所以 BUF+2,BUF 都是直接寻址。

(2) 寄存器间接寻址:间接寻址、间址

注意,这里是要从内存中取操作数,但是通过寄存器找的而已,差不多这意思?

MOV AX,[BX]
MOV AX,[SI]

但是,只有一些指定的通用寄存器能够作为间址寄存器使用

(3)基址寻址:

MOV AX,[BX+2]

同样,只用一些特别指定的通用寄存器可以作为基址寄存器使用

(4)变址寻址:

MOV AL,[SI+2]

有比例因子的变址寄存器的寻址位数都是 32 位的(就是 E。。。)

无比例因子的变址寻址方式只能使用 SI、DI

MOV SI,BUF 单元的偏移地址
MOV AH,SS:[SI+3]

(5) 基址 + 变址寻址

MOV AL,[BX+SI+2]

总结一下寻址方式:

  • BX、SI、DI 对应的一定是数据段,BP 对应的一定是堆栈段

  • SI、DI 不能做基址寄存器。 [SI] 是间址, [SI+3] 是变址,[BX] 是间址,[BX+3] 是基址。

  • [BX]、[SI] 是间址

  • [DX] 是不对的,没有 DX 的寄存器

三、汇编语言语法(名字项、操作数项、操作项)

1.可执行文件的生成(考察后缀名,过程)

源程序 ------------ .asm ----------------------- .obj --------------------- .exe

​ EDIT 编辑 TASM 汇编 TLINK 链接

  • .exe

  • .com

2.语句类型:指令性语句 指示性语句

指令性语句: 符号指令(机器指令):CPU 执行

指示性语句:伪指令和宏指令

  • 伪指令:数据定义伪指令符号定义伪指令。汇编工具执行,为汇编工具提供信息,例如计算出某个逻辑段的段基址。有 DB/DW/DD、EQU、SEGMENT、END 等等。
3.名字项:有标号和变量 2 种类型。又称符号地址,都有段属性、偏移属性、类型属性三种。
JUMP 标号       ;这里的标号就是符号地址

标号:定义在代码段中。

变量:定义在 DS、SS、ES 中

4.操作项:操作码助记符(MOV SUB…)、伪指令助记符

数据定义伪指令: DB DW DD DF/DQ/DT

符号定义伪指令:EQU =

注意的点:

①DB 可以定义用单引号括起来的很长的字符串,但是都是按照从上往下,地址由低向高排的

但是 DW 只能存单引号括起来的一个或者两个字符

5.操作数项:包括数值表达式、地址表达式

(1)数值表达式:标号和变量、常量(立即数、字符串常数、符号常数)、数值运算符(算术运算符、逻辑运算符、关系运算符、数值回送运算符)

字符串常数是用单引号括起来的一个或多个字符,在汇编结束后,会转换成对应的 ASCII 码。'12’会转换为 3132H。

关系运算只能对两个数字,或者同一个逻辑段的两个存储单元中的数。如果为真,则结果为 0FFFF H,,假的结果为 0

数值回送运算符中:

  • SEG 取出数据段的段基址

  • OFFSET 取出,在逻辑段中相对于段首的偏移地址

  • $ 返回当前汇编地址计数器的值

MOV AX,SEG DATA  ;SEG 可省略
MOV AX,SEG DATA
MOV DS,AX
BUF DB 10H,20H,30H
MOV BX,OFFSET BUF
MOV AH,[BX+1]

执行后(AH)=20H

BUF DB 'HELLO NUPT'
COUNT EQU $-BUF

汇编结束后,COUNT 的值为 10

(2)地址表达式:汇编结束后,是变量或者标号所在逻辑段的偏移地址值

包括 方括号运算符 [] 和变量的地址表达式、属性操作符(PTR、LOW/HIGH、SHORT、THIS)

MOV AH,BUF[1]
;等效于
MOV AH,BUF+1

PTR 操作符:

属性不对,必须用 PTR

(源操作数是立即数),除了直接寻址的内存操作数,都必须加 PTR

(5)操作项:包括操作码助记符、伪指令助记符

伪指令:数据定义伪指令(DB DW DD)、数据定义伪指令(EQU = )

四、汇编语言指令集

传送指令、算术运算指令、转移和调用指令、逻辑运算和移位指令、串操作指令、处理机控制指令

1.传送类指令

通用传送:MOV MOVSX MOVZX LEA 指针传送指令 LAHF/SAHF XCHG

​ MOV XCHG 源目操作数不能同时为内存操作数,不能将立即数直接传送到段寄存器

堆栈操作指令 PUSH POP , 栈底是高地址单元。

PUSH 之后,(SP) - 2 -> (SP) ; POP 之后,(SP) + 2 -> (SP)

这里有些例题,挺有意思的

2.算术运算类指令
  • ADD SUB 不能同时为内存操作数,不能同时为段寄存器 ADC SBB 为二进制数带进位

  • INC DEC: INC [BX], 单操作数,直接寻址必须加 PTR

  • NEG:求补指令,但是不是求补码! 0 - 目标操作数

  • MUL 乘数

字节乘法: AL ---->AX AL×乘数 = AX

字乘法:AX—>DXAX 高位 DX, 低位 AX AX×乘数 = DXAX

双字乘法:EAX---->EDXEAX

  • DIV 除数

字节除法:被除数 AX,商 AL, 余数 AH AX÷除数 = AL···AH

字除法:DXAX÷除数 = AX···DX 与字节除法的存储方式类似,余数覆盖高位的被除数,商覆盖低位的被除数。

(里面 2 个,外面 2 个,成对覆盖,方便记忆。。。)

  • CMP 比较指令 (CMP X,Y X-Y)
CMP BYTE PTR [BX],45H
JC NEXT
  • BCD 码调整指令

我认为考试应该只考组合 BCD 码(比如 8421 码),希望不要打脸,我要复习不完了。。。

记得第一章有 BCD 码的结果调整方法:

低四位有进位,+06H

低四位非法,+66H

高四位非法,+60H

我们在给计算机在存储数值的时候,都是用的 XXH,如果直接 ADD AX,BX, 那么进行的是真实的, 16 进制的加法。09H+06H=0FH

如果需要进行 BCD 码运算,就得用到 BCD 码的调整指令。09H +06H =15H

(这里为什么有 H 啊,H 不是十六进制数吗?其实可以这样理解,BCD 码中的 H 是没有实际意义的,只是为了方便 BCD 码的简写)

;用算术运算指令实现十进制的计算1234+5678
N1 DW 1234H
N2 DW 5678H
SUM DW ?
 
 
;-----字加----
MOV AX,N1
ADD AX,N2
DAA 
MOV SUM,AX
 
;-----字节加----
 
MOV AL,BYTE PTR N1
ADD AL,BYTE PTR N2
DAA
MOV BYTE PTR SUM,AL
 
MOV AL,BYTE PTR N1+1
ADD AL,BYTE PTR N2+1
DAA
MOV BYTE PTR SUM+1,AL
3.转移和调用指令

包括:无条件转移 JMP 、条件转移指令、子程序调用与返回指令、软件中断与返回指令

JMP 标号 只考察段内直接转移(只修改 IP,CS 值不变) 标号是符号指令的地址

条件转移指令有 3 个表格要记(根据状态位的,无符号数的,有符号数的)

这三个表里,JC(C=1)、 JZ(Z=1)、 JNC、 JNZ

JA(jump above) JC(C=1)

JG(jump greater,x>y) JGE(x>=y) 常用

LOOP:

    MOV CX,5
AGAIN:
 
 ....
 
    LOOP AGAIN

子程序 XYZ 调用: CALL XYZ

掌握段内直接调用,其原理是,首先将 CALL 指令的下一条指令的地址,即断点的偏移地址压入堆栈中保存,然后将子程序的入口偏移地址 ->(IP),(SP-2)->SP,程序由主程序转到子程序

;主函数中
CALL XYZ
 
;子程序
XYZ PROC
 ....
 ....
 ....
 RET
XYZ ENDP

软件中断与返回指令:

INT n

在第四章中,会疯狂地用到

4.逻辑运算和移位指令
  • 取反 NOT

  • 与运算 AND:使特定位为 0

  • 或运算 OR:使特定位为 1

  • XOR:使特定位取反,,,用 1 异或 x, 对 x 取反

  • TEST:测试, 运算,影响标志位

移位指令

开环:SAL SAR SHL SHR

闭环:RCL RCR ROL ROR

左移、右移看最后一个字母 left or right

逻辑、算术运算看中间 algorithm

带 R 的是有循环 recycle?

有符号数 – 算术右移补符号位

开环的左移一定都补 0,因为毕竟左移是要乘 2 的

闭环带 C 的,那就是含进位的,左移出来的那一位,不仅给了 C,也循环到了第 0 位

5.串操作指令

这里,应该是只要掌握

①源串定义在数据段,用 SI 间址访问

②目标串定义在附加段,用 EI 访问

③D=1(STD 使得 D=1),减址传送 D=0(CLD 使得 D=0),增址传送