寄存器(内存访问)
之前从从访问内存的角度再来学几个重要的寄存器。
3.1、内存中字的储存
CPU中,用16位寄存器来储存一个字(两个字节),但是内存单元是字节单元(一个单元存放一个字节),所以一个字要用两个连续的内存单元来存放,低位字节存放低地址单元中,高位字节存放在高地址单元中。
所以我们提出字单元的概念:**字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。**一般将起始地址为N的单元称为N地址字单元。
3.2 DS和[address]
CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址。比如要读取10000H单元的内容,可以用如下的程序段进行:
mov bx, 1000H
mov ds, bx
mov al, [0]
上面的三条指令将 10000H(1000:0)中的数据读到 al 中。
由于8086CPU不支持将内存数据直接读入ds(段寄存器),所以需要先将内存数据先读入bx(通用寄存器),然后再将数据在寄存器间转移。
而 “ mov al, [0] ”中 “[ ]” 表示一个内存单元, “[ ]” 中的 0 表示内存单位的偏移地址,而内存的段地址是 8086CPU 从 ds 寄存器中自动读取的,所以在把数据移入寄存器中之前,需要先行把 1000H 送入ds。
3.3、字的传送
因为 8086CPU 是 16 位结构,有 16 根数据线,所以可以一次传送一个字。只要在 mov 指令中给出 16 位的寄存器就可以进行 16 位数据的传送了。
3.4、mov、add、sub指令
mov用法汇总:
指令格式 | 示例代码 |
---|---|
mov 寄存器,数据 | mov ax,8 |
mov 寄存器,寄存器 | mov ax,bx |
mov 寄存器,内存单元 | mov ax,[8] |
mov 内存单元,寄存器 | mov [8],ax |
mov 内存单元,段寄存器 | mov ax,1000H; mov ds,ax;mov [0],cs |
mov 段寄存器,内存单元 | mov ax,1000H; mov ds,ax;mov ds,[0] |
mov 段寄存器,寄存器 | mov ds,ax |
mov 寄存器,段寄存器 | mov ax,ds |
注意,当使用 ax 寄存器与内存空间之间进行操作时,内存偏移的地址为 2,因为 ax 为十六位寄存器,每次传输数据时会传输一个字大小的
add用法汇总:
指令格式 | 示例代码 |
---|---|
add 寄存器,数据 | add ax,8 |
add 寄存器,寄存器 | add ax,bx |
add 寄存器,内存单元 | mov ax,[8] |
add 内存单元,寄存器 | mov [8],ax |
add 不能操作段寄存器!因为段寄存器用于存放段地址,不能作为操作数(硬件层面没有实现)。
sub 同理。
3.5、数据段
对于 8086PC,可以根据需要,把一组内存单元定义为一个段。如果将**一组长度为N(N <= 64KB)、地址连续、起始地址为 16 的倍数的内存单元当作专门储存数据的内存空间,**这段内存空间被称为 “数据段”。
在具体操作时,用 ds 存放数据段的段地址,再根据需要用相关指令访问。
3.6、栈
操作规则:LIFO(Last In First Out)
从程序化的角度说,有一个标记一直指示着栈顶的元素。
3.7、CPU 提供的栈机制
8086CPU 提供相关的指令来以栈的方式访问内存空间,就是说,可以把一段内存当作栈来使用。
相关指令:
- push ax(把 ax 中的数据压入栈中)
- pop ax(弹出栈顶数据存入 ax)
以上指令以字为单位进行
那么,CPU是如何确定哪段内存被作为栈来使用呢?
任意时刻,SS:SP 指向栈顶元素,push 和 pop 指令执行时,CPU 从 SS 和 SP 中得到栈顶的地址。
3.8、栈顶越界问题
8086CPU 中并没有记录栈的容量的寄存器,它只考虑当前栈顶的位置,所以无论是入栈还是出栈时都有可能使得栈顶越界,使得栈空间以外的数据、代码被覆盖。
3.9、push、pop 指令
除了通用寄存器以外,push 同样可以把段寄存器、内存单元作为操作数。
切记:push 和 pop 的操作规模永远是一个字。
3.10、栈段
对于 8086PC,可以根据需要,把一组内存单元定义为一个段。如果将**一组长度为N(N <= 64KB)、地址连续、起始地址为 16 的倍数的内存单元当作专门栈空间的内存空间,**这段内存空间被称为 “栈段”。
前提:将 SS:SP 指向我们定义的栈段。