CPU 的组成
- 运算器进行信息处理
- 寄存器进行信息存储
- 控制器协调各种器件进行工作
- 内部总线实现CPU 内各个器件之间的联系
寄存器是CPU 内部的信息存储单元
8086 有14 个寄存器
- 通用寄存器: AX, BX, CX, DX
- 变址寄存器: SI, DI
- 指针寄存器: SP, BP
- 指令指针寄存器: IP
- 段寄存器: CS, SS, DS, ES
- 标志寄存器: PSW
它们的共性为都是16 位的, 可以存放两个字节
例如通用寄存器AX, 能存储一个16 位的数据, 最大值为
如何保证对上一代的兼容? 将通用寄存器分为2 个独立的8 位寄存器使用, 例如将AX 分为AH, AL 高位和低位. 相应的8086 的字长为16bit, 高位存在高8 位寄存器, 低位存在低8 位寄存器
寄存器 | 寄存器中的数据 | 表示的值 |
---|---|---|
AX | 0100111000100000 | 20000(4E20H) |
AH | 01001110 | 78(4EH) |
AL | 00100000 | 32(20H) |
mov 和add 指令
举例, AX 和BX 值默认是0000H
指令 | 执行后AX 中的数据 | 执行后BX 中的数据 |
---|---|---|
mov ax, 4E20H | 4E20H | 0000H |
add ax, 1406H | 6226H | 0000H |
mov bx, 2000H | 6226H | 2000H |
add ax, bx | 8226H | 2000H |
mov bx, ax | 8226H | 8226H |
add ax, bx | 044CH | 8226H |
指令 | 执行后AX 中的数据 | 执行后BX 中的数据 |
---|---|---|
mov ax, 001AH | 001AH | 0000H |
mov bx, 0026H | 001AH | 0026H |
add al, bl | 0040H | 0026H |
add ah, bl | 2640H | 0026H |
add bh, al | 2640H | 4026H |
mov ah, 0 | 0040H | 4026H |
add al, 85H | 00C5H | 4026H |
add al, 93H | 0058H | 4026H |
物理地址确定的方法
物理地址
所有的内存单元构成的存储空间是一个一维的线性空间, CPU 访问内存单元时要给出内存单元地址, 每一个内存单元在这个空间中都有唯一的地址, 这个地址就被称为物理地址.
物理地址但是8086 有20 位地址总线, 寻址能力为1M. 而8086 是16 位结构的CPU, 运算器以此最多可以处理16 位的数据, 寄存器的最大宽度为16 位, 在8086 内部处理的, 传输, 咱村的地址也是16 位, 寻址能力也只有64KB, 如何解决呢?
物理地址确定方法
用2 个16 位地址(段地址, 便宜地址) 合成一个20 位物理地址
物理地址 = 段地址 * 16 偏移地址
本质含义为CPU 访问内存时, 用一个基地址(段地址 * 16) 和一个相对于基础地址的偏移地址相加, 给出内存单元的物理地址.
分段方式管理内存
实际中是没有分段, 段的划分来自于CPU
同一段内存, 可以有多中分段方案
但是, 1. 段地址 * 16 必然是16 的倍数, 所以一个段的起始地址也一定是16 的倍数. 2. 偏移地址为16 位, 16 位地址的寻址能力为64k, 所以一个段的长度最大为64k
例如
物理地址 | 段地址 | 偏移地址 |
---|---|---|
21F60H | 2000H | 1F60H |
2100H | 0F60H | |
21F0H | 0060H | |
21F60H | 0000H | |
1F00H | 2F60H |
表示方法为2000:1F60H
专门的寄存器存放地址
- CS 代码段寄存器
- DS 数据段寄存器
- SS 栈段寄存器
- ES 附加段寄存器
debug 的使用
常见命令
用R命令查看、改变CPU寄存器的内容
用D命令查看内存中的内容
用E命令改变内存中的内容
用U命令将内存中的机器指令翻译成汇编指令
用A命令以汇编指令的格式在内存中写入机器指令
用T命令执行机器指令
用Q 命令退出debug
R 命令的使用
R命令查看和修改寄存器D 命令查看内存内容
d命令查看内容- 默认128 个字节内容
- 段地址:偏移地址, 列出内存中指定地址处的内容
- 段地址:偏移地址 结尾偏移地址, 列出内存中指定地址范围内的内容
E 命令改变内存中的内容
E命令修改寄存器内容- 段地址:偏移地址 数据1 数据2 ...
- 段地址: 偏移地址
- 逐个询问修改
- 空格, 接受 继续
- 回车, 结束
U 命令, 将内存中的机器指令翻译成汇编指令
-
汇编指令为
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
-
对应的汇编为
B8 23 01
BB 03 00
89 D8
01 D8
-
e 地址 数据, 写入
-
d 地址, 查看
-
u 地址, 查看代码
A 命令以汇编指令的格式在内存中写入机器指令
-
汇编指令为
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
-
对应的汇编为
B8 23 01
BB 03 00
89 D8
01 D8
-
a 地址, 写入汇编指令
-
d 地址, 查看数据
-
u 地址, 查看代码
T 命令执行机器指令
t命令逐步执行命令mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
CS 和IP 寄存器
CS: 代码段寄存器
IP: 指令指针寄存器
CS:IP, CPU 将内存中的CS:IP 指向的内容当做指令执行
以上代码的执行过程为
- 从CS:IP 执行内存单元读取指令, 读取的指令进入指令缓冲器
- IP = IP + 所读取指令的长度, 从而指向下一条指令
- 指向指令, 重复第一步
而内存中有数据B8 23 01 BB 03 00 89 D8 01 D8
是一段数据还是指令呢?
CPU 将CS:IP 指向的内存单元中的内容看做指令
jmp 指令
使用jmp 来修改cs 和ip 指令
例如
jmp 段地址:偏移地址
jmp 2AE3:3
jmp 3:0B16
用指令中给出的段地址修改cs, 偏移地址修改ip
尽修改ip 内容
jmp 某一合法寄存器
jmp ax, 类似于mov IP,ax
jmp bx
用寄存器中的值修改IP
jmp指令应用从20000H 开始, 执行序列:
- mov ax,6622
- jmp 1000:3
- mov ax,0000
- mov bx,ax
- jmp bx
- mov ax,0123H
- 转到第3 步继续执行
用DS 和[address] 实现字的传送
问题 CPU 从内存单元中要读取数据
要求: CPU 要读取一个内存单元的时候, 必须先给出这个内存单元的地址
原理: 在8086 中, 内存地址由段地址和偏移地址组成
解决: DS 和[address] 配合
- 用ds 寄存器存放要访问的数据的段地址
- 偏移地址用[...] 形式给出
mov bx,1000H
mov ds,bx
mov al,[0]
将10000H(1000:0) 中的数据读到al 中
mov bx,1000H
mov ds,bx
mov [0],al
将al 中的数据写到10000H(1000:0) 中
注意不可以使用mov ds,1000H
, 8086 不支持将数据直接写入段寄存器
方法应为: 数据 -> 一般寄存器 -> 段寄存器
对内存单元中的数据访问
可以根据需要将一组内存单元定义为一个段
- 物理地址 = 段地址 * 16 + 偏移地址
- 将一组长度为N(N<= 64k) 地址连续, 起始地址为16 的倍数的内存单元当做专门存储数据的内存空间, 定义一个数据段
处理方式(DS):([address])
- 用DS 存放数据段的段地址
- 用相关指令访问数据段中的具体单元, 单元地址由[address] 指出
例如1: 累加数据段中的前3 个单元中的数据
mov ax,123BH
mov ds,ax
mov al,0
add al,[0]
add al,[1]
add al,[2]
累加前3个单元
例2: 累加数据段中的前3 个字型数据
mov ax, 123BH
mov ds,ax
mov ax,0
add ax,[0]
add ax,[2]
add ax,[4]
总结
用mov 指令操作数据
指令形式 | 示例 |
---|---|
mov 寄存器,数据 | mov ax,8 |
mov 寄存器,寄存器 | mov ax,bx |
mov 寄存器, 内存单元 | mov ax,[0] |
mov 内存单元, 寄存器 | mov, [0],ax |
段寄存器, 寄存器 | mov ds,ax |
加法add 和减法sub 指令
add 指令 | 示例 |
---|---|
add 寄存器,数据 | add ax,8 |
add 寄存器,寄存器 | add ax,bx |
add 寄存器,内存单元 | add ax,[0] |
add 内存单元,寄存器 | add [0],ax |
sub 指令 | 示例 |
---|---|
sub 寄存器,数据 | sub ax,8 |
sub 寄存器,寄存器 | sub ax,bx |
sub 寄存器,内存单元 | sub ax,[0] |
sub 内存单元,寄存器 | sub [0],ax |
DS 和[address] 访问内存中数据段方法总结
- 字在内存中存储时, 要用两个地址连续的内存单元来存放, 字的低位字节存放在低地址单元中, 高位字节存放再高地址单元中
- 用 mov 指令要访问内存单元, 可以在mov 指令中只给出单元的偏移地址, 此时, 段地址默认在DS寄存器中
- [address] 表示一个偏移地址为address 的内存单元
- 在内存和寄存器之间传送字型数据时, 高地址单元和高8位寄存器, 低地址单元和低8位寄存器相对应
- mov, add, sub 是具有两个操作对象的指令, 访问内存中的数据段(对照: jmp是具有一个操作对象的指令, 对应内存中的代码段)
栈及其操作
栈是一种只能在一端进行插入或删除操作的数据结构
- 入栈, 将一个新的元素放到栈顶
- 从栈顶取出一个元素
push ax; 将ax 中的数据送入栈中
pop ax; 从栈中取出数据送入ax 中
都是以字为单位对栈操作
规则: LIFO 后进先出
在8086 中可以将一段内存当做栈来使用
问题一, CPU 如何直到一段内存空间当做栈使用?
问题二, 执行push 和pop, 如何直到哪个单元是栈顶单元
8086 中, 有两个与栈相关的寄存器
- 栈段寄存器SS, 存放栈顶的段地址
- 栈顶制作寄存器SP, 存放栈顶偏移地址
任意时刻, SS:SP 指向栈顶元素
mov ax,1000H
mov ss,ax
mov sp,0010H
AX | BX | SS | SP |
---|---|---|---|
1000H | 1000H | 0010 |
mov ax,001AH
mov bx,001BH
AX | BX | SS | SP |
---|---|---|---|
001AH | 001BH | 1000H | 0010 |
push ax
push bx
AX | BX | SS | SP |
---|---|---|---|
001AH | 001BH | 1000H | 000C |
pop ax
pop bx
AX | BX | SS | SP |
---|---|---|---|
001BH | 001AH | 1000H | 0010 |
push ax
- SP = SP - 2
- 将ax 中的内容送入SS:SP 指向的内存单元, SS:SP 此时指向新的栈顶
pop ax
- 将SS:SP 指向的内存单元的数据送入ax
- SP = SP + 2, SS:SP 指向当前栈顶下面的单元, 以当前栈顶下面的单元为新的栈顶
注意栈顶超界的问题
栈小结
- push pop 实质上就是一种内存传送指令, 可以在寄存器和内存之间传送数据, 与mov 不同, push 和pop 指令访问的内存单元地址不是指令中给出的, 而是SS:SP 指出的
- 执行push 和pop 时, SP 中的内容会自动改变
- 8086 提供栈操作机制
- 在SS, SP 中存放栈顶段地址和偏移地址, 入栈出栈指令根据SS:SP 指示的地址, 按照栈的方式访问内存单元
- push 执行步骤
- SP = SP - 2
- 向SS:SP 指向的字单元中送入数据
- pop 步骤
- 从SS:SP 指向的字单元中读取数据
- SP = SP - 2
网友评论