王爽《汇编语言第二版》学习笔记
1 基础知识
2 寄存器
2.1 通用寄存器
- 8086CPU的寄存器都是16位的
- AX、BX、CX、DX4个通用寄存器
2.3 几条汇编指令
- 汇编指令或寄存器的名称不区分大小写
2.4 8086CPU给出物理地址的方法
- 通过两个16位地址合成一个20位物理地址
- 物理地址=短地址*16+偏移地址
2.5 段的概念
- 内存并没有分段,段的划分来自于CPU
- 编程时按照需要,将若干地址连续的内存单元看做一个段
2.6 段寄存器
- 4个段寄存器:CS、DS、SS、ES
- CS为代码段寄存器,IP为指令指针寄存器
- 任意时刻,CS:IP将指向的内容当做指令执行
2.7 修改CS、IP的指令
- 能够改变CS、IP的指令被称为转移指令
- jmp 段地址:偏移地址
- jmp 合法寄存器(仅修改IP)
3 寄存器(内存访问)
3.1 DS和[addr]
- DS寄存器通常用来存放要访问的数据的段地址
- [...]表示一个内存单元,...表示内存单元的偏移地址
- 8086CPU不支持直接将数据送入段寄存器
3.2 CPU的栈机制
- CPU的入栈和出栈都是以字为单位进行的
- 任意时刻,SS:SP指向栈顶
- push和pop也可以在内存单元之间传送数据
3.3 栈段
- 一段内存,既可以是存代码,也可以存数据,也可以是栈空间,关键在于CS\IP、DS、SS\SP的指向
4 第一个程序
4.1 源程序
- 源程序由指令、伪指令和标号组成
4.2 伪指令
-
伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作
-
segment
-
end
-
assume
4.3 标号
- 标号代表一个地址,segment前的标号被处理为一个段地址
4.4 程序返回
- mov ax, 4c00H
- int 21H
4.5 语法错误和逻辑错误
- 编译时出现的错误是语法错误
- 运行时发生的错误是逻辑错误
4.6 EXE文件中程序的加载过程
- 找到一段起始地址为SA:0的内存空间
- 在该区域内前256个字节,创建程序段前缀(PSP)的数据区,DOS利用PSP和程序进行通信
- 从第256字节处开始,装入程序
- 将该内存区的段地址存入DS,设置CS:IP指向程序入口(该内存区256字节处)
5 [BX]和loop指令
- 要完整描述内存单元:需要内存单元地址和长度(类型)
- [bx]表示一个内存单元,bx存储偏移地址,ds存储段地址
- 定义的描述符号“()”:(ax)表示ax中的内容,(20000H)表示内存20000H单元中的内容(括号内为物理地址)
- 约定符号idata表示常量
5.1 loop指令
-
CPU指令loops时,要进行两步:cx-1,若cs不为0跳转至s处指令,若为0向下执行
-
用mov设置cx的值,表示循环次数
5.2 masm对指令的处理
- mov al, ds:[0]
- mov al, [bx] (段地址默认在ds中)
5.3 算和问题
- 将内存单元中的8位数据赋值到16位寄存器ax中,再将ax中的数据加到dx上,从而使两个运算对象的类型匹配且结果不超界
5.4 段前缀
- 用于显式地指明内存单元段地址的"ds:"被称为段前缀
6 包含多个段的程序
6.1 在代码段中使用数据
- dw定义字型数据,db定义字节型数据,dd定义双字型数据
- 也可以说dw开辟了内存空间
- 在end start中,start标号指明了程序入口
6.2 可执行文件的程序执行过程
- 由其他程序Debug、command等将程序加载到内存
- 设置CS:IP指向程序入口
- 程序运行结束,返回加载者
6.3 可执行文件的组成
- 可执行文件由描述信息和程序组成
- 描述信息主要是编译、链接过程中对程序的伪指令进行处理获得的
- 加载者从描述信息中获取程序的入口地址
6.4 将数据、代码、栈放入不同的段
- 段名也是标号,代表了段地址,mov ax, data
- start在code段中,这样CPU就将code段中的内容当做指令执行(CS不用设置,栈和数据段都需设置段地址)
7 更灵活的定位内存地址的方法
7.1 and和or指令
- and按位与,可用于置0
- or按位或,可用于置1
7.2 [bx+idata]
- 表示一个内存单元
- 进行数组处理
7.3 SI和DI
- SI和DI类似于BX,但不能分为两个8位寄存器
7.4 [bx+si/di+idata]
-
mov ax, [bx+si+200]
-
mov ax, 200[bx][si]
-
mov ax, [bx][si].200
-
需要暂存数据的时候,一般用栈,比如双重循环的cx计数问题
8 数据处理的两个基本问题
- 数据在哪里,有多长
- reg表示寄存器,sreg表示段寄存器
8.1 bx、si、di和bp
- 只有这四个寄存器能放在[...]中进行内存单元的寻址
- 要么单独出现,要么以下四个组合:bx\bp和si、bx\bp和di
- 只要在[...]中使用bp,默认的段地址在ss中
8.2 机器指令处理的数据在哪里
- CPU内部、内存和端口
8.3 汇编语言中数据位置的表达
- 立即数、寄存器、段地址和偏移地址
8.4 寻址方式
- 用于定位内存单元的方法称为寻址方式
- 直接寻址
- 寄存器间接寻址
- 寄存器相对寻址
- 基址变址寻址
- 相对基址变址寻址
8.5 要处理的数据有多长
- 指令可以处理字节和字两种尺寸的数据
- 通过寄存器名指明要处理的数据尺寸
- 没有寄存器名的情况下,通过word/byte ptr指明内存单元长度
8.6 dup
- 和db、dw、dd等数据定义伪指令配合使用,用来进行数据重复
9 转移指令的原理
- 只修改IP,称为段内转移
- 都修改,称为段间转移
- 段内转移分为短转移和近转移
- CPU的转移指令类型:无条件、有条件、中断、过程、循环
9.1 操作符offset
- 功能是获取标号的偏移地址
9.2 根据位移进行转移的jmp指令
- jmp short 标号
- 机器码中包含的是转移的位移
- jmp near ptr 标号
9.3 转移的目的地址在指令中的jmp指令
- jmp far ptr标号是段间转移,又称为远转移
9.4 转移地址在寄存器中的jmp指令
9.5 转移地址在内存中的jmp指令
- jmp word ptr 内存单元地址 (取偏移地址)
- jmp dword ptr 内存单元地址 (高地址是段地址,低地址是偏移地址)
9.6 jcxz指令
- jcxz为有条件转移指令,所有条件转移指令都是短转移
- cx=0则跳转到标号
9.7 屏幕显示颜色字符
- 显存中有一块80*25的彩色字符模式显示缓冲区,向该内存中写入数据,内容可以立即显示到显示器上
- 一个字符占两个字节,一个字节存放arscii码,一个字节存放属性
10 CALL和RET指令
10.1 ret和retf
- ret用栈中数据修改IP,实现近转移
- retf用栈中数据修改CS:IP,实现远转移
10.2 call指令
- 将IP和CS入栈
- 转移
10.3 根据位移转移
- call 标号 (近转移)
10.4 根据目的地址进行转移
- call far ptr (段间转移)
10.5 转移地址在寄存器中的call指令
- call 16位reg
10.6 转移地址在内存中的call指令
- call word ptr 内存单元地址
- call dword ptr 内存单元地址
10.7 子程序
-
模块化程序设计
-
call和ret配合实现子程序
-
使用寄存器传参,或者使用栈传参
-
在子程序开始时,将要用到的寄存器中内容全部入栈,退出时恢复,用来解决寄存器冲突问题
11 标志寄存器
- 8086的标志寄存器flag有16位,其中的信息被称为程序状态字(PSW)
- flag寄存器是按位起作用的
11.1 ZF标志
- 零标志位
11.2 PF标志
- 奇偶标志位
11.3 SF标志
- 符号标志位
- 做有符号数的运算时,可以通过它来得知结果正负
- 无符号数运算时,会影响值,但无意义
11.4 CF标志
- 借位或进位标志位
- 用于无符号数运算
11.5 OF标志
- 溢出标志位
- 用于有符号数运算
11.6 adc指令
- 带进位的加法
- adc和add配合可以对更大的数据进行加法
11.7 sbb指令
- 带借位的减法
11.8 cmp指令
- cmp a, b,计算差,但不保留结果,只影响flag寄存器
- 比较无符号数,看zf和cf
- 比较有符号数,看sf和of
11.9 条件转移指令
- 和cmp配合使用
- 根据无符号数比较结果进行转移:je、jne、jb、jnb、ja、jna
- 二者配合使用时不用考虑标志寄存器,只用考虑逻辑含义
11.10 DF标志和串传送指令
- DF是方向标志位,在串处理指令中,控制每次操作后si和di的增减
- DF=0是递增,DF=1是递减
- movsb将ds:si指向的内存单元中的字节送入es:di中,然后根据df值,控制si\di增减
- movsw是传送字单元
- rep movsb根据cx的值,重复执行后面的串传送指令
- cld和std
11.11 pushf和popf
- 用于将标志寄存器入栈和出栈
12 内中断
12.1 内中断的产生
- 用中断类型码表示256种中断信息的来源
- 除法错误:0
- 单步执行:1
- 执行into指令:4
- 执行int n指令:n
12.2 中断向量表
-
用中断类型码通过中断向量表找到中断处理程序的入口地址
-
中断向量表在内存中保存,存放256个中断源对应的中断处理程序的入口地址
-
一个表项占两个字,高地址字存放段地址,低地址字存放偏移地址
12.3 中断过程
- CPU收到中断信息后,引发中断过程,之后将CS:IP指向程序入口,开始执行中断处理程序
- 中断过程:取得中断类型码,标志寄存器入栈,TF和IF置0,CS:IP入栈,读取程序入口地址
- 中断过程由硬件完成
12.4 中断处理程序和iret指令
- 中断处理程序步骤:保存用到的寄存器,处理中断,恢复用到的寄存器,用iret返回
- iret:pop cs, pop ip, popf
12.5 编程处理0号中断
- 0000:0000~0000:03ff空间中存放中断向量表,不会被其他程序使用
- 可以用0000:0200~0000:02ff的空间来存放中断处理程序
- 步骤:编写中断处理程序do0,将do0传送到目标内存,将do0入口地址写入0号表项
12.6 do0的安装
- 用rep movsb传送do0的字节
- 偏移地址是offset do0
- 通过编译器计算do0长度:offset do0end-offset do0
- do0end: nop
- 汇编编译器可以处理表达式
12.7 单步中断
- CPU执行完一条指令后,如果检测到TF=1,则产生单步中断
- 解释了为什么在中断过程中要将TF置0:避免CPU永远执行单步中断程序的第一条指令
- CPU提供单步中断功能为跟踪程序的执行过程,提供了实现机制
12.8 响应中断的特殊情况
- 设置ss和sp的指令连续存放,为避免指向错误栈顶,CPU在两条指令之间不会引发中断过程
13 int指令
13.1 int指令
- CPU执行int n指令,相当于引发一个n号中断的中断过程
- 系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用,又称为中断例程
13.2 BIOS和DOS中断例程的安装过程
- 开机后,CPU加电,初始化CS:IP,自动从FFFF:0单元开始执行程序,该处有一条跳转指令,专区执行BIOS的硬件系统检测和初始化程序
- 初始化程序将建立BIOS所支持的中断向量,中断例程已固化到ROM中,一直在内存中
- 调用int 19H进行操作系统的引导,将操作系统从磁盘上加载到内存中,从此将计算机交给操作系统控制
- DOS启动后,将它所提供的的中断例程装入内存,并建立中断向量
13.3 BIOS中断例程应用
- BIOS和DOS提供的中断例程,都用ah来传递内部子程序的编号
13.4 DOS中断例程应用
- mov ah, 4ch; 程序返回功能
- mov al, 0; 返回值
- int 21h; 中断
14 端口
-
CPU在操控存储器时,把它们总的看做一个内存地址空间
-
和CPU通过总线相连的芯片除了存储器,还有各种接口卡上的接口芯片和主板上的接口芯片(连接外设)
-
这些芯片中,都有一组CPU可读写的寄存器,即端口
-
CPU的角度,对这些端口进行统一编址,建立了统一的端口地址空间
-
CPU可从以下三个地方读写数据:CPU内部寄存器、内存单元、端口
14.1 端口的读写
- 范围0~65535
- 读写指令in和out
- 只能使用ax或al从端口读入数据或发送数据,访问8位端口时用al,16位用ax
- 对256~65535的端口读写时,端口号要放到dx中
14.2 CMOS RAM芯片
- PC机中,有一个CMOS RAM芯片,靠电池供电,关机后内部实时时钟仍然工作
- 0~0dh单元用于保存时间信息,其余大部分单元用来保存系统配置信息,供系统启动时BIOS程序读取
- 两个端口,70h用来存放访问RAM单元的地址,71用来存放访问的数据
14.3 shl和shr指令
- shl是逻辑左移指令,左移一位,移出的送到CF中,最低位用0补充
- 如果移动位数大于1时,必须将移动数放到cl中
15 外中断
- CPU通过端口和外部设备进行联系
15.1 外中断信息
- 外设的输入放到端口后,相关芯片向CPU发出中断信息,引发中断过程,处理外设的输入
(1)可屏蔽中断
- CPU可以不响应的外中断,IF=0,不响应可屏蔽中断
- 可屏蔽中断的中断过程和内中断相同,只是中断类型码是通过数据总线送入CPU,而内中断类型码是在CPU内部产生的
- sti和cli可以设置IF位
(2)不可屏蔽中断
- CPU必须响应的外中断
- 中断类型码固定为2
- 几乎所有由外设引发的外中断,都是可屏蔽中断
15.2 PC机键盘的处理过程
(1)键盘输入
-
每个按键相当于一个开关,芯片对每个键的开关状态进行扫描
-
按下按键后,产生扫描码(通码),送入60h端口
-
松开按键后,产生扫描码(断码),送入60h端口
(2)引发9号中断
- 送入60h端口后,芯片向CPU发出9号中断,CPU响应中断,引发中断过程,执行int 9中断例程
(3)执行int 9中断例程
- 进行基本的键盘输入处理,如果是字符键,将扫描码和对应的arscii码送入内存中的BIOS键盘缓冲区;如果是控制键,写入存储状态字节的单元
15.3 编写int 9中断例程
-
int过程可以模拟为:
-
标志寄存器入栈,TF=1、IF=1,call dword ptr ds:[0]
-
端口和中断机制,是CPU进行IO的基础
16 直接定址表
16.1 描述单元长度的标号
-
a db 1,2,3
-
标号a后没有“: ”,它是同时描述内存地址和单元长度的标号,可以代表内存单元
-
mov al, a[bx+si+3]
-
a被称为数据标号,而带": "的被称为地址标号(只能在代码段中使用)
16.2 在其他段中使用数据标号
- 如果想在代码段中直接使用其他段的数据标号访问数据,需要指定该段的段寄存器和段地址
(1)存储标号偏移地址
- c dw a
- 此时数据标号c处存储的字型数据为a的偏移地址
- 相当于 c dw offset a
(2)存储标号偏移地址和段地址
- c dd a
- 此时数据标号c处存储的双字型数据为a的偏移地址和段地址
- 相当于c dw offset a, seg a
16.3 直接定址表
- table db '0123456789ABCDEF'
- 利用表,可以直接在两个集合(数字集合和字符集合)之间建立映射关系,通过查表,根据给出的数据(如0),可以得到其在另一集合中对应的数据(如'"0"')
- 通过数据,直接计算出所要找的元素的位置(表内偏移)的表,称为直接定址表
17 使用BIOS进行键盘输入和磁盘读写
- BIOS为这两种外设的IO提供了最基本的中断例程
17.1 键盘处理
- int 9中断例程在有键按下时向键盘缓冲区写入数据
- int 16h中断例程在应用程序对其调用时,将数据从键盘缓冲区读出
17.2 磁盘读写
- 设置参数后,调用int 13h
18 CPU的三种工作模式
18.1 实模式
- 实模式下,指令直接访问物理地址,通过段地址*16+偏移地址获得物理地址
18.2 保护模式
-
为了实现进程隔离和多任务并发,引入了保护模式,同时为实现虚拟内存提供了硬件支持
-
通过分段和分页机制来进行保护和隔离
(1)分段和分页
-
分段:引入GDT(全局描述符,即段表),表中的表项称为段描述符,段寄存器中存放索引(段选择子),通过索引找到表项,获取段信息,加上偏移地址得到线性地址,目的是在寻址过程中保存保护信息(比如内存段属性)
-
分页:将虚拟内存划分为大小相同的页,同时物理空间也分为若干个物理块(页框),二者大小相等,离散分配,通过MMU查页表,实现页号到物理块号的映射,目的是将内存分块,将暂时不用的块放到磁盘上,扩展空间
-
分段是必须的,分页不是必须的
-
段是二维的,大小不固定,页是一维的,大小固定
-
保护模式下,每个段都设置了特权级,高特权级的代码可以对地特权级数据进行访问
(2)四种地址
-
虚拟地址是没有经过分段和分页转化的地址,是段寄存器和变址寄存器的组合
-
逻辑地址是用户可以操作的地址,即偏移地址
-
线性地址是虚拟地址经过分段转化后的地址,可以定位虚拟内存
-
物理地址是线性地址经过分页转化后的地址
18.3 虚拟8086模式
-
使用户可以方便地在保护模式下运行一个或多个原8086程序
-
DOS系统中,CPU以实模式运行
-
Windows系统中,CPU以保护模式运行
-
想在Windows中运行DOS程序,CPU切换至虚拟8086模式
19 实验
19.1 屏幕显示颜色字符
19.2 利用栈倒序存储字符串
19.3 编程处理0号中断
20 附录
- 数据可以分为字符和数字两种类型,在内存中都以二进制形式存储,其中字符存储arscii码(一个字节),数字存储补码(长度由具体数字类型决定)
- 关于内存单元的地址和内容:地址表示在几个字节处,内容表示存储了什么字节