欢乐狗 发表于 2022-6-23 10:58:56

汇编语言访问寄存器和内存篇---02

汇编语言访问寄存器和内存篇---02



[*]CPU的组成
[*]

[*]寄存器是CPU内部的信息存储单元
[*]通用寄存器——以AX为例
[*]横看成岭侧成峰
[*]“字”在寄存器中的存储

[*]mov和add指令
[*]确定物理地址的方法
[*]

[*]物理地址
[*]8086CPU给出物理地址的方法
[*]“段地址×16+偏移地址=物理地址”的本质含义

[*]内存的分段表示法
[*]

[*]用分段的方式管理内存
[*]同一段内存,多种分段方案
[*]用不同的段地址和偏移地址形成同一个物理地址

[*]Debug
[*]

[*]启动Debug
[*]用R命令查看、改变CPU寄存器的内容
[*]用D命令查看内存中的内容
[*]用E命令改变内存中的内容
[*]用U命令将内存中的机器指令翻译成汇编指令
[*]用A命令以汇编指令的格式在内存中写入机器指令
[*]用T命令执行机器指令
[*]用Q命令退出Debug

[*]CS、IP与代码段
[*]

[*]两个关键的寄存器

[*]jmp指令
[*]内存中字的存储
[*]

[*]字单元

[*]用DS和实现字的传送
[*]

[*]字的传送
[*]案例

[*]DS与数据段
[*]

[*]对内存单元中数据的访问
[*]将123B0H~123BAH的内存单元定义为数据段
[*]用mov指令操作数据
[*]加法add和减法sub指令
[*]用DS和形式访问内存中数据段方法小结

[*]栈及栈操作的实现
[*]

[*]栈结构
[*]例:设将10000H~1000FH内存当作栈来使用……
[*]栈的操作
[*]push 指令和pop指令的执行过程
[*]执行入栈(push)时,栈顶超出栈空间
[*]执行出栈(pop)时,栈顶超出栈空间
[*]栈顶超界问题的解决
[*]栈的小结

[*]关于“段”的总结
[*]

[*]综合示例:按要求设置段并执行代码
[*]综合示例:三个段地址可以一样滴!


   本系列文章参考汇编语言第四版和汇编语言程序设计 贺利坚主讲整理而成
CPU的组成



[*] 运算器进行信息处理;
[*] 寄存器进行信息存储;
[*] 控制器协调各种器件进行工作;
[*] 内部总线实现CPU内 各个器件之间的联系
https://img-blog.csdnimg.cn/69e83d38de5c411d94e75d4c69a1de6a.png
寄存器是CPU内部的信息存储单元

https://img-blog.csdnimg.cn/5e762a71a93147df83c5fc9e8d469515.png
8086CPU有14个寄存器:


[*] 通用寄存器:AX、BX、CX、DX
[*] 变址寄存器:SI、DI
[*] 指针寄存器:SP、BP
[*] 指令指针寄存器: IP
[*] 段寄存器:CS、SS、DS、ES
[*] 标志寄存器:PSW
共性; 8086CPU所有的寄存器都是16位的, 可以存放两个字节。
通用寄存器——以AX为例

一个16位寄存器存储一个16位的数据, 最大值?


[*] 2的16次方-1
https://img-blog.csdnimg.cn/86242c09509144cf8282218edf38b2bf.png
例:在AX中存储18D


[*] 18D — 12H — 10010B
https://img-blog.csdnimg.cn/ba4df84b02e04088a31d9e4328568340.png
再例:在AX中存储20000D


[*] 20000D — 4E20H — 0100111000100000B
https://img-blog.csdnimg.cn/1be222412ec8438e8e8b33e3f3c15cc9.png
横看成岭侧成峰

问题: 8086上一代CPU中的寄存器都是8位 的,如何保证程序的兼容性?
https://img-blog.csdnimg.cn/086b156440b043a1b82b77396fc9a043.png
方案: 通用寄存器均可以分为两个独立的 8位寄存器使用
https://img-blog.csdnimg.cn/a9ea2bdadd6c4e9a9d01489521f1b541.png
细化:


[*] AX可以分为AH和AL
[*] BX可以分为BH和BL
[*] CX可以分为CH和CL
[*] DX可以分为DH和DL
“字”在寄存器中的存储

8086是16位CPU


[*] 8086的字长(word size)为16bit
一个字(word)可以存在一个16位寄存器中


[*] 这个字的高位字节存在这个寄存器的高8位寄存器
[*] 这个字的低位字节存在这个寄存器的低8位寄存器
https://img-blog.csdnimg.cn/cb856c1de61a468ab0dcb3f7c4d58958.png
mov和add指令

https://img-blog.csdnimg.cn/7b5b5eaf2e98448bb5947006a8216ac9.png
注:汇编指令不区分大小写
确定物理地址的方法

物理地址



[*] CPU访问内存单元时要给出内存单元的地址。
[*] 所有的内存单元构成的存储空间是一个一维的线性空间。
[*] 每一个内存单元在这个空间中都有唯一的地址,这个唯 一的地址称为物理地址。
事实


[*] 8086有20位地址总线,可传送20位地址,寻址能力 为1M。
[*] 8086是16位结构的CPU运算器一次最多可以处理16位的数据,寄存器 的最大宽度为16位。
[*] 在8086内部处理的、传输、暂存的地址也是16位,寻址能力也只有64KB!
问题:8086如何处理在寻址空间上的这个矛盾?!
https://img-blog.csdnimg.cn/57030b50c3dd4e9b868293e0ae5006a2.png
8086CPU给出物理地址的方法

8086CPU的解决方法


[*] 用两个16位地址(段地址、偏移地址) 合成一个20位的物理地址。
地址加法器合成物理地址的方法 : 物理地址=段地址×16+偏移地址
段地址 * 16=基地址左移4个二进制位
https://img-blog.csdnimg.cn/faec57c01f6747059cb76454aab3f367.png
演示:物理地址=段地址×16+偏移地址
https://img-blog.csdnimg.cn/503ddab48c814a94b52dd7aac1d3f195.png
注意: 并不是一个物理地址就可以决定一个段地址,而是当我们给出一个段地址后,通过适当调整,即加上一个偏移地址,就可以得到一个物理地址,看下面的例子:
https://img-blog.csdnimg.cn/6767340e315b4de8a710cc4b6ab82303.png
“段地址×16+偏移地址=物理地址”的本质含义

要解决的问题 : 用两个16位的地址(段地址、偏移地址), 相加得到一个20位的物理地址
本质含义 : CPU在访问内存时,用一个基础地址(段 地址×16)和一个相对于基础地址的偏移 地址相加,给出内存单元的物理地址。
内存的分段表示法

用分段的方式管理内存

8086CPU用“(段地址×16)+偏移地址=物理地址”的方 式给出内存单元的物理地址。
内存并没有分段,段的划分来自于CPU!!!
https://img-blog.csdnimg.cn/29c9d59e750742a1ad62c2dc58173f36.png
同一段内存,多种分段方案

https://img-blog.csdnimg.cn/73bb3cf8f7ef4cf88083382a37e47ab4.png
(1)段地址×16 必然是 16的倍数,所以一个段的起始地址也一定是16的倍数
(2)偏移地址为16位,16 位地址的寻址能力为 64K,所以一个段的长度最大为64K
用不同的段地址和偏移地址形成同一个物理地址

https://img-blog.csdnimg.cn/9e4486fcba484be29606fb0ee77bd34d.png
Debug

Debug是DOS系统中的著名的调试程序,也可以运行在windows系统实模式下。 :
使用Debug程序,可以查看CPU各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程 序的运行!
启动Debug

在DOS提示符下输入命令:debug
https://img-blog.csdnimg.cn/167c64687f39452cb31a3470270099c6.png
用R命令查看、改变CPU寄存器的内容



[*] R - 查看寄存器内容
[*] R 寄存器名 - 改变指定寄存器内容
https://img-blog.csdnimg.cn/f881cf89e1bd456b8ce45fb9167cc511.png
用D命令查看内存中的内容



[*] D - 列出预设地址内存处的 128个字节的内容
https://img-blog.csdnimg.cn/408482b84a6141938f2c0dae6069b8db.png
   段地址*16 + 偏移地址得到的是物理地址


[*] D 段地址:偏移地址 - 列出内 存中指定地址处的内容
https://img-blog.csdnimg.cn/7526faf105ae4186a04e71f6f888d31b.png
   我们可以手动选择查看某一个段地址和对应的偏移地址,默认显示的是预设的段地址


[*] D段地址:偏移地址 结尾偏移 地址 - 列出内存中指定地址范 围内的内容
https://img-blog.csdnimg.cn/2688e1fcf5ce4e5babfc90df086860ba.png
   2000是指定的段地址,0000是偏移地址,而f是显示多少个字节,默认显示128个,这里我们要求显示16个字节,2f就是48个字节,这里使用和显示的数值都是十六进制形式的
用E命令改变内存中的内容



[*] E 段地址:偏移地址 数据1 数据2 …
https://img-blog.csdnimg.cn/0a2ba96d3c5e4f4eb4449a65e19f6e05.png


[*] E 段地址:偏移地址
[*] 逐个询问式修改
[*] 空格 - 接受,继续
[*] 回车 -结束
https://img-blog.csdnimg.cn/221638d9760d4458999e5fde533c20bd.png
   如果采用询问式方式修改,那么每按一次空格,他都会给出下一个字节的内容,例如: 12. 然后我们需要在.后面给出需要替换掉当前字节的内容
用U命令将内存中的机器指令翻译成汇编指令

有汇编指令


[*] mov ax, 0123H
[*] mov bx 0003H
[*] mov ax, bx
[*] add ax, bx
对应的机器码为


[*] B8 23 01
[*] BB 03 00
[*] 89 D8
[*] 01 D8
e 地址 数据 - 写入
d 地址 - 查看
https://img-blog.csdnimg.cn/ea1b2cfda25b4dad87f444acfd6e184a.png
u 地址 - 查看代码
https://img-blog.csdnimg.cn/89bd6fc595c8476e82971c2095bee033.png
用A命令以汇编指令的格式在内存中写入机器指令

有汇编指令


[*] mov ax, 0123H
[*] mov bx, 0003H
[*] mov ax, bx
[*] add ax, bx
对应的机器码为


[*] B8 23 01
[*] BB 03 00
[*] 89 D8
[*] 01 D8
a 地址 - 写入汇编指令
https://img-blog.csdnimg.cn/1e5fa08631af48489c44e2afae419a30.png
向2000:100处写入上面汇编指令
d 地址 - 查看数据
https://img-blog.csdnimg.cn/5a04b861d5b344fab2d7666c824b91bb.png
u 地址 - 查看代码
https://img-blog.csdnimg.cn/b01c47c655b74c8698a1489d83db513a.png
用T命令执行机器指令

t - 执行CS:IP处的指令


[*] mov ax, 0123H
[*] mov bx, 0003H
[*] mov ax, bx
[*] add ax, bx
https://img-blog.csdnimg.cn/61d2d0b018004f839a5f0d493eb88ca1.png
先调整cs和ip寄存器初始值


[*] 指令指针寄存器: IP
[*] 段寄存器:CS
https://img-blog.csdnimg.cn/44f63fc4465c4d34a1d2fa21b232c13d.png
https://img-blog.csdnimg.cn/93513e1e91cc4119a05b823292332d5c.png
用Q命令退出Debug

q - 退出Debug
https://img-blog.csdnimg.cn/f34857032bee4826b4490aa9921255b1.png
CS、IP与代码段

两个关键的寄存器



[*] CS:代码段寄存器
[*] IP: 指令指针寄存器
[*] CS:IP:CPU将内存中CS:IP 指向的内容当作指令执行。
https://img-blog.csdnimg.cn/2f5771d3c37c45e09636a161cf2f095b.png


[*] CS提供段地址,16位
[*] IP可以理解为PC寄存器,提供的是偏移地址,也是16位
[*] 位址加法器对两个16位地址进行相加,然后得到一个20位地址
[*] 通过BUS地址总线,发送给MAR
[*] MAR从内存中取出该地址对应的指令,然后放入MDR
[*] MDR中的数据通过数据总线转交给CPU处理
[*] 因为,这里取出的指令是3个字节,因此这里IP自动加3
例示:在CS和IP指示下代码的执行


[*] 8086CPU当前状态:CS中内容为2000H,IP中内容为0000H
[*] 内存20000H~20009H处存放着可执行的机器代码
https://img-blog.csdnimg.cn/50a30ff0ba894b5d9838beefc31ec900.png
8086PC读取和执行指令演示
8086PC工作过程的简要描述:
(1)从CS:IP指向内存单元读取 指令,读取的指令进入指 令缓冲器;
(2)IP = IP + 所读取指令的长 度,从而指向下一条指令;
(3)执行指令。 转到步骤
(1),重复这个过程。
指令读取和执行的实证演示-Debug
用debug程序执行下面的代码


[*] mov ax, 0123H
[*] mov bx, 0003H
[*] mov ax, bx
[*] add ax, bx
a 地址 - 写入汇编指令
u 地址 - 查看代码
t - 执行CS:IP处代码
https://img-blog.csdnimg.cn/07cf17f59f9848c08838fe58da2d4a20.png
https://img-blog.csdnimg.cn/070bdf3bfb964072b56f742f383e6c16.png
https://img-blog.csdnimg.cn/7c235a8e8f95443dbeff89fe3d94e410.png
https://img-blog.csdnimg.cn/e546772ebf614620b86f09bc33fc3e59.png
问:内存中有数据 B8 23 01 BB 03 00 89 D8 01 D8, 究竟用作一般数据,还是用作指令?
答:CPU将CS:IP指向的内存单元中的内容看作指令!
jmp指令

修改CS、IP的指令:
事实:执行何处的指令,取决于CS:IP
应用:可以通过改变CS、IP中的内容,来控制CPU要执行的目标指令
问题:如何改变CS、IP的值?
方法1:Debug 中的 R 命令可以改变寄存器的值——r cs, r ip
Debug是调试手段,并非程序方式!
方法2:用指令修改
https://img-blog.csdnimg.cn/6380675b55e64a7d84ca680a4963ace8.png
方法3:转移指令 jmp
转移指令 jmp :


[*] 同时修改CS、IP的内容
jmp 段地址:偏移地址
jmp 2AE3:3

jmp 3:0B16 功能:用指令中给出的段地址修改CS,偏移地址修改IP。


[*] 仅修改IP的内容 jmp
某一合法寄存器
jmp ax (类似于 mov IP, ax) jmp bx 功能:用寄存器中的值修改IP。
问题分析
https://img-blog.csdnimg.cn/56d6af1d6f204ebba88329c6b616204c.png
从20000H开始,执行的序列是:
(1)mov ax,6622
(2)jmp 1000:3
(3)mov ax,0000
(4)mov bx,ax
(5)jmp bx
(6)mov ax,0123H
(7)转到第(3)步执行
内存中字的存储

事实:对8086CPU,16位作为一个字
问题: 16位的字存储在一个16位的寄存器中,如何存储?
回答:


[*] 高8位放高字节,低8位放低字节
问题: 16位的字在内存中需要2个连续字节存储,怎么存放?
回答:


[*] 低位字节存在低地址单元,高位字节存在高地址单元
例:20000D(4E20H)存放0、1两个单元,18D (0012H)存放在2、3两个单元
https://img-blog.csdnimg.cn/7d9543ae293247279a7f669dcce5c3ec.png
字单元

字单元:由两个地址连续的内存单元组成,存放一个字型数据(16位)
原理:在一个字单元中,低地址单元存放低位字节,高地址单元存放高位字节


[*] 在起始地址为0的单元中,存放的是4E20H
[*] 在起始地址为2的单元中,存放的是0012H
https://img-blog.csdnimg.cn/af55b3ec8e234d00823a4ff7674ea02e.png
问题:
(1)0地址单元中存放的字节型数据是( 20H)
(2)0地址字单元中存放的字型数据是( 4E20H)
(3)2地址单元中存放的字节型数据是( 12H )
(4)2地址字单元中存放的字型数据是( 0012H )
机器字长、存储字长、指令字长
用DS和实现字的传送

要解决的问题:CPU从内存单元中要读取数据
要求: CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址
原理: 在8086PC中,内存地址由段地址和偏移地址组成(段地址:偏移地址)
解决方案:DS和配合 ,用 DS寄存器存放要访问的数据的段地址 ,偏移地址用[…]形式直接给出
https://img-blog.csdnimg.cn/ca4a65a8d11b413dab819583e1c0f65a.png
   给出偏移地址后,会默认去DS寄存器获取段地址
https://img-blog.csdnimg.cn/c258b4fb84454aa0a92b2e82825f4b27.png
字的传送

8086CPU可以一次性传送一个字(16位的数据)



[*] mov bx, 1000H
[*] mov ds, bx
[*] mov ax, —> 1000:0处的字型数据送入ax
[*] mov ,cx —> cx中的16位数据送到1000:0处
案例

https://img-blog.csdnimg.cn/60d351c169194214bb0873e9bdaa9c16.png


[*] 先将1000:0处内存的前四个字节设置为上面给出的值
https://img-blog.csdnimg.cn/230e4144301045839dd32ea8193e248e.png


[*] 在默认的起始地址处,输入指令
https://img-blog.csdnimg.cn/7045600fd3f5430a98f384c1d353e37c.png
https://img-blog.csdnimg.cn/8f043e45f4cf4017a61dd903ba0e7896.png
https://img-blog.csdnimg.cn/f954e904f1c34cb39fd22cb909a53e53.png
后面同理,DS和配合,可以让我们通过从内存中快速读取一个字到指定寄存器中
DS与数据段

对内存单元中数据的访问

对于8086PC机,可以根据需要将一组内存单元定义为一个段。


[*] 物理地址=段地址×16+偏移地址
[*] 将一组长度为N(N≤64K)、地址连续、起始地址为16的倍数的内存单元当作专门存储数 据的内存空间,从而定义了一个数据段。
例:用123B0H~123B9H的空间来存放数据


[*] 段地址:123BH 起始偏移地址:0000H 长度:10字节
[*] 段地址:1230H 起始偏移地址:00B0H 长度:10字节
[*] …
处理方法:(DS): ( )


[*] 用DS存放数据段的段地址
[*] 用相关指令访问数据段中的具体单元,单元地址由指出
mov、add、sub...   将哪段内存当作数据段,段地址如何定,在编程时安排
将123B0H~123BAH的内存单元定义为数据段

https://img-blog.csdnimg.cn/a238dbabbaea4781bf9192a7ae0d1128.png
   AL是AX的低字节位
用mov指令操作数据

https://img-blog.csdnimg.cn/87e615a93976429b824f11632c7157c4.png
加法add和减法sub指令

https://img-blog.csdnimg.cn/ec9ee2fae4ba436584e9cab5eea5851e.png
用DS和形式访问内存中数据段方法小结

https://img-blog.csdnimg.cn/8a424b60ad134853ae5cfbf95630c362.png
(1)字在内存中存储时 ,要用两个地址连续的内存单元来存放,字的 低位字节存放在低地址单元中,高位字节存放再高地址单元中。
(2)用 mov 指令要访问内存单元,可以在mov指令中只给出单元的偏 移地址,此时,段地址默认在DS寄存器中。
(3)表示一个偏移地址为address的内存单元。
(4)在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器、 低地址单元和低8位寄存器相对应。
(5)mov、add、sub是具有两个操作对象的指令,访问内存中的数据段 (对照:jmp是具有一个操作对象的指令,对应内存中的代码段)。
(6)可以根据自己的推测,在Debug中实验指令的新格式。
栈及栈操作的实现

栈结构

栈是一种只能在一端进行插入或删除操作的数据结构。
栈有两个基本的操作:入栈和出栈。


[*] 入栈:将一个新的元素放到栈顶
[*] 出栈:从栈顶取出一个元素。
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈 中取出。
栈的操作规则:LIFO(Last In First Out,后进先出)
CPU提供的栈机制:
现今的CPU中都有栈的设计。
8086CPU提供相关的指令,支持用栈的方式访问内存空间。
基于8086CPU的编程,可以将一段内存当作栈来使用。
PUSH(入栈)和 POP(出栈)指令

push ax:将ax中的数据送入栈中

pop ax:从栈顶取出数据送入ax

(以字为单位对栈进行操作) 例:设将10000H~1000FH内存当作栈来使用……

入站:
mov ax,0123H
push ax
mov bx,2266H
push bx
mov cx,1122H
push cx https://img-blog.csdnimg.cn/1b0f2e43b2d7489e9bf97858a64b89f6.png
出栈:
pop ax
pop bx
pop cx https://img-blog.csdnimg.cn/7fc3322223cd4b84a6106d5df962ad48.png
问题:
1、CPU如何知道一段内存空间被当作栈使用?
2、执行push和pop的时候,如何知道哪个单元是栈顶单元?
回答:
8086CPU中,有两个与栈相关的寄存器:


[*] 栈段寄存器SS - 存放栈顶的段地址
[*] 栈顶指针寄存器SP - 存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶元素。
栈的操作

https://img-blog.csdnimg.cn/705e7350900e4773bda707b217c55180.png
push 指令和pop指令的执行过程

push ax


[*] (1)SP=SP–2;
[*] (2)将ax中的内容送入SS:SP指向的内存单 元处,SS:SP此时指向新栈顶
pop ax


[*] (1)将SS:SP指向的内存单元处的数据送 入ax中;
[*] (2)SP = SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的 栈顶。
栈顶超界问题:
如何能够保证在入栈、出栈时,栈顶不 会超出栈空间?
执行入栈(push)时,栈顶超出栈空间

https://img-blog.csdnimg.cn/03a576c03cc7456dafe4c52082a4503a.png
执行出栈(pop)时,栈顶超出栈空间

https://img-blog.csdnimg.cn/caf69946e02246ed93420edb7b622149.png
栈顶超界问题的解决

https://img-blog.csdnimg.cn/93bbd2d2dfb543b28927a944c0b4ca1e.png
栈的小结

push、pop 实质上就是一种内存传送指令,可以在寄存器和内存 之间传送数据,与mov指令不同的是,push和pop指令访问的内 存单元的地址不是在指令中给出的,而是由SS:SP指出的。
执行push和pop指令时,SP 中的内容自动改变。
8086CPU提供的栈操作机制:


[*] 在SS,SP中存放栈顶的段地址和偏移地址,入栈和出栈指 令根据SS:SP指示的地址,按照栈的方式访问内存单元。
[*] push指令的执行步骤:
1)SP=SP-2
2)向SS:SP指向的字单元中送入数据。

[*] pop指令的执行步骤:
1)从SS:SP指向的字单元中读取数据
2)SP=SP-2。 关于“段”的总结

基础: 物理地址=段地址×16+偏移地址
做法:


[*] 编程时,可以根据需要将一组内存单 元定义为一个段。
[*] 可以将起始地址为16的倍数,长度为 N(N ≤64K )的一组地址连续的内存 单元,定义为一个段。
[*] 将一段内存定义为一个段,用一个段 地址指示段,用偏移地址访问段内的 单元——在程序中可以完全由程序员 安排。
三种段


[*] 数据段: 将段地址放在 DS中,用mov、add、sub等访问内存单元的指令 时,CPU将我们定义的数据段中的内容当作数据段来访问
[*] 代码段: 将段地址放在 CS中,将段中第一条指令的 偏移地址放在IP中 ,CPU将执行我们定义的代码段中的指令
[*] 栈段: 将段地址放在SS中,将栈顶单元的偏移地 置放在 SP 中 , CPU在需要进行栈操作(push、pop)时,就 将我们定义的栈段当作栈空间来用。
综合示例:按要求设置段并执行代码

https://img-blog.csdnimg.cn/2c3e1537caeb4c979a5bd31d84a410ee.png
综合示例:三个段地址可以一样滴!

https://img-blog.csdnimg.cn/dc1f8f97de6041b1b388771ac97219cc.png

来源:https://blog.csdn.net/m0_53157173/article/details/125304122
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 汇编语言访问寄存器和内存篇---02