第三章 寄存器
3.1内存中字的存储
CPU中,用16位寄存器来存储一个字,高8位存放高位字节,低8位存放地位字节。在内存中存储时,由于内存单元是字节单元(一个单元存放一个字节),则一个字需要两个地址连续的内存单元来存放,这个字的低位字节存放在低地址单元中,高位字节存放在高位地址单元中。
(从0地址开始存放20000)20000的十六进制数为4E20H
对于这个字单元,0号单元是低地址单元1位高地址
我们来理解这里的2,3号单元。0012H(18),这里先读高字节00H再读12H
(以后,将起始地址为N的字单元,比如一个字单元由2、3两个内存单组成则这个字单元的起始地址为2,我们可以说这是2地址字单元)
在内存单元中存储时,
- 一个单元存放一个字节,一个字需要两个地址连续的内存单元来存放
- 两个连续的内存单元存放一个字型数据(16位)
- 0地址单元中存放的字节型数据:20H
- 0地址字单元中存放的字型数据:4E20H
- 2地址单元中存放的字节型数据:12H
- 2地址字单元中存放的字型数据:0012H
3.2 DS和[address]
CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086PC中,内存地址由段地址和偏移地址组成。
读取10000H单元(1000:0)的内容
mov bx,1000H //将10000H(1000:0)中的数据读到bx中。
mov ds,bx //ds被赋值为bx
mov al,[0] //[...]表示一个内存单元,其中0表示内存单元的偏移地址
3.3 字的传送
前面我们用mov指令在寄存器和内存之间进行字节数据的传送,因为8086CPU是16位结构,有16根数据线。在mov指令中给出16位寄存器可进行16位数据传送。 我们来实现下图(用debug)
3.4mov、add、sub指令
前面我们用到了mov、add、sub指令,他们都带有两个操作对象。 到现在,我们知道,mov指令可以有以下几种形式
mov | 寄存器 ,数据 | 比如:mov ax,8 |
---|
mov | 寄存器,寄存器 | 比如:mov ax,bx | mov | 寄存器,内存单元 | 比如:mov ax,[0] | mov | 内存单元寄存器 | 比如:mov [0],ax | mov | 段寄存器,寄存器 | 比如:mov ds,sx |
以下是两个例子演示 (1)我们来试一下“mov 段寄存器,寄存器”的用法。如下图 ,进入debug用A命令 在上图中用A命令在预设地址0740:0100处,用mov ax,ds命令写入后用T命令执行。 (2)“mov 内存单元,寄存器”演示如下 将ax赋值1000,交换ds和ax后 mov [0],cs,将寄存器cs的内容送入内存10000H 当CS:IP指向0740:0105,Debug显示命令mov [0000],cs 因为这是一条访问内存的指令,的Debug在右下角需要显示访问的内存单元中的内容。由于指令中的CS是一个16位寄存器,所以要访问一个字单元,它的偏移地址为0,段地址在ds中,所以显示的“DS:0000=0000”我们可知道这个字单元中的内容为0。 mov [0000],cs执行后CS中的数据(0740H)被写入1000:0处,1000:0单元存放 40H,1000:1单元存放 7H,最后用D命令显示
3.5数据段
对于8086PC机,在编程时可根据需要,将一组内存单元定义·为一个段。我们可以将一组长度为N(N<=64KB)、地址连续、起始地址为16的倍数的内存单元当做专门存储数据的内存空间,从而定义了一个数据段。比如用123BOH~123B9H这段内存空间来存放数据,可以认为这段内存是一个数据段,段地址为123BH,长度为10个字节。 那么我们如何访问数据段中的数据呢?将一段内存当做数据段,用ds存放数据段的段地址,在根据需要用相关指令访问数据中的具体单元。
这里直接举例子就能看懂了
使CS:IP指向汇编第一条指令地址
t单步调试(…略)
mov ax,1 | | |
---|
mov ds,ax | AX=DS=0001H | ds:[0000]的物理地址为0010H(既物理地址=0001*16+0000=0010H | mov ax,[0000] | AX=2662H | ax=ds:[0000]=2262H | mov bx,[0001] | BX=E626H | bx=ds:[0001]=E626H | mov ax,bx | AX=E626H | ax=bx=E626H | mov ax,[0000] | AX=2662H | ax=ds:[0000]=2262H | mov bx,[0002] | BX=D6E6 | bx=ds:[0002]=D6E6H | add ax,bx | AX=FD48H | ax=ax+bx=2662H+D6E6H=FD48H | add ax,[0004] | AX=2C14H | ax=ax+ds:[0004]=FD48H+2ECC=2C14 | mov ax,0 | AX=0000H | ax=0000H | mov al,[0002] | AX=00E6H | al=ds:[0002]=E6H,ax=ah+al=00E6H | mov bx,0 | BX=0000H | bx=0000H | mov bl,[000C] | BX=0026H | bl=ds:[000C]=26H;bx=bh+bl=0026H | add al,bl | AX=000CH | al=al+bl=E6+26=10CH=0CH,ax=ah+al=000CH |
3.6 栈
我们可以把栈比作一个盒子,入栈就是把一个新的元素放到盒子底部(栈顶),出栈就是从盒子最上面栈顶取出一个元素。这种规则又称LIFO(后进先出)
3.7 CPU提供的栈机制
8086CPU提供入栈和出栈指令,最基本的两个是PUSH(入栈)和POP(出栈)。 push ax表示将寄存器ax中的数据送入栈中 pop ax表示从栈顶取出数据送入ax 8086CPU的入栈和出栈都是以字为单位进行的 在进行push和pop命令时我们需要知道栈顶的位置,那么我们现在只知道CS、IP是段地址和偏移地址。那么在8086CPU中有两个寄存器,SS和SP,栈顶的段地址存放在SS中,偏移地址存放在SP中,SS:SP指向栈顶元素。
- pop指令功能
例如pop ax pop ax的执行过程和push ax刚好相反 (1)将SS:SP指向的内存单元处的数据送入ax中 (2)SP=SP+2,SS:SP指向当前栈顶下面的单元为新的栈顶 出栈后SS:SP指向新的栈顶1000EH,pop操作前的栈顶元素,1000CH处的2266H任存在,但已经不在栈中,如果再次执行push等入栈指令后,SS:SP移植1000CH,并在里面写入新的数据。
3.8栈顶超界的问题
执行push后栈顶超出栈空间 执行pop后续
3.9 push、pop指令
push 寄存器;将一个寄存器中的数据入栈 pop 寄存器; 出栈,用一个寄存器接收出栈的数据
push和pop也可以在内存单元和内存单元质检传送数据
push 内存单元 ;将一个内存单元处的字入栈 pop 内存单元 ; 出栈,用一个内存字单元接收出栈的数据 比如 mov ax,1000H mov ds,ax ;内存单元的段地址要放在ds中 push [0] ;将1000:0处的字压入栈中 pop [2];出栈,出栈的数据送入1000:2处 举个例题 (1)将10000H~1000FH 这段空间当做栈,初始状态栈是空的; (2)设置AX=001AH,BX=001BH (3) 利用栈,交换AX和BX中的数据
3.10 栈段
我们可以将长度为N(N<=64KB)的一组地址连续,起始地址为16的倍数的内存单元,当作栈空间来用。比如,我们将10010H~1001FH这个长度为16字节的内存空间当做栈来用,以栈的方式进行访问。这段空间就可以称为一个栈段,段地址为1001H,大小为16字节。 我们将内存当作栈段,仅仅是我们在编程时的一种安排,CPU不会自动将我们定义的栈段当作栈空间来访问。 如果将10000H~1FFFFH这段空间当做栈段,初始状态栈是空的,此时,SS=1000H,SP=0 (我们把10000H~1FFFFH这段空间当做栈段,SS=1000H,栈空间为64KB,栈最底部的字单元地址为1000:FFFF。SS:PP指向栈顶单元,当栈中只有一个元素,SS=1000H,SP=FFFEH。栈为空,就相当于出栈,出栈后SP=SP+2,则FFFE+2=0 所以sp最终=0)
|