本篇文章将继续讲解剩余的寄存器以及一些寻址的情况
书接上回,上节我们介绍了通用寄存器的构造以及一些简单的计算方法和简单的汇编指令,如mov add等. 通用寄存器AX,BX,CX,DX,可以按照高位和地位分为H和L,用来存放不同的数据
2.2 8086CPU给出物理地址的方法
首先我们知道8086CPU是有20位地址线的,因此能容纳1MB的寻址能力,加上8086的16位的结构,也就是2^16=64KB,如果地址只是从其内部简单的发出,毫无疑问的时 寻址能力只有64KB
因此8086CPU采用了一种全新的寻址方式:物理地址=段地址*16+偏移地址
也就是俗话说的段地址左移4位后,与偏移地址相加,我们知道段地址和偏移地址都是16位的,在段地址经过左移4位后,段地址就变成了20位,与16位的偏移地址相加之后,就变成了20位.
来演示一下这个过程:
段地址 | 偏移地址 | 变化 |
---|---|---|
1230 | 00C8 | 将段地址和偏移地址放入地址加法器中 |
1230 | 00C8 | 这里将段地址左移4位(*16) |
12300 | 00C8 | 变化后的样子 |
12300 | 00C8 | 进行相加之后 物理地址为:123C8 |
随后地址加法器就将物理地址123C8输出 | ||
其实际意义就是物理地址=基础地址+偏移地址 | ||
基础地址=段地址*16 | ||
那么什么是段地址呢? 内存似乎被划分成了一段一段的?固定死啦? | ||
实则不然 并没有被被动的划分成为一段一段的,这里说的段其实是比较自由的段 | ||
举个例子:内存里10000H-100FFH可以被看作一段,在这里面的地址10000H-1007FH也可以被看作一段,10080H-100FFH也可以被看成一段,所以可以根据自己编程的需要将连续的内存单元化成段,用段地址*16就可以获得基础地址,然后用偏移地址去确定其中的内存单元.既然基础地址是段地址左移4位去形成的,那么基础地址必然是16的倍数,所以一个段的其实地址也必然是16的倍数.偏移地址我们知道是16位的,其寻址能力是64KB,因此一个段的大小最大是64KB | ||
通过这个例子,差不多可以理解段的含义了:是可以通过便利编程的目的来给内存中一段连续的空间划分为段,由此可见,段是十分重要的,这么重要的东西当然有对应的寄存器 | ||
也就是段寄存器 |
2.3 段寄存器
以8086CPU为例,其有AX,BX,CX,DX四个通用寄存器,也有4个段寄存器:CS.DS.SS.ES,这四个寄存器是存放段地址的,当CPU想要访问内存时,由这四个寄存器提供内存单元的段地址,然后送到加法寄存器里去做左移操作,后与偏移地址相加得到物理地址.这里以CS段寄存器为例
2.3.1 CS和IP
CS和IP寄存器是CPU中非常重要且关键的寄存器,CS为代码段寄存器,IP为指令指针寄存器.
在8086中,任意时刻,假设CS中的内容为M,IP中的内容为N,那么8086CPU将从内存M*16+N单元开始,读取一条指令并且执行 换句话说,8086CPU将执行CS:IP指向的内容当作指令执行. 这里用 王爽老师的图片来进行讲解:
当前CS中的值为2000 IP中的值为0000 内存中20000-20002 存放着汇编指令为
mov ax,0123H
内存中20003-20005存放着:
mov bx,0003H
…以此类推
从头开始,CS经过左移4位后,变为20000 随后20000+IP的值组成物理地址 由20位地址总线去寻址 找到了内存中的20000,随后开始读取一条指令:B8,23,01后将其传送至数据总线,然后将其存入指令缓冲器中,这个时候IP变为03 也就是下一条指令的开始 IP+3 此时的CS:IP 2000:0003
以上就是CPU读取和执行指令的过程
那么来做个练习吧~ 来练习下下一步CPU读取的过程
答案:cs左移4+ip后 读取到20003 随后开始读取 BB 03 00这条指令后 ip=ip+3 此时CS:IP=2000:0006
2.3.2 修改CS和IP的指令
这里是jmp指令
jmp cs:ip
比如
jmp 2AE3:3
执行后 cs将变为2AE3H,IP为0003H CS:IP=2AE3:3 物理地址为 2AE33H
jmp 3:0B16
执行后 CS:IP=0003H:0B16H 物理地址为:0B46H
当然JMP指令还可以用作改变IP寄存器的值
JMP ax
假设执行指令前Ax的值为1000H CS为2000H IP为0003H
那么执行指令过后
ax | cs | ip |
---|---|---|
1000H | 2000H | 1000H |
不难想到 jmp一个通用寄存器/某一合法寄存器的指令实际上就是 |
mov IP ,AX
来做下练习
要注意的有一点:cpu是先读取再执行,再执行jmp 1000:3这条指令时,要注意 cpu先读取这条指令, 随后ip变为3+5=8 后执行jmp指令后 ip变为3
2.4 实验环节
2.4.1 环境配置
首先要准备环境 MSDOS (因为win11不支持dos环境了)
笔者这里使用dosbox来代替
首先准备好这两个环境
按照DosBOX后 找到文件夹中的
双击执行后,翻到下面
在这里输入Debug.exe的位置 如图所示
后面两行也要输入,保存关闭
然后执行
输入r查看寄存器的值,出现就代表环境配置成功!
2.4.2 开始实验 debug
使用"R"指令后可以看到寄存器的对应的值
我们目前学习到了AX,BX,CX,DX,CS,IP 这6个寄存器
来看下CS和IP的值: CS:073F IP:0100
CS:IP=073F:0100
也就是说下一步要执行的物理地址为:73F0+0100=74F0 这里debug中也指出了这里的值为0000
R指令也可以用来修改某个寄存器中的值
这里尝试修改ax的值
输入
r ax
这里使用r 指令将ax寄存器的值改为了666
接下来尝试修改cs和ip
这里笔者将cs和ip分别改为了0777 和3000
来使用D指令读取其内容
d cs:ip
可以让ip为0 来查看从cs开始的段的内容
这里由于笔者自身的操作失误 导致了其内容出现了些变化 ,接下来重新开始运行软件
使用d指令查看1000:0处的内容
d 1000:0
左侧时每行的起始地址 中间是内存中的内容 右侧是 ASCII码
继续键入d可以让程序继续的顺着地址执行下去
假如想要查看 1000:0 0 到 1000:0 9的位置
就直接输入
d 1000:0 9
可以用e指令来改写内存中的内容
e 地址处 数值 数值 数值...
也可以使用
e 地址
然后输入
当然 也可以进行 输入 字符
也可以写入机器码
那么写入的是什么意思呢?该如何查看呢?
使用u指令去查看
u 地址
实际上写入的
我们使用t指令来执行下
执行前使用r指令查看下对应寄存器的值
可以看到对应的值 AX:0000 BX:0000 CS:073F IP:0100
而我们在 1000:0 0 到 1000:0 3 处指令为 mov ax 0001
使用r指令修改 cs和ip使其指向1000:0
r cs
r ip
使用t指令
可以看到 我们执行了 mov ax 0001 并且 在执行完后 ip为03 因为 mov指令占了03 所以 ip=ip+3
让我们来继续执行
t
ip=IP+3 后 cx也执行了
当然 我们也可以使用A指令去直接将汇编语言写入内存中
让我们修改ip 使其重新执行 查看效果
ax改为了1 且IP为3
继续t
这里执行了 add指令
所有的指令执行完毕,且符合预期!
2.4.3 总结
指令的作用:
指令 | 作用 |
---|---|
R | 查看修改对应寄存器的内容 |
D | 查看对应内存中的内容 |
E | 修改对应内存中的内容 包括写入字符 数字 字符串 |
U | 将内存中的内容解释为对应的汇编指令 |
T | 执行对应内容中的指令(CS:IP) |
A | 以汇编的形式写入内存 |
2.4.4 来做练习吧~
编写汇编指令 计算2的8次方
答案很明显了
mov ax,1
add ax,ax
jmp 2000:0003
将其写入2000:0000即可
查看ROM的生产日期,在FFF00H-FFFFFH处
其实很简单,先找到对应的物理地址:FFF00H 将其拆解为 5位+4位的形式 即