使用方法:ctrl+f,输入你想查找的知识关键词

常量

字符常量:用单引号’ ’括起来的ASCII字符,其值是该字符的ASCII代码值

’The 2X means 2 multiple X’

符号常量:是用标识符定义的常量

PORTA EQU 80H

则指令 MOV AL,PORTA与指令MOV AL,80H等价

变量

DB:定义字节(8位)

DW:定义字(16位)

  • DB伪指令将按顺序为字符串中每一个字符或符号分配一个字节单元,存放它们的ASCII编码,

  • 但DB以外的数据定义伪指令只允许定义最多两个字符的字符串,且按逆序存放在低地址开始的单元。

注意:3个及其以上的字符,只能用DB定义。当字符串的长度不超过两个字符时可用DW定义。

SEGMENT:变量的段地址

OFFSET:变量的偏移地址

?:表示不预置确定的初值

运算符与表达式

算数运算符

可以用于数值和地址。

地址相减,同一段中两个地址相减,其值为两个地址之间字节单元的个数

地址+整数=另一个地址

逻辑运算符

只能用于数值表达式,不能用于地址表达式

注意:逻辑运算是逐位计算

  • AND

可以用于清除一个或多个位

假设 BL 寄存器包含 0011 1010 。如果需要将高位清除为 0,则使用 0FH 对其进行 与运算

1
ANDBL,  0FH  ; BL 设置为 0000 1010
  • OR

OR 指令总是清除进位(CF)和溢出标志位(OF),并根据目标操作数的值来修改符号标志位(SF)、零标志位(ZF)和奇偶标志位(PF)。比如,可以将一个数与它自身(或 0)进行 OR 运算,来获取该数值的某些信息:

1
or al,al

下表给出了零标志位和符号标志位对 AL 内容的说明:

零标志位 符号标志位 AL 中的值
清0 清0 大于0
置1 清0 等于0
清0 置1 小于0
  • XOR

  • TEST

Test命令将两个操作数进行逻辑与运算,并根据运算结果设置相关的标志位。但是,Test命令的两个操作数不会被改变。运算结果在设置过相关标记位后会被丢弃。

TEST AX,BXAND AX,BX命令有相同效果,只是Test指令不改变AX和BX的内容,而AND指令会把结果保存到AX中

取值运算符

NEAR 指令单元

FAR 指令单元

  1. 这个主要和两个指令有关call ret
    call 一个near过程,只把偏移地址压入堆栈,过程返回时用retn返回
    call一个far过程,把偏移地址和段地址入栈,过程返回时用retf返回
    在过程中的ret指令根据near和far的不同,分别编译成retn和 retf
    retn和retf的机器码是不同的,你也可以不定义过程,直接用retn和retf

如果你的子程序和主程序在同一个代码段,则使用near,调用发生后,主程序堆栈中只压入ip值;
如果你的子程序和主程序不在一个代码段,则使用far,调用发生后,主程序堆栈中将压入cs、ip值;

  1. near定义的标号表示段内近跳转,近调用的地址
    far定义的标号表示段间远跳转,远调用的地址
    near的时候,地址是16位的,far的时候是32位的(段地址+偏移地址)

常见指令

1
2
MOV AX, DATA
MOV DS, AX

mov ax, data ;将伪段地址放入ax中,data不是指令,而是伪指令,实际上是一个动态的内存地址,要想运行,必须先将其放入到ds中,但是内存数不能放入段地址。所以

mov ds,ax ;就是将段地址装入段寄存器,最终达到了段地址装入段寄存器的目的!

在80X86中规定,内存数不可以直接装入段寄存器,所以才会有这样的一次中转!
datas不是指令字,而是伪指令,实际上你可以理解他为一个标签,将这个标签装入AX中,实际装入的就是数据段的首地址

指令与伪指令的区别在在于,指令是属性指令集(属于CPU的),而伪指令则是属于编译软件的,扫描后由编译软件进行的操作

伪指令

EQU:赋值伪指令

ASSUME CS:CODE, DS:DATA

(101条消息) 汇编语言——assume的作用_汇编assume_手写的从前66的博客-CSDN博客

assume 的作用是关联段名与段寄存器

如果你在数据段中定义了变量名,比如:

1
x   db  0

而你在代码中,需要直接使用这个变量名,比如:

1
mov   al,  x

那么,汇编程序在汇编时,就会报告错误。

因为,mov指令中遇到 x 这个变量名时,汇编程序不知道它要用哪个段寄存器作为段地址

所以:

若要用变量名直接访问,或使用语句标号(比如你例子中的标号 start)就必须要在assume伪指令中将这些变量或标号所在段的段名,与段寄存器名关联,否则会出错。

如果你不使用段中的变量名,可以不关联这个段的段名与寄存器。

如果你访问变量时,都指定了段跨越前缀,关联也不是必须的。比如你可以用 mov al, ds:x访问变量 x 。

1
2
MOV SI, OFFSET X	;将x的地址放到SI中
MOV DI, OFFSET Z ;将z的地址放到DI中

基于无符号数比较的跳转

Mov指令

img

img

img

Notice

MOV AX,10-CX 错误的,不能用数字直接减寄存器

$代表当前地址

若寄存器不够用,可以将之前的寄存器push进堆栈

跳转指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
JE   ;等于则跳转			同JZ
JNE ;不等于则跳转 同JNZ

JA ;无符号大于则跳转
JNA ;无符号不大于则跳转
JAE ;无符号大于等于则跳转 同JNB
JNAE ;无符号不大于等于则跳转 同JB


JB ;无符号小于则跳转
JNB ;无符号不小于则跳转
JBE ;无符号小于等于则跳转 同JNA
JNBE ;无符号不小于等于则跳转 同JA


JG ;有符号大于则跳转
JNG ;有符号不大于则跳转
JGE ;有符号大于等于则跳转 同JNL
JNGE ;有符号不大于等于则跳转 同JL



JL ;有符号小于则跳转
JNL ;有符号不小于则跳转
JLE ;有符号小于等于则跳转 同JNG
JNLE ;有符号不小于等于则跳转 同JG




JZ ;为零则跳转
JNZ ;不为零则跳转

JS ;为负则跳转
JNS ;不为负则跳转

JC ;进位则跳转
JNC ;不进位则跳转

JO ;溢出则跳转
JNO ;不溢出则跳转



JP
;为偶则跳转

JNP ;不为偶则跳转

JPE ;奇偶位置位则跳转 同JP

JPO ;奇偶位复位则跳转 同JNP

注意有无符号!!

JL/JG是用于有符号数的,JB/JA用于无符号数

MOV AL, NUM ;AL=19H
CMP AL, NUM + 1 ;19H-98H: ZF=0, CF=1, OF=1; SF=1

JA … —–CF=0 且 ZF=0,不满足条件,不转移
JG … —–SF=OF且 ZF=0,满足条件,转移

JA … —–大于转移,是针对无符号数的

JG … —–大于转移,是针对有符号数的

如果是无符号数,19H 就小于 98H。
如果是有符号数,19H 就大于 98H。因为 89H 是负数。

ENDP 表示PROC所定义的过程结束. (end procedure)
ENDS 表示SEGMENT定义的段结束. (end segment)
END 程序结束.

移位指令

逻辑移位: SHL、SHR

算术移位: CAL、CAR

汇编语言中 sal(算术左移指令)和shl(逻辑左移指令)指令的寻址方式、控制移位方式等都一样,区别其实只有一处:
SAL算术移位指令在执行时,实际上把操作数看成有符号数进行移位,最高位符号位移入CF,但本身保持原值;其余位顺序左移,次高位被舍弃。

SHL逻辑移位指令在执行时,实际上把操作数看成无符号数进行移位,所有位顺序左移,最高位移入CF。

例子如下???

1
2
3
4
MOV AX,8001H;(AX)=1000 0000 0000 0001B
SAL AX,1 ;(AX)=1000 0000 0000 0010B
MOV AX,8001H;(AX)=1000 0000 0000 0001B
SHL AX,1 ;(AX)=0000 0000 0000 0010B

CPU指令

IN/OUT指令

汇编语言中,CPU对外设的操作通过专门的端口读写指令来完成;

读端口用IN指令,写端口用OUT指令。
例子如下:

1
2
3
4
5
6
7
8
IN AL,21H;表示从21H端口读取一字节数据到AL
IN AX,21H;表示从端口地址21H读取1字节数据到AL,从端口地址22H读取1字节到AH
MOV DX,379H
IN AL,DX ;从端口379H读取1字节到AL
OUT 21H,AL;将AL的值写入21H端口
OUT 21H,AX;将AX的值写入端口地址21H开始的连续两个字节。(port[21H]=AL,port[22h]=AH)
MOV DX,378H
OUT DX,AX ;将AH和AL分别写入端口379H和378H

从硬件角度理解什么是计算机端口 - 知乎 (zhihu.com)

端口

CPU与外界传输信息的“中介”,物理上则是一块硬件,内部有寄存器,寄存器中存放与CPU和外设交换的信息,因此,CPU将这些寄存器当作端口访问

因为这个硬件有地址线和CPU相连,因此CPU可以通过寻址的方式找到这些寄存器。

交换数据的过程

外设的输入不直接送入内存和CPU ,而是送入相关的接口芯片的端口中;(接口芯片装在接口卡和主板上)
CPU 向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。

宏指令

宏定义是指用一个标识符(宏名)来代替一组指令序列

宏功能的使用过程是:宏定义、宏调用、宏展开。

MACRO 与 ENDM必须成对出现,先定义后引用

1
2
3
4
5
6
;宏指令定义格式:
宏指令名 MACRO <形参列表>
汇编程序段(宏体)
ENDM
;宏指令调用格式:
宏指令名 [实参列表]

image-20230529201351138

算数运算指令

加法与减法指令

不带进位加/减法指令:ADD,SUB

带进位加/减法指令:ADC,SBB

自增/减:INC,DEG

取负:NEG

DAA

DAS

地址

LEA与OFFSET

(101条消息) 汇编语言LEA和OFFSET区别_汇编offset和lea区别_Baoli1008的博客-CSDN博客

Load Effective Address,即装入有效地址的意思,它的操作数就是地址;

lea 是机器指令,offset 是伪指令。

LEA BX, BUFFER;在实际执行时才会将变量buffer的地址放入bx

MOV BX, OFFSET BUFFER ;在编译时就已经计算出buffer的地址为4300(假设),然后将上句替换为: mov bx,4300

lea可以进行比较复杂的计算,比如lea eax,[esi+ebx $\times$ 4],把ebx的值*4,加上esi的值,存入eax中。
mov就不行了。

OFFSET只能取得用”数据定义伪指令“定义的变量的有效地址,不能取得一般操作数的有效地址(摘自80x86汇编语言程序设计教程)
MOV BX,OFFSET [BX+200]这句是错误的 应该用LEA BX,[BX+200]

$与ORG

地址计数器与对准伪指令

$取当前地址

用在指令:代表当前指令的首地址(偏移地址)

用在数据段:表示当前变量的位置,即地址计算器的当前值

此例中,ARRAY首地址是0074,操作到$+4时,该值为0078+4=7C,即存入的数据为7C

image-20230529200805791

ORG:移动地址指针伪指令,用于设置当前汇编地址计数器中的值

格式:ORG 数值表达式

1
2
3
BUFFER  LABEL  BYTE		;建立8字节未初始化数据缓冲区
ORG $+8 ;等价于
BUFFER DB 8 DUP(?)

程序设计

一般分为顺序结构,选择结构,循环结构,

建议先画流程图

分支结构程序设计

跳跃表法:

  • 用于分支较多的情况,即实现CASE结构;

  • 对于分支比较少的情况IF_THEN_ELSE结构是最简便可行

实现步骤

  • 第一是构造跳转表;

  • 第二是使用跳转地址、转移指令或关键字实现分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
DATA	SEGMENT
DATATAB DW ROUTINE_1
DW ROUTINE_2
DW ROUTINE_3
DW ROUTINE_4
DW ROUTINE_5
DW ROUTINE_6
DW ROUTINE_7
DW ROUTINE_8
DATA ENDS
CODE SEGMENT
MAIN PROC FAR ;?
ASSUME CS: CODE,DS: DATA
START: PUSH DS ;保存DS
SUB BX,BX ;BX清零
PUSH BX
MOV BX,DATA
MOV DS,BX ;载入数据段地址
CMP AL,0
JE CONT ;AL=0的情况
MOV SI,0
LP: SHR AL,1 ;逻辑右移1位
JNB NOT_YET ;CF=0,不低于情况
JMP DATATAB[SI]
NOT_YET: ADD SI,TYPE BRANCH TABLE
JMP LP
CONT:
……
ROUTINE_1: ………
ROUTINE_2: …………
RET
MAIN ENDP ;?
CODE ENDS
END START

循环

如果有LOOP则CX会自动递减

如果没有,则要手动DEC CX

DOS表

表:DOS系统功能调INT 21H

MOV AH,4CH

4C 带返回码结束 AL=返回码

返回码?

AH 功能 调用参数 返回参数
00 程序终止(同INT 20H) CS=程序段前缀
01 键盘输入并回显 AL=输入字符
02 显示输出 DL=输出字符
03 异步通迅输入 AL=输入数据
04 异步通迅输出 DL=输出数据
05 打印机输出 DL=输出字符
06 直接控制台I/O DL=FF(输入) DL=字符(输出) AL=输入字符
07 键盘输入(无回显) AL=输入字符
08 键盘输入(无回显) 检测Ctrl-Break AL=输入字符
09 显示字符串 DS:DX=串地址 ‘$’结束字符串
0A 键盘输入到缓冲区 DS:DX=缓冲区首地址 (DS:DX)=缓冲区最大字符数 (DS:DX+1)=实际输入的字符数
0B 检验键盘状态 AL=00 有输入 AL=FF 无输入
0C 清除输入缓冲区并 请求指定的输入功能 AL=输入功能号 (1,6,7,8,A)
0D 磁盘复位 清除文件缓冲区
0E 指定当前缺省的磁盘驱动器 DL=驱动器号 0=A,1=B,… AL=驱动器数
0F 打开文件 DS:DX=FCB首地址 AL=00 文件找到 AL=FF 文件未找到
10 关闭文件 DS:DX=FCB首地址 AL=00 目录修改成功 AL=FF 目录中未找到文件
11 查找第一个目录项 DS:DX=FCB首地址 AL=00 找到 AL=FF 未找到
12 查找下一个目录项 DS:DX=FCB首地址 (文件中带有*或?) AL=00 找到 AL=FF 未找到
13 删除文件 DS:DX=FCB首地址 AL=00 删除成功 AL=FF 未找到
14 顺序读 DS:DX=FCB首地址 AL=00 读成功 =01 文件结束,记录中无数据 =02 DTA空间不够 =03 文件结束,记录不完整
15 顺序写 DS:DX=FCB首地址 AL=00 写成功 =01 盘满 =02 DTA空间不够

寄存器

CS:Code Segment(代码段地址)

DS:Data Segment(数据段地址)

ES:Extra Segement(扩充数据,在DS不够放时用)

SS:Stack Segment

IP:

SI:源变址寄存器,一般可用于存放变量地址

BUFF DW 5,7,...

LEA SI,BUFF

存储器

计算机存储器 - 知乎 (zhihu.com)

(103条消息) 理解CPU/寄存器/内存之间的关系_cpu和寄存器的关系_山羊胡子Q的博客-CSDN博客

计算机存储器可分为内部存储器(简称内存或主存)、CPU缓存外部存储器(辅助存储器)。

内存CPU能直接寻址的存储空间,由半导体器件制成。计算机内存的特点是访问速率快,容量小,价格高。

我们平常使用的程序,如Windows操作系统、打字软件、游戏软件等,一般都是安装在硬盘等外存上的,但必须把它们调入内存中运行,才能真正使用其功能,我们平时输入一段文字,或玩一个游戏,其实都是在内存中进行的,数据产生后不断地由内存向外存进行刷写。就好比在一个书房里,存放书籍的书架和书柜相当于电脑的外存,而我们工作的办公桌就是内存。通常我们把要永久保存的、大量的数据存储在外存上,而把一些临时的或少量的数据和程序放在内存上,当然内存的好坏会直接影响电脑的运行速度。

Note:区分存储器与寄存器

外部存储器是指处理内存和CPU以外的存储器。这种存储器的最大特点就是断电后仍能保存数据。除此之外还具有访问速率慢,容量大,价格相对较低的特点。常见的外存设备有:硬盘、软盘、光盘、U盘等。

固态硬盘与移动硬盘

CPU高速缓存(英语:CPU Cache,简称缓存)是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。由静态存储芯片(SRAM)组成。其容量远小于内存,但速度却可以接近处理器的频率。当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。

img

按照存储能力与电源的关系可以分为两类:

易失性存储器和非易失行存储器。

  • RAM

  • 电源断电后,存储器中所存储的数据便会消失的存储器,临时数据存储介质。RAM分为:动态随机存取存储器和静态随机存取存储器

    • DRAM(动态随机存取存储器)
    • SRAM(静态随机存取存储器)
  • ROM

  • 闪存

堆栈

涉及的元件:

SS:堆栈段寄存器,顾名思义,与DS对应,存放的是堆栈的段地址

SP:堆栈指针寄存器,指出堆栈段中栈顶的偏移地址

BP:基址指针寄存器,指出要处理的数据在堆栈段中的起始地址

堆栈必须按字操作

在子程序调用和中断处理后,需要保存通用寄存器的值,子程序返回时,恢复通用寄存器的值

PUSH:SP-=2;(SP+1:SP)<-SRC

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ADDP	PROC  		;加法子程序,完成2位十进制数相加
PUSH AX ; 保护现场
PUSH BX
CLC ;清除进位标志
MOV BX,0
AGAIN: MOV AL,DAT1[BX] ;相加
ADC AL,DAT2[BX]
DAA ;十进制调整
MOV SUM[BX],AL ;存结果
INC BX ;修改下标
LOOP AGAIN ;循环执行8次
ADC SUM[BX],0 ;结果存在SUM为首地址的数据区?
POP BX ; 恢复现场
POP AX
RET ;返回主程序
ADDP ENDP
CODE ENDS
END START

子程序设计

①定义: 过程名 PROC [NEAR/FAR]

RET

过程名 ENDP

②调用: CALL 过程名

NEAR类型为段内调用,即主子程序在同一个代码段内,NEAR可省略。

FAR类型为段间调用,被其它代码段调用的过程要定义为FAR过程

Example

两个字节数据相加,存放到一个结果单元中,并显示十六进制结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CODE  SEGMENT    
ASSUME CS:CODE,DS:DATA
START: MOV AX,DATA
MOV DS,AX
MOV AL,AD1
ADD AL, AD2
MOV DL, AL
MOV SUM,AL ;后面为显示所需
MOV BL, AL
MOV CL,4
SHR AL,CL
AND AL,0FH ;清0高位,实际上可以不用
ADD AL, 30H ;与ascii码对应!0的ascii码为30,所以要加上30,如果要输出字母,则要加37
MOV DL,AL
MOV AH,2
INT 21H
MOV AL,BL
AND AL,0FH
ADD AL,30H
MOV DL,AL
MOV AH,2
INT 21H
CODE ENDS
END START

问题:

A:是不是只有SI和DI能储存地址

Q:不是,可以用BX等寄存器存地址,当寻址时

MOV AX,[BX]表示以BX内的数据为偏移地址,取出对应地址的数据,放入AX中

此为寄存器间接寻址,如果指定的寄存器是BX,SI,DI,则操作数默认在数据段DS中,取DS作为该操作数的段地址

如果指定寄存器是BP,则操作数默认在堆栈段,取SS寄存器的值作为操作数的段地址

A:寄存器与存储器

Q:AX,CX等为寄存器,存储器可以理解为数据段中的地址,需通过地址来访问!

存储器的一个作用就是存储数据,解决寄存器不够的问题,让寄存器只是“暂时存放数据”!