本系列文章参考汇编语言第四版和汇编语言程序设计 贺利坚主讲整理而成
8086CPU有14个寄存器:
共性; 8086CPU所有的寄存器都是16位的, 可以存放两个字节。
一个16位寄存器存储一个16位的数据, 最大值?
例:在AX中存储18D
再例:在AX中存储20000D
问题: 8086上一代CPU中的寄存器都是8位 的,如何保证程序的兼容性?
方案: 通用寄存器均可以分为两个独立的 8位寄存器使用
细化:
8086是16位CPU
一个字(word)可以存在一个16位寄存器中
注:汇编指令不区分大小写
事实
问题:8086如何处理在寻址空间上的这个矛盾?!
8086CPU的解决方法
地址加法器合成物理地址的方法 : 物理地址=段地址×16+偏移地址
段地址 * 16=基地址左移4个二进制位
演示:物理地址=段地址×16+偏移地址
注意: 并不是一个物理地址就可以决定一个段地址,而是当我们给出一个段地址后,通过适当调整,即加上一个偏移地址,就可以得到一个物理地址,看下面的例子:
要解决的问题 : 用两个16位的地址(段地址、偏移地址), 相加得到一个20位的物理地址
本质含义 : CPU在访问内存时,用一个基础地址(段 地址×16)和一个相对于基础地址的偏移 地址相加,给出内存单元的物理地址。
8086CPU用“(段地址×16)+偏移地址=物理地址”的方 式给出内存单元的物理地址。
内存并没有分段,段的划分来自于CPU!!!
(1)段地址×16 必然是 16的倍数,所以一个段的起始地址也一定是16的倍数
(2)偏移地址为16位,16 位地址的寻址能力为 64K,所以一个段的长度最大为64K
Debug是DOS系统中的著名的调试程序,也可以运行在windows系统实模式下。 :
使用Debug程序,可以查看CPU各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程 序的运行!
在DOS提示符下输入命令:debug
段地址*16 + 偏移地址得到的是物理地址
我们可以手动选择查看某一个段地址和对应的偏移地址,默认显示的是预设的段地址
2000是指定的段地址,0000是偏移地址,而f是显示多少个字节,默认显示128个,这里我们要求显示16个字节,2f就是48个字节,这里使用和显示的数值都是十六进制形式的
如果采用询问式方式修改,那么每按一次空格,他都会给出下一个字节的内容,例如: 12. 然后我们需要在.后面给出需要替换掉当前字节的内容
有汇编指令
对应的机器码为
e 地址 数据 - 写入
d 地址 - 查看
u 地址 - 查看代码
有汇编指令
对应的机器码为
a 地址 - 写入汇编指令
向2000:100处写入上面汇编指令
d 地址 - 查看数据
u 地址 - 查看代码
t - 执行CS:IP处的指令
先调整cs和ip寄存器初始值
q - 退出Debug
例示:在CS和IP指示下代码的执行
8086PC读取和执行指令演示
8086PC工作过程的简要描述:
(1)从CS:IP指向内存单元读取 指令,读取的指令进入指 令缓冲器;
(2)IP = IP + 所读取指令的长 度,从而指向下一条指令;
(3)执行指令。 转到步骤
(1),重复这个过程。
指令读取和执行的实证演示-Debug
用debug程序执行下面的代码
a 地址 - 写入汇编指令
u 地址 - 查看代码
t - 执行CS:IP处代码
问:内存中有数据 B8 23 01 BB 03 00 89 D8 01 D8, 究竟用作一般数据,还是用作指令?
答:CPU将CS:IP指向的内存单元中的内容看作指令!
修改CS、IP的指令:
事实:执行何处的指令,取决于CS:IP
应用:可以通过改变CS、IP中的内容,来控制CPU要执行的目标指令
问题:如何改变CS、IP的值?
方法1:Debug 中的 R 命令可以改变寄存器的值——r cs, r ip
Debug是调试手段,并非程序方式!
方法2:用指令修改
方法3:转移指令 jmp
转移指令 jmp :
jmp 段地址:偏移地址
jmp 2AE3:3
jmp 3:0B16
功能:用指令中给出的段地址修改CS,偏移地址修改IP。
某一合法寄存器
jmp ax (类似于 mov IP, ax)
jmp bx
功能:用寄存器中的值修改IP。
问题分析
从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位的寄存器中,如何存储?
回答:
问题: 16位的字在内存中需要2个连续字节存储,怎么存放?
回答:
例:20000D(4E20H)存放0、1两个单元,18D (0012H)存放在2、3两个单元
字单元:由两个地址连续的内存单元组成,存放一个字型数据(16位)
原理:在一个字单元中,低地址单元存放低位字节,高地址单元存放高位字节
问题:
(1)0地址单元中存放的字节型数据是( 20H)
(2)0地址字单元中存放的字型数据是( 4E20H)
(3)2地址单元中存放的字节型数据是( 12H )
(4)2地址字单元中存放的字型数据是( 0012H )
要解决的问题:CPU从内存单元中要读取数据
要求: CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址
原理: 在8086PC中,内存地址由段地址和偏移地址组成(段地址:偏移地址)
解决方案:DS和[address]配合 ,用 DS寄存器存放要访问的数据的段地址 ,偏移地址用[…]形式直接给出
给出偏移地址后,会默认去DS寄存器获取段地址
8086CPU可以一次性传送一个字(16位的数据)
例
后面同理,DS和[address]配合,可以让我们通过从内存中快速读取一个字到指定寄存器中
对于8086PC机,可以根据需要将一组内存单元定义为一个段。
例:用123B0H~123B9H的空间来存放数据
处理方法:(DS): ( [address])
mov、add、sub...
将哪段内存当作数据段,段地址如何定,在编程时安排
AL是AX的低字节位
(1)字在内存中存储时 ,要用两个地址连续的内存单元来存放,字的 低位字节存放在低地址单元中,高位字节存放再高地址单元中。
(2)用 mov 指令要访问内存单元,可以在mov指令中只给出单元的偏 移地址,此时,段地址默认在DS寄存器中。
(3)[address]表示一个偏移地址为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
(以字为单位对栈进行操作)
入站:
mov ax,0123H
push ax
mov bx,2266H
push bx
mov cx,1122H
push cx
出栈:
pop ax
pop bx
pop cx
问题:
1、CPU如何知道一段内存空间被当作栈使用?
2、执行push和pop的时候,如何知道哪个单元是栈顶单元?
回答:
8086CPU中,有两个与栈相关的寄存器:
任意时刻,SS:SP指向栈顶元素。
push ax
pop ax
栈顶超界问题:
如何能够保证在入栈、出栈时,栈顶不 会超出栈空间?
push、pop 实质上就是一种内存传送指令,可以在寄存器和内存 之间传送数据,与mov指令不同的是,push和pop指令访问的内 存单元的地址不是在指令中给出的,而是由SS:SP指出的。
执行push和pop指令时,SP 中的内容自动改变。
8086CPU提供的栈操作机制:
1)SP=SP-2
2)向SS:SP指向的字单元中送入数据。
1)从SS:SP指向的字单元中读取数据
2)SP=SP-2。
基础: 物理地址=段地址×16+偏移地址
做法:
三种段