计算机 CPU 与寄存器知识
# 寄存器与物理地址
寄存器一般为 16 位,而地址总线可能大于 16
8086 处理器,寄存器为 16,地址总线为 20
如果我们想要寻址,该怎么办呢?
CPU 中存在好几个段寄存器 CS,DS,SS,ES 用于提供段地址。
段寄存器
CS 是代码段寄存器
DS 是数据地址
SS 是堆栈地址
ES 提供额外的地址 (什么都可以有)
我们需要理由 段地址
和 偏移量
进行操作,这两个数据存储在两个寄存器中,段地址就是物理地址的头部,而偏移量是物理地址的尾部,通过将段地址左移再和偏移量相加,就可以得到完整地址。
举个栗子 段地址 16 进制若为 21F0H,偏移地址 0060H,此时段地址左移 1 位即 x16, 也是加 4 位的意思,此时共 20 位为 21F00H,这个时候加上偏移地址 0060H 可以得到 21F60H 这个 20 位地址
同一地址可以用不同组段地址 + 偏移量表示:
2000H + 1F60H ->21F60H
2100H + 0F60H ->21F60H
1F00H + 2F60H ->21F60H
以上都可表示为 段地址:偏移地址
IP 寄存器是指令指针寄存器,用于提供偏移地址。
CS 和 IP 最为关键,他们指示 CPU 要读取指令的地址,当 CS:IP 指向哪里,代码就执行到哪里
小测试
2AE3:3=?
3:0B16=?
答案是 2AE33 和 00B46
# CPU 执行指令过程
首次执行
CS:IP -> 地址加法器 -> CPU 输入输出线路 -> 存储器地址 -> 获取数据 (可能是多个字节) -> CPU 输入输出线路 -> CPU 执行控制器
第二次执行
CS:IP+(上次获取到的字节数) -> 地址加法器 -> CPU 输入 ......
即 新的指令执行IP=旧IP+指令读取长度
。
小知识
处理器通电后 CS 会被初始化为 FFFFH,IP 被初始化为 000H
即计算机开机会执行内存中 FFFF0H 的指令 (这是计算机执行第一条指令)
# debug 修改寄存器
Windows 打开 cmd 进入 debug
- R 查看修改寄存器内容
- D 查看内存内容
- E 改写内存内容
- U 将内存中机械指令翻译成汇编指令
- T 执行一行机械指令 (从 CS:IP 开始)
- A 以汇编语言在内存写入机械指令
以上指令不区分大小写
# DS 寄存器和 [address]
获取指定地址的数据
将段地址存入 ds, 再通过偏移 [address] 获取数据
mov ax , 1000H | |
mov ds , ax | |
mov al , [0] |
因为计算机 CPU 设计问题,不能直接使用 mov ds , 1000H
改写数据
mov ax , 1000H | |
mov ds , ax | |
mov [0], al |
需要注意的是:如果你使用 16 位寄存器 (ax) 进行操作如 mov ax , [0]
那 ax 中将会存在 16 位数据,这些数据来自 [1] 和 [0], 如果你使用 8 位寄存器 (al) 进行操作如 mov al,[0]
那 al 中将会存在八位数据 (仅仅来自 [0])
# 栈与寄存器
栈是一段特殊内存,具有先进先出的规则,栈顶是栈的最高的存有数据的内存地址
段寄存器 SS 存放栈顶段地址
寄存器 SP 存放栈顶的偏移地址
任意时刻 SS:SP 指向栈顶地址
栈在代码编译的时候自动分配和销毁,每个函数都会有一个单独的栈,在函数执行时创建,执行完毕后销毁,栈的存在确保函数的相对封闭与数据的独立,当一个一个函数调用另一个函数时,调用者数据会进入栈中保存,被调用者执行完毕后,再从栈中读取数据继续执行,数据入栈和出栈顶顺序相反
用 push 命令入栈数据,pop 命令出栈数据
push 命令会根据当前的栈顶执行进行操作,数据入栈前会将偏移地址减去数据长度 (此处为 2, 因为寄存器为内存单元两倍), 然后再存入数据,而 pop 顺序则相反
如果要在10000H存入数据2266H | |
mov ax,1000H | |
mov ss,ax | |
mov sp,2 | |
mov, ax,2266H | |
push ax |
栈地址排列为上低下高,初始栈顶执行最高地址 + 1,CPU 只知道栈在哪,不会知道栈有多大,需要开发者注意越界