寄存器(内存访问)
1.1 内存中字的存储
20000 0x4E 20
a) Little - Endian (小端)就是低位字节放在内存的低地址端,高位字节放在内存的高地址端
b) Big - Endian (大端)就是高位字节放在内存的低地址端,低位字节放在内存的高地址端
网络字节顺序(也就是大端模式)
int a = 20000; 0x00004e20
int* p = &a;
printf("p=0x%p\n",p);
p
0x004CFE44 20
0x004CFE45 4e
0x004CFE46 56
0x004CFE47 43
1.2 DS和[address]
1.2.1 内存单元地址
- CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址;
- 在8086PC中,内存地址由段地址和偏移地址组成。
- 8086CPU中有一个 DS寄存器,通常用来存放要访问的数据的段地址。
1.2.2 mov指令传送功能
(1)将数据直接送入寄存器;
(2)将一个寄存器中的内容送入另一个寄存器中。
(3)mov 指令 还可以将一个内存单元中的内容送入一个寄存器,
或者将寄存器中的数据写入内存单元
1.2.3 字的传送(练习)
(1)
1000:0 10000H 1123H
ax = 1123H
1000:2 10002H 6622H
bx = 6622H
1000:1 10001H 2211H
cx = 2211H
1000:1 10001H 2211H
add bx,[1] => add bx,2211H
bx = bx + 2211H = 6622H + 2211H = 8833H
1000:2 10002H 6622H
add cx,[2] = add cx,6622H
cx = cx + 6622H = 2211H + 6622H = 8833H
1.2.4 mov、add、sub指令
1.2.5 数据段
将一段内存当作数据段,是我们在编程时的一种安排,
我们可以在具体操作的时候 ,用 ds 存放数据段的段地址,
再根据需要,用相关指令访问数据段中的具体单元。
1.2.6 检测点3.1
(1)
ds = 1
MOV AX,[0000]
1:0 1*16 + 0 = 10H AX = 2662H
MOV BX,[0001]
1:1 1*16 + 1 = 11H BX = E626H
MOV AX,BX AX = E626H
MOV AX,[0000] AX = 2662H
MOV BX,[0002]
1:2 1*16 + 2 = 12H BX = D6E6H
ADD AX,BX AX = FD48H
ADD AX,[0004]
1:4 14H AX = FD48H + 2ECCH = 2C14H 舍去溢出部分
mov ax,0 ax = 0
mov al,[0002]
1:2 12H al = e6H ax = 00e6H
mov bx,0 bx = 0
mov bl,[000c] bl = 26H BX = 0026H
add al,bl al = 0cH ax = 000CH
(2)
MOV AX,6622H CS=2000H IP=0 DS=1000H AX=6622H BX=0
JMP 0FF0:0100 CS=0FF0 IP=100H DS=1000H AX=6622H BX=0
MOV AX,2000H CS=0FF0 IP=103H DS=1000H AX=2000H BX=0
MOV DS,AX CS=0FF0 IP=105H DS=2000H AX=2000H BX=0
MOV AX,[0008] CS=0FF0 IP=108H DS=2000H AX=C389H BX=0
MOV AX,[0002] CS=0FF0 IP=10BH DS=2000H AX=EA66H BX=0
1.3 栈
1.3.1 栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,
最后进入这个空间的数据,最先出去。
1.3.2 栈有两个基本的操作:入栈和出栈。
入栈:将一个新的元素放到栈顶;
出栈:从栈顶取出一个元素。
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。
栈的操作规则:LIFO(Last In First Out,后进先出)
1.3.3 CPU提供的栈机制
(1) 8086CPU提供入栈和出栈指令: (最基本的)
PUSH(入栈)
POP (出栈)
push ax:将寄存器ax中的数据送入栈中;
pop ax :从栈顶取出数据送入ax。
8086CPU的入栈和出栈操作都是以字为单位进行的。
(2) 8086CPU中,有两个寄存器:
段寄存器SS 存放栈顶的段地址
寄存器SP 存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶元素。
1.3.4 push 指令的执行过程
push ax
(1)SP=SP–2;
(2)将ax中的内容送入SS:SP指向的内存单元处,
SS:SP此时指向新栈顶。
任意时刻,SS:SP 指向栈顶元素,当栈为空的时候,栈中没有元素,
也就不存在栈顶元素,所以SS:SP 只能指向栈的最底部单元下面的单元,
.该单元的偏移地址为栈最底部的字单元的偏移地址+2,栈最底部字单元
的地址为1000:000E,所以栈空时,SP=0010H。
1.3.4 pop 指令的执行过程
(1) pop ax
(1)将SS:SP指向的内存单元处的数据送入ax中;
(2)SP = SP+2,SS:SP指向当前栈顶下面的单元,
以当前栈顶下面的单元为新的栈顶。
注意:
出栈后,SS:SP指向新的栈顶 1000EH,pop操作前的栈顶元素,
1000CH 处的2266H 依然存在 ,但是,它已不在栈中。
当再次执行push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,
它将被覆盖。
1.3.5 栈顶超界
(1) 当栈满的时候再使用push指令入栈,
栈空的时候再使用pop指令出栈,
都将发生栈顶超界问题。
(2) 栈顶超界是危险的。
(3) 8086CPU的工作机理,只考虑当前的情况:
当前栈顶在何处;
当前要执行的指令是哪一条。
1.3.6 栈与内存
栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。
1.3.7 push、pop指令
push和pop指令的格式(1)
push 寄存器:将一个寄存器中的数据入栈
pop寄存器:出栈,用一个寄存器接收出栈的数据
例如:push ax
pop bx
push和pop指令的格式(2)
push 段寄存器:将一个段寄存器中的数据入栈
pop段寄存器:出栈,用一个段寄存器接收出栈的数据
例如:push ds
pop es
push和pop指令的格式(3)
push内存单元:将一个内存单元处的字入栈(栈操作都是以字为单位)
pop 内存单元:出栈,用一个内存字单元接收出栈的数据
例如:push [0]
pop [2]
1.3.9 问题分析
(1) 3.7 10000H ~ 1000FH 16字节
mov ax,1000H
mov ss,ax
mov sp,0010H
push ax
push bx
push ds
(2) 3.8 10000H ~ 1000FH ss:1000H sp=?
mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx
sub ax,ax
sub bx,bx
pop bx
pop ax
结论:
从上面的程序我们看到,用栈来暂存以后需要恢复的寄存器中的内容时 ,
出栈的顺序要和入栈的顺序相反,因为最后入栈的寄存器的内容在栈顶 ,
所以在恢复时,要最先出栈。
(3) 3.9
mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx
sub ax,ax
sub bx,bx
pop ax
pop bx
(4)3.10
栈内存示意:
10000H 66
10001H 22
10002H <- sp
mov ax,1000H
mov ss,ax
mov sp,0002H
mov ax,2266H
push ax
1.3.11 栈段
我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,
当作栈来用,从而定义了一个栈段。
1.3.13 段的综述
我们可以将一段内存定义为一个段,用一个段地址指示段,
用偏移地址访问段内的单元。这完全是我们自己的安排。
我们可以用一个段存放数据,将它定义为“数据段”;
我们可以用一个段存放代码,将它定义为“代码段”;
我们可以用一个段当作栈,将它定义为“栈段”;
我们可以这样安排,但若要让CPU按照我们的安排来访问这些段,就要:
- 对于数据段,将它的段地址放在 DS中,用mov、add、sub等访
问内存单元的指令时,CPU就将我们定义的数据段中的内容当作
数据段来访问;
- 对于代码段,将它的段地址放在 CS中,将段中第一条指令的偏移地址放在IP中,
这样CPU就将执行我们定义的代码段中的指令;
- 对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地置放在 SP 中,
这样CPU在需要进行栈操作的时候,比如执行 push、pop 指令等,就将我们
定义的栈段当作栈空间来用。
总结:
一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,
也可以什么也不是。关键在于CPU中寄存器的设置,即:CS、IP、SS、SP、DS的指向。
检测点3.2:
(1)
把2000H安排为栈段
mov ax,2000H
mov ss,ax
mov sp,0010H
(2)
把1000H安排为栈段
mov ax,1000H
mov ss,ax
mov sp,0H
实验任务:
mov ax,[0] ;ax = C0EAH
add ax,[2] ;ax = C0FCH
mov bx,[4] ;bx = 31F0H
mov bx,[6] ;bx = 30F0H
push ax ;sp = 00FEH 修改的内存单元地址是2200:00FE 内容为C0FCH
push bx ;sp = 00FCH,修改的内存单元地址是2200:00FC 内容为6021H
pop ax ;sp = 00FCH, ax = 6021H
pop bx ;sp = 00FEH, bx = COFCH
push [4] ;sp = 00FEH ,修改的内存单元地址是2200:00FE 内容为2F31H
push [6] ;sp = 00FCH ,修改的内存单元地址是2200:00FC 内容为30F0H