《汇编语言(第四版)》—王爽 第三章寄存器(内存访问) 详细笔记 ~后续章节笔记,课后检测,实验代码持续更新中
第三章寄存器(内存访问)
3.1、内存中字的存储
内存中从上到下依次为低位—>高位
任何两个地址连续的内存单元,N号单元和 N+1号单元,可以将它们看成两个内存单元,也可看成一个地址为N的字单元中的高位字节单元和低位字节单元。
例如:
解答:
3.2、DS和[address]
CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址
在8086中,内存地址是由段地址和偏移地址组成的
8086CPU中有一个DS寄存器,通常用来存放要存放的数据的段地址
可以使用mov指令将一个内存单元中的内容送入一个寄存器中。从哪一个内存单元送到哪一个寄存器中呢?在指令中必须指明。寄存器用寄存器名来指明,内存单元则需用内存单元的地址来指明。显然,此时mov指令的格式应该是: mov寄存器名,内存单元地址。
【…】表示的是一个内存单元,【…】中的表示内存单元中的偏移地址,在执行指令是8086会自动取ds中的数据为内存单元的段地址
8086CPU不支持将数据直接送入到段寄存器的操作,DS是一个段寄存器(硬件设计的问题),因此mov ds,1000H是非法的
数据–>通用寄存器–>段寄存器
将数据从寄存器送入到内存单元
mov bx,1000H
mov ds,bx
mov [0]al #重点在于a'l[0] 和 [0]al的区别
3.3、字的传送
8086为16位的结构,有16根数据线,所以可以一次性传送16位的数据,也就是一次性传送一个字
例题:注意8086传送的是16位的字即两个字节,分为高8位和低8位
解答:
例题:注意16位,替换高位的内容,不要只替换低位而忽略高位
3.4、mov、add、sub指令
这样的写法是正确的,书本中出现了错误
mov 寄存器,段寄存器 表示的是将段寄存器中的数据给通用寄存器
add和sub是否能对段地址寄存器进行操作?(事实证明–不可以)
3.5、数据段
对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。
我们可以将一组长度为N(N≤64KB)、地址连续、起始地址为16 的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
比如用123BOH123B9H这段内存空间来存放数据,我们就可以认为,123B0H123B9H 这段内存是一个数据段,它的段地址为123BH,长度为10个字节。
要选择低位进行操作,不然按照8086的16位数据AX寄存器会自动补全高位,AL是按照低位的8位进行的
这个问题问的是累加字型而非上一个问题中的单元
3.1~3.5 小结
- 字在内存中存储时,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,,高位字节存放在高地址单元中。
- 用mov 指令访问内存单元,可以在mov指令中只给出单元的偏移地址,此时,段地址默认在DS寄存器中。
- [address]表示一个偏移地址为address的内存单元。
- 在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应。
- mov、add、sub是具有两个操作对象的指令。jmp是具有一个操作对象的指令。
- 可以根据自己的推测,在Debug 中实验指令的新格式。
检测点例题说明
重点:CS和IP是用了来寻找指令的
DC以及[偏移量]是用来寻找数据的
3.6、栈
栈是一种具有特殊的访问方式的存储空间。它的特殊性在于,最后进入这个空间的数据最先出去
栈的两个基本操作:入栈和出栈
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出
栈的操作原则:LIFO(后进先出)
3.7、CPU提供的栈机制
8086CPU提供相关的指令来以栈的方式访问内存空间,因此基于8086CPU编程时,可以将一段内存当作栈来使用
8086提供的入栈出栈的指令(最基本):push(入栈) pop(出栈)
push ax:将寄存器ax中的数据送入到栈中
pop ax:从栈顶取出数据送入到ax中
pop 指令就是取栈顶的数据
8086CPU的入栈和出栈都是以字为单位进行的
一段指令的执行过程,注意内存空间从哪里开始存储,是从这段空间中相对较大处开始的,符合栈先进后出的规则
存在两个问题:
- CPU如何知道一段内存空间被当作栈来使用?
- 8086CPU中,有两个寄存器
- 段寄存器SS 存放栈顶的段地址
- 寄存器SP 存放栈顶的偏移地址
- 任何时候,SS:SP指向栈顶元素
- 8086CPU中,有两个寄存器
- 执行push和pop的时候,如何知道哪个单元是栈的顶部?
- 执行push时
- SP = SP - 2
- 将ax中的内容送入到SS:SP指向的内存单元
- SS:SP此时为新的栈顶
- 执行pop时
- 将SS:SP指向的内存单元中的内容送入到ax
- SP = SP + 2
- SS:SP此时为新的栈顶
- 执行push时
当栈空时,SP指向的时最高地址的下一个单元(任何时刻,SS:SP指向栈顶元素,当栈为空的时候,栈中没有元素,也就不存在栈顶元素 ,所以SS:SP只能指向栈的最底部单元的下面一个单元)
栈最底部 字 单元的地址为1000:000E,则栈为空时,SP为000E + 2 ------->0010H
在我们执行pop命令时,数值复制到寄存器中,在栈中该数据还是存在的,等待下一次执行push时将这个数值覆盖掉了
利用编译语言来理解栈:
- 在我们调用函数时,会利用部分变量的数据,但是不能改变这些原有的变量的数值,因此利用到栈,先将数据全部读取到栈中,然后函数进行运算,做完一系列操作之后,再将栈中的数据取出,来还原变量
- 局部变量只在局部起作用,则局部变量也是放在栈中
3.8、栈顶超界的问题
SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶
栈顶超界问题:在栈满时继续使用push命令,在栈空时继续使用pop命令
栈顶超界时很危险的
8086CPU不保证对栈的操作不会越界,8086CPU只知道栈顶在何处,而不知道安排的栈的空间有多大
我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。
3.9、push和pop指令
push和pop指令时能够在寄存器和内存之间传送数据的
栈与内存:栈空间当然也是内存空间的一部分,他只是一段可以以一种特殊的方式进行访问的内存空间
push 寄存器:将寄存器中的数据放入栈中
push 段寄存器:将一个段寄存器中的数据入栈
push 内存单元:将一个内存单元处的字入栈(栈的操作都是以字为单位)
pop 寄存器:将栈中的数据放入寄存其中
pop 段寄存器:出栈,用一个段寄存器接受出栈的数据
pop 内存空间:出栈,用一个内存字单元接受出栈的数据 pop[2]
指令在执行时,CPU要知道内存的地址,可以在push、pop指令中给出内存单元的偏移地址,段地址在指令指向时,CPU从ds中取得
push、pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和 pop 指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的。同时,push和 pop指令还要改变SP中的内容
例题分析:(push和pop指令,还要改变SP的内容)
补充之后代码如下:
在检测时自我出现的问题:(需要格外注意的地方)
将SP的值置为了1,问题分析,对于栈的 + 2 以及 - 2 的方式还不是很熟悉,以及对于SS:SP永远指向栈顶理解不透彻
对于栈的知识注意的有:
- pop和push等栈操作指令,修改的只有SP的值,也就是说栈顶的最大变化范围为0~FFFFH(因为是16位)
3.10、栈段
对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。
我们可以将长度为N(N≤64KB)的一组地址连续、起始地址为 16的倍数的内存单元,当作栈空间来用,从而定义了一个栈段。
比如,我们将10010H~1001FH 这段长度为16字节的内存空间当作栈来用,以栈的方式进行访问。这段空间就可以称为一个栈段,段地址为1001H,大小为16字节。
将一段内存当作栈段,仅仅是我们在编程时的一种安排,CPU不会这样安排