本文内容:
- 总线
- 寄存器
- 栈
总线
地址总线
一个CPU
如果有N
条地址总线,那么可以寻找2^N
个内存单元
数据总线
数据总线的宽度决定每次传输的数据的位数,比如8086
每次可以传输8位,8088
每次可以传输16
位数据,如果8086
想要传输8
位以上的数据,就得分开传输,实例如下:
控制总线
有多少根总线就意味着有多少种控制
小结
寄存器
为了向下兼容低位的寄存器,这里可以把AX
拆分为AH
和AL
字节:一个字节占 8 bit 空间
字:一个字占两个字节空间,即 16 bit 空间
物理地址
划重点!!!
物理地址 = 段地址 * 16 + 偏移地址
物理地址 = 基础地址 + 偏移地址
如果段地址确定的话,只能通过偏移地址确定物理地址的话,最多能确定FFFFH
即64KB
大小的物理地址空间
段寄存器
CS:IP
CS
为代码段寄存器,顾名思义,是存放CPU
要执行命令的段地址的寄存器
CS
寄存器和DS
寄存器(数据段寄存器是不一样的!
总而言之,过程很简单
就是CPU
通过对CS:IP
来合成物理地址进行指令读取,
在读取完毕后,IP
的值会增加读取指令长度的值,记得,是在读完之后知道指令长度的情况下,才会增加!
从而让CS:IP
的地址指向下一个指令的地址开头,如此循环
如果想修改
CS和IP
寄存器的值的话,该怎么办?
使用JMP
指令即可
如果只是想修改IP
寄存器的值
可以采用jmp 合法寄存器的值
的方式来修改
DS(数据段寄存器)
这里是指数据段寄存器
如果说想读取10000H
内存地址处的内容到dl
里面,就需要这么执行:
mov bx, 1000H |
由于DS
寄存器的硬件设计原因,不可以直接mov, DS 1000H
需要通过中间寄存器来传递值
一般而言,mov
指令格式为mov 寄存器名 内存单元地址
此时如果传递入得内存地址为[0]
,是指偏移地址为 0 ,但这个只是偏移地址,还需要段地址
此时就需要从DS
寄存器来读取我们的段地址,所以说需要提前把段地址从中间寄存器中转到DS
寄存器才可以执行
内存中的存储方式
由于一个字是两个字节,内存是连续的空间,所以一个字要占用两个内存单元
一个字的高地址会存储在内存的高地址上,而他的低地址会存储在内存的低地址上
这里存储的数据是4E20H
和0012H
几个指令
还存在的mov
指令:
mov 寄存器, 段寄存器 |
数据段传送
对于内存单元的偏移的是一个字节还是一个字,要看目的操作数的类型
比如,我们的目的寄存器是ax
,那么mov ax, [0]
传输的是偏移地址在 0 处的一个字
如果目的寄存器是al
,那么mov al, [0]
传送的是一个字节
小结
栈
栈简介
栈的概念这里就不写了,先入后出,这里列一下一个过程
为了能随时找到栈的位置,CPU
通过SS:SP
段地址存放在SS
寄存器中,SP
中存放偏移地址
任何时候,
SS:SP
都指向栈顶元素!
push ax
的过程
- 首先
SP = SP - 2
,栈被抬高,SS:SP
指向当前栈顶的前面内存单元,成为新的栈顶 - 将
ax
压入到当前SS:SP
指向的内存单元,成为新的栈顶
入栈时,栈顶存高地址往低地址方向减小
不过要注意的是,如果设定10000H~10010H
为栈,当栈为空时,此时的SS:SP
为1000H:0010H
因为前面提到过,任何时候SS:SP
指向当前栈顶元素,假设此时栈中有一个字单元124EH
,此时的栈结构为:
栈地址 | 元素 | SS:SP |
---|---|---|
1000CH | ||
1000DH | ||
1000EH | 4E | <——SS:SP |
1000FH | 12 | |
10010H |
如果此时把这个124EH
元素pop
出来,SP = SP + 2
,即SP = 000EH+ 2 = 0010H
那么新的SS:SP
指向不就是1000:0010H
?
栈地址 | 元素 | SS:SP |
---|---|---|
1000CH | ||
1000DH | ||
1000EH | 4E | |
1000FH | 12 | |
10010H | <——SS:SP |
入栈时,栈顶存低地址往高地址方向增加
pop ax
过程如下:
- 首先把
SS:SP
指向的元素送到ax
当中 - 然后
SP = SP +2
,新的SS:SP
就是新的栈顶
这里要注意的是,
pop
并不是真的把元素给弹出去了,里面的元素还是存在的,只不过是SS:SP
的指向发生了变化,如果有新的元素被push
进来,那么就是把原来pop
出去的元素覆盖即可
栈越界问题
根据前面所学的东西,这里应该很明白栈在push
和pop
时候会产生越界问题
push
产生的越界问题
pop
时产生的越界问题
这里CPU
是没法确定栈的上限和下限的,他只能通过SS:SP
确定当前的状态,如果说一个程序员写程序,没有控制好数据的读写问题,很有可能出现栈溢出漏洞
pop和push指令
这里前面既然有push 寄存器
和pop 寄存器
那么肯定还有其他的形式
push 段寄存器
pop 段寄存器
pop 内存单元
,如pop [0]
push 内存单元
,如push [0]
对于一些mov [0], ax
等操作
我们可以同理用push
和pop
来操作
原理是使用mov ss, bx
来设置栈的段地址,使用mov sp, 偏移地址
来设定偏移地址,这样就可以把一段内存空间设为栈,此时就可以使用push
和pop
达到mov
相同的作用了
不过时刻要记住
Push
指令是先使得sp
减2,然后让数据传入栈中
Pop
指令是先把数据从栈中传入到寄存器,然后sp
加2
这俩过程是相反的
小结
参考书籍
《汇编语言-第三版 王爽著》