汇编实验文档
使用方法: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,BX
与AND AX,BX
命令有相同效果,只是Test指令不改变AX和BX的内容,而AND指令会把结果保存到AX中。
取值运算符
NEAR 指令单元
FAR 指令单元
- 这个主要和两个指令有关call ret
call 一个near过程,只把偏移地址压入堆栈,过程返回时用retn返回
call一个far过程,把偏移地址和段地址入栈,过程返回时用retf返回
在过程中的ret指令根据near和far的不同,分别编译成retn和 retf
retn和retf的机器码是不同的,你也可以不定义过程,直接用retn和retf
如果你的子程序和主程序在同一个代码段,则使用near,调用发生后,主程序堆栈中只压入ip值;
如果你的子程序和主程序不在一个代码段,则使用far,调用发生后,主程序堆栈中将压入cs、ip值;
- near定义的标号表示段内近跳转,近调用的地址
far定义的标号表示段间远跳转,远调用的地址
near的时候,地址是16位的,far的时候是32位的(段地址+偏移地址)
常见指令
1 | MOV AX, DATA |
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 | MOV SI, OFFSET X ;将x的地址放到SI中 |
基于无符号数比较的跳转
Mov指令
Notice
MOV AX,10-CX
错误的,不能用数字直接减寄存器
$代表当前地址
若寄存器不够用,可以将之前的寄存器push进堆栈
跳转指令
1 | JE ;等于则跳转 同JZ |
注意有无符号!!
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 | MOV AX,8001H;(AX)=1000 0000 0000 0001B |
CPU指令
IN/OUT指令
汇编语言中,CPU对外设的操作通过专门的端口读写指令来完成;
读端口用IN指令,写端口用OUT指令。
例子如下:
1 | IN AL,21H;表示从21H端口读取一字节数据到AL |
从硬件角度理解什么是计算机端口 - 知乎 (zhihu.com)
端口
CPU与外界传输信息的“中介”,物理上则是一块硬件,内部有寄存器,寄存器中存放与CPU和外设交换的信息,因此,CPU将这些寄存器当作端口访问
因为这个硬件有地址线和CPU相连,因此CPU可以通过寻址的方式找到这些寄存器。
交换数据的过程
外设的输入不直接送入内存和CPU ,而是送入相关的接口芯片的端口中;(接口芯片装在接口卡和主板上)
CPU 向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。
宏指令
宏定义是指用一个标识符(宏名)来代替一组指令序列
宏功能的使用过程是:宏定义、宏调用、宏展开。
MACRO 与 ENDM必须成对出现,先定义后引用
1 | ;宏指令定义格式: |
算数运算指令
加法与减法指令
不带进位加/减法指令: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
ORG
:移动地址指针伪指令,用于设置当前汇编地址计数器中的值
格式:ORG 数值表达式
1 | BUFFER LABEL BYTE ;建立8字节未初始化数据缓冲区 |
程序设计
一般分为顺序结构,选择结构,循环结构,
建议先画流程图
分支结构程序设计
跳跃表法:
用于分支较多的情况,即实现CASE结构;
对于分支比较少的情况IF_THEN_ELSE结构是最简便可行
实现步骤
第一是构造跳转表;
第二是使用跳转地址、转移指令或关键字实现分支。
1 | DATA SEGMENT |
循环
如果有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
存储器
(103条消息) 理解CPU/寄存器/内存之间的关系_cpu和寄存器的关系_山羊胡子Q的博客-CSDN博客
计算机存储器可分为内部存储器(简称内存或主存)、CPU缓存和外部存储器(辅助存储器)。
内存是CPU能直接寻址的存储空间,由半导体器件制成。计算机内存的特点是访问速率快,容量小,价格高。
我们平常使用的程序,如Windows操作系统、打字软件、游戏软件等,一般都是安装在硬盘等外存上的,但必须把它们调入内存中运行,才能真正使用其功能,我们平时输入一段文字,或玩一个游戏,其实都是在内存中进行的,数据产生后不断地由内存向外存进行刷写。就好比在一个书房里,存放书籍的书架和书柜相当于电脑的外存,而我们工作的办公桌就是内存。通常我们把要永久保存的、大量的数据存储在外存上,而把一些临时的或少量的数据和程序放在内存上,当然内存的好坏会直接影响电脑的运行速度。
Note:区分存储器与寄存器
外部存储器是指处理内存和CPU以外的存储器。这种存储器的最大特点就是断电后仍能保存数据。除此之外还具有访问速率慢,容量大,价格相对较低的特点。常见的外存设备有:硬盘、软盘、光盘、U盘等。
固态硬盘与移动硬盘
CPU高速缓存(英语:CPU Cache,简称缓存)是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。由静态存储芯片(SRAM)组成。其容量远小于内存,但速度却可以接近处理器的频率。当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。
按照存储能力与电源的关系可以分为两类:
易失性存储器和非易失行存储器。
RAM
电源断电后,存储器中所存储的数据便会消失的存储器,临时数据存储介质。RAM分为:动态随机存取存储器和静态随机存取存储器
- DRAM(动态随机存取存储器)
- SRAM(静态随机存取存储器)
ROM
闪存
堆栈
涉及的元件:
SS:堆栈段寄存器,顾名思义,与DS对应,存放的是堆栈的段地址
SP:堆栈指针寄存器,指出堆栈段中栈顶的偏移地址
BP:基址指针寄存器,指出要处理的数据在堆栈段中的起始地址
堆栈必须按字操作
在子程序调用和中断处理后,需要保存通用寄存器的值,子程序返回时,恢复通用寄存器的值
PUSH:SP-=2;(SP+1:SP)<-SRC
Example:
1 | ADDP PROC ;加法子程序,完成2位十进制数相加 |
子程序设计
①定义: 过程名 PROC [NEAR/FAR]
…
RET
过程名 ENDP
②调用: CALL 过程名
NEAR类型为段内调用,即主子程序在同一个代码段内,NEAR可省略。
FAR类型为段间调用,被其它代码段调用的过程要定义为FAR过程
Example
两个字节数据相加,存放到一个结果单元中,并显示十六进制结果
1 | CODE SEGMENT |
问题:
A:是不是只有SI和DI能储存地址
Q:不是,可以用BX等寄存器存地址,当寻址时
MOV AX,[BX]
表示以BX内的数据为偏移地址,取出对应地址的数据,放入AX中
此为寄存器间接寻址,如果指定的寄存器是BX,SI,DI,则操作数默认在数据段DS中,取DS作为该操作数的段地址
如果指定寄存器是BP,则操作数默认在堆栈段,取SS寄存器的值作为操作数的段地址
A:寄存器与存储器
Q:AX,CX等为寄存器,存储器可以理解为数据段中的地址,需通过地址来访问!
存储器的一个作用就是存储数据,解决寄存器不够的问题,让寄存器只是“暂时存放数据”!