汇编语言第4版 王爽(一)
本书采用以8086CPU为中央处理器的PC机来进行学习。8086CPU常用而结构简洁,可以方便地进行实践,便于教学。纯粹的8086PC机已经不存在了,但是现在的任何一台PC机中的微处理器,只要是和Intel兼容的系列,都可以用8086的方式进行工作。
附注1
微机中常用的Intel系列微处理器的主要发展过程是:8080,8086/8088,80186,80286,80386,80486,Pentium,Pentium II,Pentium III, Pentium4.
8086/8088是一个重要的阶段,它们略有区别,功能相同。8088被IBM用在它所生产的第一台微机上,该微机的结构事实上成为以后微机的基本结构。
80386是第二个重要的型号,随着微机应用及性能的发展,在微机上构造可靠的多任务操作系统的问题日益突出。人们希望自己的PC机能够稳定地同时运行多个程序,同时处理多项工作,或将PC机用作主机服务器,运行UNIX那样的多用户系统。
8086/8088不具备实现一个完善的多任务操作系统的功能。为此Intel开发了80286,80286具备了对多任务系统的支持。但对8086/8088的兼容却做得不好。这妨碍了用户对原8086机上的程序的使用。IBM最早基于80286开发了多任务系统OS/2,结果犯了一个战略错误。
随后Intel开发了80386微处理器,可以在3个模式下工作。
- 实模式:工作方式相当于一个8086
- 保护模式:提供支持多任务环境的工作方式,建立保护机制(这与VAX等小型机类似)。
- 虚拟8086模式:可从保护模式切换至其中的一种8086工作方式。这种方式的提供使用户可以方便地在保护模式下运行一个或多个原8086程序。
80286的缺陷在于,它只提供了实模式和保护模式,没有提供虚拟8086模式。这使得基于80286构造的多任务系统,不能方便地运行原8086系统中的程序。如果运行原8086系统中的程序,需要重启计算机,使CPU工作在实模式下才行。
从80386到当前的CPU,提供8086实模式的目的是为了兼容。现今CPU的真正有效力的工作模式是支持多任务操作系统的保护模式。
基础知识
汇编语言的组成
- 汇编指令:机器码的助记符,有对应的机器码
- 伪指令:没有对应的机器码,由编译器执行,计算机并不执行。
- 其他符号:如+、-、*、/等,由编译器识别,没有对应的机器码。
8088的数据总线宽度是8,8086的数据总线宽度是16。8088送2字节数据第一次送低字节,第二次送高字节。
检测点1.1
1、13
2、1024 0 1023
3、2^13 2^10
4、2^30 2^20 2^10
5、64 1 16 4
6、1 1 2 2 4
7、512 256
8、二进制
扩展插槽通过总线和CPU相连,接口卡插在扩展插槽上。通过总线,CPU可以直接控制这些接口卡,从而实现CPU对外设的间接控制。简而言之,就是CPU通过总线向接口卡发送命令,接口卡根据CPU的命令控制外设进行工作。
CPU在操控这些存储器时,把它们都当做内存来对台,把它们总的看作一个由若干存储单元组成的逻辑存储器,这既是我们所说的内存地址空间。学习汇编时,我们所面对的是内存地址空间。
每个物理存储器在这个逻辑存储器中占有一个地址段,即一端地址空间。CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。
假设,上图中的内存地址空间的地址段分配如下
地址0~7FFFH的32KB空间为主随机存储器的地址空间;
地址8000H~9FFFH的8KB空间为显存地址空间
地址A000H~FFFFH的24KB空间为各个ROM的地址空间。
内存地址空间的大小受CPU总线宽度的限制。
8086PC机内存地址空间分配的基本情况,8086的地址总线宽度为20,寻址空间为1MB
寄存器
通用寄存器
8086CPU有14个寄存器,分别是AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。
8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、BX、CX、DX是通用寄存器。
8086CPU的上一代CPU中的寄存器都是8位的,为了保证兼容,是原来基于8086CPU编写的程序稍加修改就可以运行在8086CPU上,故通用寄存器都可以分为两个可独立使用的8位寄存器来用:如,AX可分为AL和AH。
字:记为word,由两个字节组成,分别称为这个字的高位字节和低位字节。
在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的。两个操作对象的位数不一致的指令是错误的。
检测点2.1
AX=F4A3H
AX=31A3H
AX=3123H
AX=6246H
BX=826CH
CX=6246H
AX=826CH
AX=04D8H
AX=0482H
AX=6C82H
AX=D882H
AX=D888H
AX=D810H
AX=6246H
mov ax,2
add ax,ax
add ax,ax
add ax,ax
8086是16位机,也可以说8086是16位结构的CPU。
概括地讲,16位结构(16位机,字长为16位)描述了一个CPU具有下面几个方面的结构特性:
- 运算器一次最多可以处理16位的数据;
- 寄存器的最大宽度为16位;
- 寄存器和运算器之间的通路为16位。
在8086内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,对于16位CPU,能一次性处理、传输、暂时存储16位的地址。
8086CPU有20位地址总线,可以传送20位地址,达到1MB寻址能力,内部又是16位结构。8086CPU采用一种内部用两个16位地址合成的方法来形成一个20位的物理地址。
地址加法器采用物理地址=段地址X16+偏移地址的方法用段地址和偏移地址合成物理地址。
8086CPU的这种寻址功能是“基础地质+偏移地址=物理地址”寻址模式的一种具体实现方案。8086CPU中,段地址X16可看作是基础地址。
段的概念
内存并没有分段,段的划分来自于CPU。段的大小也来自于划分,可大可小。
将若干地址连续的内存单元看作一个段,用段地址X16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
有两点需要注意,段地址X16必然是16的倍数,所以一个段的起始地址也一定是16的倍数,偏移地址为16位,16位地址的寻址能力为64KB,所以一个段的长度最大为64KB。
检测点2.2
00010H 1000FH
1001H 2000H
段地址小于1001H或大于2000H
段寄存器
4个段寄存器:CS,DS,SS,ES
8086PC机中,任意时刻,CPU将CS:IP指向的内容当做指令执行。
读取一条指令后,IP中的值自动增加,以使CPU可以读取下一条指令,IP中增加的值为读取的指令的长度(字节数)。例如读入B82301,长度为3个字节,IP中的值加3
8086CPU的工作过程可以简要描述如下。
(1)从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP=IP+所读取指令的长度,从而指向下一条指令;
(3)执行指令,转到步骤(1),重复这个过程。
在8086CPU加电启动或复位后(即CPU刚开始工作时)CS和IP被设置为CS=FFFFH,IP=0000H。
指令FFFF0H
FFFF0h与07C00h,这两个都是机器启动后默认访问的内存地址。
首先要知道bios是用来初始化硬件的最底层的软件(然后才是操作系统),因此计算机启动后必须最先被执行。另外我们都知道CPU只能执行内存中的内容的,而一般内存中的数据是易失性的,断电之后内容就会消失。工程师的解决方法是:将存放bios的rom芯片与内存芯片统一编址(不明白的话去看看微机原理与接口的书就明白了)。这样就可以把存放bios的ROM芯片看作是数据永远不会消失不允许被更改的内存了。
开机启动后默认的CS=FFFFh IP=0000h。这个地址就是bios的地址。这段内存空间很小,所以不能够容下操作系统等大型程序。
相对bios而言操作系统的功能更强大,更新也更快但是也需要更多的空间,通常放在硬盘中。但是如果没有bios的话,那么将会连硬盘都不能使用,又如何启动存放在硬盘中的操作系统呢?正是由于此,机器启动后自动执行bios使其它完成硬件初始化(这样包括硬盘在内的cpu以外的其他硬件设备就可以工作了)。bios完成硬件初始化的任务后,就要把权力移交给操作系统。
工程师进行了强制性的规定:到内存中的07c00h 处寻找系统的引导程序,即CS=0000h IP=7c00h。也就是说任何系统,他的引导程序都必须安排在07c00h开始的地方,否则就不能被正确的引导。当引导程序完成后我们就进入了Linux Windows等系统了。
伪指令org用来规定目标程序存放单元的偏移量。比如,如果在源程序的第一条指令前用了如下指令:
org 200h
那么,汇编程序会把指令指针的ip的值设成200h,即目标程序的第一个字节放在200h处,后面的 内容则顺序存放,除非遇上另一个org 语句
修改CS、IP的指令
mov指令不能用于设置CS、IP的值,因为8086CPU没有提供这样的功能。能够改变CS、IP内容的指令被统称为转移指令。
可以用jmp 段地址:偏移地址来修改CS、IP的内容。如果想仅修改IP的内容,可用形如jmp 某一合法寄存器的指令完成。
代码段
对于8086PC机,在编程时,可以根据需要,将长度为N(N<=64KB)的一组代码,存在一组地址连续、起始地址为16的倍数的内存单元中。我们可以认为,这段内存时用来存放代码的,从而定义了一个代码段。
为了使代码段中的指令被执行,我们需要设置CS、IP的值,使其指向代码段的第一条指令。
检测点2.3
4次
每次读取指令后和执行完jmp指令后
0
Debug
Debug是DOS、Windows都提供的实模式(8086方式)程序的调试工具。使用它可以查看CPU各种寄存器中的内容、内存的情况和在机器码机跟踪程序的运行。
我们用到的Debug功能
- R命令查看、改变寄存器的内容
- D命令查看内存中的内容
- E命令改写内存中的内容
- U命令将内存中的机器指令翻译成汇编指令
- T命令执行一条机器指令
- A命令以汇编指令的格式在内存中写入一条机器指令。
- P命令将一个子程序调用指令、循环指令、中断指令或一个重复字符串指令,停止在下一条指令上。
在Windows中进入DOS方式,此时进入的是虚拟8086模式的DOS。 现在的Windows10默认已经移除了这个插件,所以使用dosbox。
安装dosbox和汇编工具
链接:https://pan.baidu.com/s/1UwSpoh9mU0PtJ00BF63zRA
提取码:hiaj
- 下载后解压并安装dosbox,我安装在e:\dosbox目录下
- 将asm文件夹移到e:\dosbox目录下
- 打开dosbox,输入
mount c E:\dosbox\asm
C:
或者找到下图中所在位置的conf文件,将上述两条代码加到最后。
寄存器(内存访问)
字单元
存放一个字型数据(16位)的内存单元,由两个连续的内存单元组成。高地址内存单元存放字型数据的高位字节,低地址单元中存放字型数据的低位字节。我们将起始地址为N的字单元简称为N地址字单元。
DS和[address]
8086CPU不支持将数据直接送入段寄存器的操作,所以将段地址送入ds要进过通用寄存器中转。
mov指令支持将数据直接送入寄存器(非段寄存器),将一个寄存器中的内容送入另一个寄存器,将内存单元中的数据送入一个寄存器(可以是段寄存器),将寄存器(可以是段寄存器)中的数据送入内存单元,将一个寄存器的内容送入一个段寄存器,讲一个段寄存器的内容送入一个寄存器中
段寄存器不能作为add/sub指令的操作对象。
检测点3.1
AX=2662H
BX=E626H
AX=E626H
AX=2662H
BX=D6E6H
AX=FD48H
AX=2C14H
AX=0
AX=00E6H
BX=0
BX=0026H
AX=000CH
1,mov ax,6622H
2,jmp 0fff:0100
3,mov ax,2000H
4,mov ds,ax
5,mov ax,[0008]
6,mov ax,[0001]
数据和程序在计算机中都是以二进制的形式存放的,在区别数据和程序时,关键是看段地址,如果段地址是ds,则说明存放的是数据,如果段地址是CS,则说明内存存放的是指令。
栈
LIFO(Last In First Out,后进先出)8086CPU的入栈和出栈操作都是以字为单位进行的。
任意时刻,SS:SP指向栈顶元素。push和pop指令执行时,CPU从SS和SP中得到栈顶的地址。
push的过程,SP = SP - 2,将数据送入SS:SP指向的内存单元处,高字节送入高地址(靠近栈底),低字节送入低地址(靠近栈顶),SS:SP此时指向新栈顶。
pop的过程,将SS:SP指向的内存单元的数据送入寄存器或内存单元,SP=SP+2,此时SS:SP指向新的栈顶元素(原来栈顶元素的下面的一个元素)
栈空时,SS:SP指向栈空间最高地址单元的下一个单元,栈满时,SS:SP指向栈空间的最低地址单元,即栈顶元素的低地址单元。
8086CPU并没有记录栈顶上限和栈底的寄存器,我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排站的大小,防止入栈的数据太多而导致的超界;执行出栈操作的时候也要注意,防止栈空时继续出栈导致的超界。
8086CPU只知道栈顶在何处(SS:SP),而不知道安排的栈空间有多大,就像CPU只知道当前要执行的指令在何处(CS:IP),而不知道要执行的指令有多少。8086CPU的工作机理:它只考虑当前的状况:当前的栈顶,当前要执行的指令。
push、pop指令的操作对象可以是寄存器,内存单元,段寄存器。
pop、push等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。
栈段
一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么都不是。关键在于CPU寄存器的设置,即CS、IP、SS、SP、DS的指向。
检测点3.2
mov ax,2000H
mov ss,ax
mov sp,0010H
mov ax,1000H
mov ss,ax
mov sp,0000H
实验2
在使用debug的t指令逐指令运行程序时,发现运行修改栈段寄存器SS的指令,它的下一条指令mov sp,10也紧接着执行。
(1)略
(2)在2000:0 f内存中,存放着SS、CS、IP、标志寄存器等内容。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)