3.1 内存中字的存储
一个内存的存储单元 是 字节, 一个字 是两个字节
在0地址处开始存放2000 (十进制) 、 (4E20H)十六进制
‘ 我们要知道 高位对应高地址 ,地位对应低地址 两个16进制位 是一个字节
两个字节 是一个字
0 和 1 0是低地址, 1是高地址,
0地址 单元存放的字节型 数据是多少??? 20 十进制的32 没说什么单元就是字节单元
0地址 字单元存放的字节型 数据是多少??? 20000(十进制) 4E20H
一个字节 是一个内存单元!!! 所以是 一个数据
2地址 存放的字型 数据是多少??? 考虑两个字节 124EH
结论:
任何两个地址连续的内存单元,N号单元和 N+1号单元,可以将它们看成两个内存单元,也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元。
3.2 DS和[address]
1. CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址
就跟你遇到美女,要先问到电话,才能下一步联系
2.8086CPU中, 内存地址,由段地址,和 偏移地址组成 (段地址是起始地址 偏移地址指向下一位)
3.8086CPU 有一个DS寄存器, 通常用来存放,要访问的数据的段地址 DS是数据的意思
例子:
读取1000H 单元内容,用什么程序段?
mov bx,1000H bx 1000 ,ds运算 ds*16 向左移一位
mov ds,bx
mov al,[0]
mov al,[0] 我们 已知 的mov 指令 是完成两种操作,
1,将数据直接送入寄存器 mov ax,1000H
2,将一个寄存器中的内容,放到 另一个寄存器 中 mov ax,bx
现在 mov指令,还可以将一个内存单元中的内容放到 寄存器中 mov al,[0] 偏移地址0 内存单元0
ax bx 是 16个字节的 为了兼容之前的,拆成了ah , al 两个8字节的 (字节,位)
先用 ds 定位到段地址,1000的 内存中, 然后偏移地址放到al中
mov 指令的格式:
mov 寄存器名,内存单元
[ .... ] 表示一个内存单元, [0] 表示内存单元的偏移地址
内存单元的段地址是多少??
执行指令时,8086CPU自动取DS中的数据为内存单元的段地址。
8086CPU 不支持,将数据直接放到段寄存器的操作, ds 是一个段寄存器
要先 mov ax,1000H 然后 mov ds,ax
数据 ------->通用寄存器 ---------> 段寄存器
例题
写几条指令,将al中的数据放到内存单元10000H?(思考后分析)
怎样将寄存器放到内存单元?
mov bx,1000H(一个中介) mov ds,dx mov [0],al (寄存器放到内存)
ds:al这样吗?? 可以是ah,al 但是题目 是 al
3.3 字的传送
8086是16位结构,有个16根数据线,
因为8086CPU是16位结构,有16根数据线,
所以,可以一次性传送16位的数据,也就是一次性传送一个字。 一个字==两个字节
mov ax,[0] 是 ax是16位,所以放的是16位的 要是 ah ,al 就是8位
指令执行后寄存器ax,bx,cx中的值。
内存地址是连续的 -d 查看一下, 指定内存地址
然后让CS IP 指向 内存地址
-a 来在CS和IP里,写入汇编指令
首先看看 这个 CS 和 IP 段中,有没有其它指令,没有的话,可以直接使用
-a 073f:100
mov ax,1000
..................
-t执行
3.4 mov、add、sub指令
mov指令的形式,
mov 寄存器,数据 mov ax,6
mov 寄存器,寄存器 mov bx,ax
mov 寄存器,内存单元 mov ax,[0]
mov 内存单元,寄存器 mov [0],ax
mov 段寄存器,寄存器 mov ds,ax
mov 寄存器,段寄存器
mov 内存单元,段寄存器
mov 段寄存器,内存单元
ax的值变了。
这里推荐一个 汇编神器
汇编金手指。 这个软件, 各种指令的 解释 都有
可以直接改变 段寄存器的值吗?
比如 add ds,ax ------- 不能
3.5 数据段
一个段 ,最多的长度是16位。 偏移地址是4个F 的16位 ==64kb
2^16 =65536 (Byte)/ 1024= 64 kb
我们可以将一组长度为N (N S64K) 、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
比如 123B0 H ~ 123B9H 段地址123B 长度0-9 10字节
段地址1230 偏移地址B0 ~ B9
一个地址 指向 一个内存单元 == 一个字节 == 8位(bit)
为什么是8位??? 因为 我们发明了一个字 来 代替 字节
我们8086CPU 都是16位的,32 字 = 两个字节的16位。双字 4个 字节的32位,4字==64位
以前cpu是八位的,用的字节 ,现在16位 用字 32位 双字 64位 four word (4字)
一个字 单位 == 64 位
用 ds 存放数据段的段地址,再根据需用相关指令访问数据段中的具体单元
把 数据的段地址 放到 ds 中 再根据需要 写出偏移地址 , 就可以访问啦,真是太酷啦!!
我们将123B0H~123BAH的内存单元定义为数据段,累加这个数据段中的前3个单元中的数据,代码如下 : 一个单元就是一个字节
al 是 8位 就是 一个字节 ax的话16位 由于ax能存放两个字节,下面add命令会把下一个内存单元的值 存进高位,这样 加的就不是前三个单元了,而是前三个字,
mov ax,123BH
mov ds,ax 将123BH送入ds中,作为数据段的段地址
mov al,[0] 将数据段第一个单元 (偏移地址 0 ) 的数组 放到 al中
add al,[1] 将数据段第二个单元 (偏移地址 1 ) 的数组 放到 al中
add al,[2] 将数据段第三个单元 (偏移地址 2 ) 的数组 放到 al中
3.1 ~ 3.5 小结
(1) 字在内存中存储时,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放再高地址单元中。
(2)用 mov 指会要访问内存单元,可以在mov指令中只给出单元的偏移地址,此时,段地址默认在DS寄存器中。 试试偏移地址的, mov ax,1000:0 [0] 不行。。。
(3) [address]表示个偏移地址为address的内存单元。 段地址在 ds中
(4) 在内存和寄存器之间传送字型数据时高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应。
(5) mov、add、sub是具有两个操作对泉的指令。jmp是具有一个操作对泉的指令。
jmp 给一个偏移地址, 或 段地址
3.6 栈
栈!!!!!!, 一个空间,
栈,先进后出 , 压子弹理解
栈有两个基本的操作,入栈和出栈
入栈:将一个新的元素,放到栈顶,
出栈:从栈顶取出一个元素
栈顶的元素总是最后入栈,需要出栈时,又最先被栈中取出
这么理解 一个洞 a 先放到洞中, b在放进去,b压着a ,a要想先出去,必须让b先出去 a为栈底,c为栈顶, 所以出栈 c先出栈。
栈的操作规则: LIFO
先进后出 , 后出先进
3.7 cpu提供的栈机制
所有cpu都有栈设计,肯定
提供相关指令来 以栈的方式 访问内存空间
意味着,可以将一段内存当作栈来使用
8086CPU 提供入栈 和 出栈的指令(最基本的)
PUSH (入栈)
POP (出栈)
push ax :将寄存器ax中的数据 , 放到栈
pop ax :从栈顶取出数据放到 ax 中
入栈 和 出栈 操作都是以 字 为单位进行
mov ax,0123h
push ax
高地址栈底,低地址栈顶
ax 是1122H bx 2266H cs 是 0123H 先放进去,再拿出来
字型数据用两个内存单元存放,高地址单元放高 8 位,低地址里元放低8 位。
一个内存单元是8个字节 一个字型是16个字节 所以用两个
ebp 和 sp bp 栈底 sp栈顶
CPU如何指导当前要执行的指令所在的位置?
答 : 寄存器CS和IP中存放着当前指令的段地址和偏移地址。
还有两个寄存器,
段寄存器SS 存放 栈顶的 段地址
寄存器SP 存放栈顶的 偏移地址
任何时刻, SS:SP 指向栈顶元素
问题3.6:如果我们将10000H~1000FH 这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=? 0010H
最高地址单元的下一个单元 push ax ss:sp指向栈中第一个元素
push ax , sp = sp - 2 找 sp 的位置,
栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2,SP 原来为000EH,加 2后SP=10H
栈的最大地址 为 1000:000F 超出不是 栈底元素
换个角度看 :
任意时刻,SS:SP 指向栈顶元素,当栈为空的时候,栈中没有元素,也就不存在栈顶元素
所以SS:SP 只能指向栈的最底部单元下面的单元该单元的偏移地址为栈最底部的字单元的偏移地址+2
栈最底部字单元的地址为1000:000E,所以栈空时SP=0010H。
pop 指令的 执行过程
pop ax 出栈
(1) 将SS:SP指向的内存单元处的数据送入ax中
(2) SP = SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
出栈后,SS:SP指向新的栈顶1000EH,pop操作前的栈顶元素,1000CH处的2266H 依然存在,但是它已不在栈中。
当再次执行push等入栈指会后,SS:SP移至1000CH,并在里面写入新的数据,它将被覆盖·
3.8 栈顶超界的问题
SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶。
可是,如何能够保证在入栈、步栈时栈顶不会超出栈空间?
当栈满的时候再使用push指令入栈
栈空的时候再使用pop指令出栈,都将发生栈顶超界问题。
8086CPU的工作机理,只考虑当前的情况:
当前栈顶在何处 ;
当前要执行的指令是哪一条。
栈顶超界是危险的。
在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈 push 的数据太多而导致的超界 ;
要释放内存
执行出栈 pop 操作 ,防止栈空的时候,继续出栈,而越界
3.9 push、pop指令
栈和内存, 先进后出
push和pop的指令,格式(1)
push 寄存器, 将一个寄存器中的数据入栈 放到栈中
pop 寄存器 出栈,用一个寄存器, 来 接收 出栈的数据
push ax 把ax通用寄存器 的内容 放到 栈中 ,SS:SP指向的地址栈中
pop ax 把当前栈顶地址,指向的字单元内存中的值,放到ax寄存器中
段寄存器 都是 以 S 结尾的!!!!!!!! ds ss es
通用寄存器都是以 X 结尾的 ax bx cx dx
push 和pop 指令的格式,
push 内存单元: 将内存单元处的字入栈 (栈操作都是以字为的单位)
pop 内存单元: 用一个内存字单元接收出栈的数据
push [0] 自动找到 ds 段寄存器 存放的段地址 + 偏移地址[0]
pop [2] ds:0
CPU 要知道内存单元的地址,指令执行时可以在 push、pop 指会中给出内存单元的偏移地址,段地址在指令执行时,CPU从ds中取得
数据的段地址,永远是从 ds 获得
代码的段地址 ,永远是从cs中获得
栈的段地址, 永远是从ss中获得
设置 mov sp,0010H 设置栈顶的偏移地址,栈为空,所以sp指向的是一个不存在的下一个地址
指针指向栈底+1 的 就是空的
10000H ~1000FH f+1 进一位,变成10010H
10010 说明栈是空的
push 开始的时候 有两个操作, 1. ps = ps - 2,先把栈顶指针 指上去 2.再执行数据放进去
push、pop 实质上就是一种内存传送指令,
可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指令的。 cpu开不出来栈有多大,要检查栈有没有溢出,超界
同时 push 和pop 也要改变sp中的内容, ss:sp
push 1 先 sp = sp - 2 2再把数据放进去---入栈 ss:sp
pop 1 先把数据拿出来 ---- 出栈 2 在 sp = sp + 2 ss:sp
修改sp sp是一个-偏移地址,也是16位 16位最大 2的16次方 栈的最大范围就是 0 ~ FFFFH。
3.10 栈段
ss是不能动的, 栈顶是变化的 cpu是16位的,
所以最大表示 2的16次方,65536 /1024 = 64KB 大小,决定了变化范围 。 0 ~ FFFFH
1~FFFE
pop push 是改变sp 的 值,偏移地址
压栈,栈满时,继续压栈,会覆盖
用一个段存放数据,将它定义为“数据段’”; ds
用一个段存放代码,将它定义为“代码段”; cs
用一个段当作栈,将它定义为栈段”; ss
调用函数,反到栈中
对于数据段,将它的段地址放在 DS中,用mov add、sub等访问内存单元的指气时,CPU就将我们定义的数据段中的内容当作数据段来访问 ;
对于代码段,将它的段地址放在 CS中,将段中第一条指令的偏移地址放在IP这样CPU就将执行我们定义的代码"
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地置放在 SP 中,这样CPU在需要进行栈操作的时候,比如执行 push 、pop 指令等,就将我们定义的栈段当作栈空间来用
关键在于CPU中寄存器的设置,即 :CS、IP、SS、SP、DS的指向。