搞了那么久的博客现在终于可以开始汇编语言的学习了
这里我用的教材是王爽老师的《汇编语言》,使用的环境是基于DOSBOX的模拟DOS环境
接下来开始正式的学习
今天的内容是 寄存器 和 实验一
寄存器
8086CPU共有14个寄存器,每个寄存器都有对应的名称,他们分别是:
AX
,BX
,CX
,DX
,SI
,DI
,SP
,BP
,IP
,CS
,SS
,DS
,ES
,PSW
我们不一次性的对其进行研究,我们在后续对这些寄存器的作用进行一一的讲解
通用寄存器
AX,BX,CX,DX 这四个寄存器用来放一般性的数据,所以被称为通用寄存器
8086的所有寄存器都是16位的,可以存放两个字节
为了保证CPU的兼容性,这四个通用寄存器都可以被分为两个可独立使用的八位寄存器:
- AX = AH + AL
- BX = BH + BL
- CX = CH + CL
- DX = DH + DL
XL 指的是 XX寄存器的低八位(0-7位),XH 指的是 XX 寄存器的高八位(8-15位)
字在寄存器中的存储
出于对兼容性的考虑,8086CPU可以对以下两种尺寸的数据进行操作:
- 字节(Byte):一个字节由八位组成,可以存储在八位寄存器中
- 字(Word): 一个字由两个字节组成,这两个字节分别被称为这个字的高位字节和地位字节
一个字可以存在一个16位寄存器中,那么这个字的高位字节和低位字节便存储在这个寄存器的高8位寄存器和低8位寄存器中
基本的汇编指令
介绍 mov 与 add
这里我们用几个例子来展示他们的用法
MOV:
- mov ax,18 -> 将18送入寄存器ax中
- mov ah,15 -> 将15送入寄存器ah中
- mov ax,bx -> 将寄存器bx中的值送入寄存器ax
ADD:
- add ax,8 -> 将寄存器ax中的值加上8
- add ax,bx -> 将ax和bx的值相加,并将结果保存在ax中
注意:
十六位寄存器的溢出:
当ax = bx = 8226H时,执行 add ax,bx 后ax = ?
ax本来应该等于 1044CH,但是由于最高位溢出了,所以ax = 044CH
八位寄存器的溢出:
当 ax = 00C5H时,执行 add al,93H 后al = ?
ax本来应该等于 0158H,但是由于最高位溢出了,所以ax = 0058H,al = 58H
8086给出物理地址的方法
所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间都有唯一的地址,我们将这个唯一的地址称为物理地址。在CPU发出物理地址之前,必须要在内部先生成这个地址
8086CPU有20位地址总线,最多可以传20位地址,达到1MB的寻址能力
可是8086是16位机器,按道理只能做到16位的64KB寻址,是怎么做到的呢?
这是因为我们使用了一种通过两个十六位地址合成一个二十位地址的方法
当8086CPU要读写内存时:
- CPU相关部件提供两个地址:一个是16位段地址,一个是16位偏移地址
- 这两个地址通过内部总线传输到地址加法器合成为一个20位的物理地址
- 将这20位的物理地址传输到地址总线上
在这里地址加法器通过 **物理地址 = 段地址*16 + 偏移地址** 生成20位的物理地址
注意:段地址*16 本质上就是将段地址左移四位,将地址XXXXH变为XXXX0H
段的概念
在根据编程需要时我们将若干连续的内存单元看作一个段
先讲解一下段的含义,在这里,段并不意味着内存被分为一段一段,而是我们可以通过分段的方式来管理内存
段的划分来源于CPU ,由于我们使用 **物理地址 = 段地址*16 + 偏移地址 ** 的方式给出内存的物理地址
所以我们用 段地址*16 定位段的起始地址(基础地址),用偏移地址定位段中的内存单元
我们需要注意:
- 段地址*16,即一个段的起始地址一定是16的倍数
- 偏移地址为16位,即其最大寻址位置为64KB,所以说一个段的最大长度为64KB
段寄存器
8086 有四个段寄存器CS、DS、SS、ES,这里我们先只看CS
其中CS,IP 是8086CPU中最重要的两个寄存器,他们指示了CPU当前要读取的指令的地址
CS 为代码段寄存器,IP为指令指针寄存器,我们可以这样理解,在8086机中任意时刻,CPU将CS:IP指向的内容当作指令执行
我们可以把8086CPU的工作过程表述为:
- 从 CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲区
- IP = IP + 所读取的指令的长度,从而指向下一条指令
- 执行指令,并返回步骤(1),重复这个过程
我们可以这么说,内存中的一段信息被CPU执行过,那么他所在的内存单元一定被CS:IP指向过
修改CS、IP指令
8086CPU大部分的寄存器的值都可以通过mov来改变,但是mov不能用于修改CS:IP 的值,mov被称为传送指令
能够修改CS:IP的值的指令通称为转移指令,如简单的jmp指令
- 若想同时修改CS、IP的值,我们可以使用指令 jmp 段地址:偏移地址 的指令完成
- 若想修改仅IP的内容,我们可以使用指令 jmp
某一合法的寄存器 的指令完成,这一步可以抽象理解为
mov IP 该寄存器中的值
第一条指令
在8086CPU加电启动或者复位后(即刚开始工作时),CS和IP被设置为
CS=FFFFH , IP=0000H
即在8086PC机刚启动时,CPU从内存FFFF0H单元中读取指令执行,FFFF0H单元存放着8086PC机开机后执行的第一条命令
实验一:查看CPU和内存,用机器指令和汇编指令编程
使用DEBUG
DEBUG 是 DOS,Windows 都提供的实模式(8086方式)程序的调试工具。可以用它查看CPU的各种寄存器中的内容、内存使用的情况和在机器码级跟踪程序的运行
我们需要用到以下的Debug功能:
- R:查看、改变CPU寄存器的内容
- D:查看内存中的内容
- E:改写内存中的内容
- U:将内存中的机器指令翻译成汇编指令
- T:执行一条机器指令
- A:以汇编指令的格式在内存中写入一条机器指令
R命令
1 | debug -r |
开启了debug的R模式

我们可以看到一个用法 -r 寄存器名称 可以修改指定寄存器的值
D命令
1 | debug -d |
我们可以看出其显示的三部分:
- 左边部分是每行的起始地址
- 中间部分是从指定地址开始的128个内存单元的内容,注意每行中间的”-“,这是用来区分每行的前八个字节和后八个字节的标志
- 右边是每个内存单元中的数据对应可显示的ASCII字符,如果不可显示,则用”.”替代
其有以下三种用法:
- 一是 d 段地址:偏移地址 指定地址CS:IP,对其进行128字节的查看
- 二是 d 段地址 :偏移地址 结尾偏移地址 可以指定查看的范围 即从偏移地址到结尾偏移地址之间的内存空间
- 三是 d 直接查看,将列出Debug预设的地址处的内容
E命令
1 | debug -e |

我们可以通过E指令来进行内存空间的改写,其操作如下:
e 起始地址 数据1 数据2 数据3 ……这个操作对起始地址之后的内存空间进行覆盖
其中数据可以是字符,也可以是数值,甚至是字符串
或者我们可以使用提问式的方法来进行修改

有以下步骤:
- 首先,输入 e 起始地址 ,按Enter
- 我们从起始地址的第一个值开始,“.”之前的数值是该单元的原数值,之后则是要修改的数值
- 我们使用空格跳过当前单元(无论是否修改),按下Enter表示结束修改
U指令
我们使用E指令写入以下的一段汲取嘛,然后用U指令对其内容翻译为汇编指令
1 | b80100 mov ax,0001 |
我们看到D命令的输出可以分为三部分:
- 左边部分为指令占用的内存单元的起始地址和对应的机器码
- 右边部分为翻译后的汇编语言
T命令
现在我们尝试执行我们写的汇编语言,使用T命令执行一条或多条汇编指令
首先我们需要修改CS:IP 指向的命令 然后开始执行

我们可以看到执行结果符合我们呢的期望
A命令
前面我们使用E命令写入机器指令,这样很不方便,所以我们使用A指令直接写入指令
其效果如下:

我们看到再给出起始地址后可以直接进行编辑