8086汇编语言:8086CPU寄存器的相关介绍

一、基本知识:

对于一个汇编程序员来说,CPU中的主要部件是寄存器。寄存器是CPU中程序员可以用指令读写的部件;程序员通过改变各种寄存器中的内容来实现对于CPU的控制。对于不同的CPU,寄存器个数、结构是不相同的

8086CPU中有14个寄存器,每个寄存器都有一个对应的名称:AX、BX、CX、DXSI、DISP、BP、IP、CS、SS、DS、ESPSW

8086CPU中所有的寄存器都是16位,可以存放两个字节。

以AX寄存器为例

但是只有AX、BX、CX、DX这4个寄存器可以分为两个可独立使用的8位寄存器来使用;其他寄存器都不行!

 

二、通用寄存器:

数据寄存器:(AX、BX、CX、DX)

基本介绍:

AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据。

以AX寄存器为例

因为8086CPU的上一代CPU中的寄存器都是8位的,为了保证兼容,使得原来基于上代CPU编写的程序稍加修改就可以在8086CPU上运行,故8086CPU的 AX、BX、CX、DX这4个寄存器都可以分为两个可独立使用的8位寄存器来使用。(注意:但是仅仅限于这四个寄存器,其他的寄存器都不能分成高低8位来使用。)

以寄存器AX为例

(以寄存器AX为例)AX的低八位构成了AL寄存器,高八位构成了AH寄存器;这两个寄存器都是可以分别独立使用的8位寄存器。

关于使用的一点注意:(以寄存器AX为例,其他数据寄存器也一样!)

mov ah,0
mov al,200
add al,60

200+60=260;但是因为al是一个8位的寄存器,所以它会发生溢出,所以在运算之后在al中实际存储的值是260-256=4;那么问题来了,溢出的那一位是否会存储在ah中呢?使用代码验证一下!

检验代码:

DATAS SEGMENT
    ;此处输入数据段代码  
DATAS ENDS

STACKS SEGMENT
    ;此处输入堆栈段代码
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    ;此处输入代码段代码
    mov ah,0
    mov al,200
	add al,60
	
	mov dl,ah
	add dl,48
	mov ah,2
	int 21h	
	
    MOV AH,4CH
    INT 21H    
CODES ENDS
    END START

通过运行结果,我们可以发现那溢出的一位并没有存储在寄存器ah中,因此我们可以得出结论——单独使用寄存器al和寄存器ah时;他们均是作为一个8位独立的寄存器来使用的;彼此互不干扰。

但是当使用ax时就不一样了!

DATAS SEGMENT
    ;此处输入数据段代码  
DATAS ENDS

STACKS SEGMENT
    ;此处输入堆栈段代码
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    ;此处输入代码段代码
    mov ax,200
	add ax,60
	
	mov bx,ax
	call output
	
    MOV AH,4CH
    INT 21H
      
;多位输出函数,入口参数为bx
output proc
	push ax;数据入栈区
	push cx
	push dx
	
	;初始化变量
	mov ax,bx;数据放入准备除法
	mov cl,10;作为除数
	mov ch,0;用于计数便于后续出栈输出
	
divagain:;除法数字剥离部分
	cmp ax,0;判断是否已经除尽
		je divover
	inc ch;计数器加1
	div cl
	push ax;入栈,提取的时候取用ah部分,存储余数(低位优先)
	mov ah,0;调整ax
		jmp divagain;再次除法剥离数字
		
divover:;出栈输出部分
	cmp ch,0;判断数字是否已经出尽
		je outputover
	pop ax;取用ah部分
	mov dl,ah;输出部分
	add dl,48
	mov ah,2
	int 21h
	dec ch
		jmp divover 
		
outputover:;收尾部分
	pop dx
	pop cx
	pop ax;数据出栈区
	ret
output endp
  
CODES ENDS
    END START

因此我们可以知道使用ax寄存器时,al寄存器和ah寄存器是作为一个整体,彼此不再独立使用!

如果对于图中的多位输出函数——output不太理解的话;可以看我的往期内容,点击下方链接直达!

8086汇编语言:多位数据进行输出(👈点击这里(*^_^*)!)

①Ax(累加寄存器):

首先,AX寄存器作为一个数据寄存器,所以可以在AX寄存器中存放普通的数据,你可以把它作为一个整体使用,存放16位的数据;你可以把它“掰开”来用,分成低八位AL和高八位AH,分别单独存储8位数据。(此时AL和AH的是相互独立的,彼此不受影响,不明白的看上面的论证!)

AX寄存器除此之外,参与运算的频率是相当的高!

——除法运算:div指令

除法指令分为8位和16位两种,被除数默认放置在AX或者AX和DX中,除数放置在一个寄存器或者内存单元中:

  • 除数为8位,被除数则为16位,默认放置在AX中。运算过后,AL存储除法的商,AH存放除法的余数。

  • 除数为16位,被除数则为32位,默认放置在AX和DX中;其中DX存放高16位,AX存放低16位。运算过后,AX存储除法的商,DX存储除法的余数。

mov ax,10
mov dl,2
div dl 

——乘法运算:mul指令

乘法指令也分为8位和16位两种,其中一个因数默认放置在AL或者AX中,另一个因数放置在寄存器中或者内存单元中:

  • 因数都是8位时,其中一个因数默认放置在AL中;运算过后,运算的结果放置在AX中。

  • 因数都是16位时,其中一个因数默认放置在AX中;运算过后,运算结果的高16位放置在DX中,低16位放置在AX中。

mov al,100
mov bl,10
mul bl

②BX(基址寄存器):

首先,BX寄存器作为一个数据寄存器,所以可以在BX寄存器中存放普通的数据,你可以把它作为一个整体使用,存放16位的数据;你可以把它“掰开”来用,分成低八位BL和高八位BH,分别单独存储8位数据。(此时BL和BH的是相互独立的,彼此不受影响,不明白的看上面的论证!)

BX寄存器除此之外,还可以表示偏移地址!

对于内存空间的访问,我们通过DS中给出的段地址以及“[...]”给出的偏移地址来确定;但是我们在未了解BX之前只能在“[...]”中置入常量来使用(譬如:mov al,[0]);这样的使用方式有着诸多限制;但是在引入BX之后,我们可以使用譬如mov al,[BX]这样的指令;这样偏移地址就可以更加灵活多样了

DATAS SEGMENT
    data db 'ABCD'
DATAS ENDS

STACKS SEGMENT    
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
    mov bx,offset data
    mov cx,4
output:
	mov dl,[bx]
	mov ah,2
	int 21h
	inc bx
	loop output    
    
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

上面的代码,利用BX使得偏移地址可以作为一个变量出现在代码中,使用起来更加方便,适用场景更加灵活!当有循环出现的时候尤为有用!

 

因为BX在表示内存单元上的运用,我们还可以将BX和常量结合起来使用!

 

使用举例:mov al,[bx+2];下面列举一个实际的例子演示一下!

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT    
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
    mov bx,offset data
    mov cx,4
output:
	mov dl,[bx+4]
	mov ah,2
	int 21h
	inc bx
	loop output    
    
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

 

我又试了试减法,乘法,除法;看看8086CPU是否支持它们!

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT    
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
    mov bx,offset data
    add bx,10
    mov cx,4
output:
	mov dl,[bx-4]
	mov ah,2
	int 21h
	inc bx
	loop output    
    
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

最后得出结论——加法和减法可以,乘法和除法不支持!(但是无论你使用加法还是减法,一定注意越界的问题,别用着用着越界了,8086CPU没有越界的管理机制,可能会修改一些未知的重要数据!)

mov al,[bx+2]为例;我们还可以将其写成其他的形式:

mov al,[2+bx]
mov al,2[bx]

我还了解到一种写法:

mov al,[bx].2

我验证了一下,报错了(structure field expected);因为自己才疏学浅,不知道问题到底出在哪里,所以大家在使用该形式的时候留一个心眼,如果你知道是为什么的话,欢迎在评论区留言!谢谢!

③CX(计数器寄存器):

首先,CX寄存器作为一个数据寄存器,所以可以在CX寄存器中存放普通的数据,你可以把它作为一个整体使用,存放16位的数据;你可以把它“掰开”来用,分成低八位CL和高八位CH,分别单独存储8位数据。(此时CL和CH的是相互独立的,彼此不受影响,不明白的看上面的论证!)

CX寄存器除此之外,还可以和Loop配合使用来表示循环!

当8086CPU执行到Loop指令时,先将CX中数值减一,然后判断CX中的数值是否为零:为零则向下执行代码,不为零则跳转到标号处执行。因此我们在将Loop指令和CX寄存器搭配使用时,在CX中存储循环的次数,Loop指令来决定跳转判断的时机。

    mov cx,10
    mov dl,'A'
again:
    mov ah,2
    int 21h
    loop again

这样就可以使用这几行简短的代码就可以输出十个A了!

④DX(数据寄存器):

首先,DX寄存器作为一个数据寄存器,所以可以在DX寄存器中存放普通的数据,你可以把它作为一个整体使用,存放16位的数据;你可以把它“掰开”来用,分成低八位DL和高八位DH,分别单独存储8位数据。(此时DL和DH的是相互独立的,彼此不受影响,不明白的看上面的论证!)

DX寄存器除此之外,首先可以与寄存器AX配合使用,参与16位的乘法运算和32位的除法运算!(详情请见上面对于AX的相关介绍。)

然后,DX寄存器常常用来存放外设端口地址,当你在汇编代码中与I/O接口进行数据的交互时,你可能会使用到DX寄存器!

 

为什么说可能呢?

 

当你的端口号用8位存的下外设端口地址时,你可以直接用立即数表示出外设端口地址来进行相关操作!

in al,30H
out 0Fh,al

(都说到这了,就顺便提一嘴:你传输的数据必须放置在寄存器AL中进行传输或接收,当寄存器AL放不下时,你可以放置在寄存器AX中!)

当你的端口号用8位存不下外设端口地址时,必须先使用DX放置外设端口地址,再进行相关操作!

mov dx,300H
int al,dx
mov dx,4F1H
out dx,al

变址寄存器:SI、DI

SI和DI是8086CPU中和BX功能相近的寄存器,但是值得注意的是SI和DI是不能分成两个8位的寄存器来使用

以下三组代码是等价的!

mov bx,0
mov ax,[bx]

mov si,0
mov ax,[si]

mov di,0
mov ax,[di]

以下的三组代码也是等价的!

mov bx,0
mov ax,[bx+123]

mov si,0
mov ax,[si+123]

mov di,0
mov ax,[di+123]

因此我们可知,寄存器SI和DI在表示内存单元上的运用和BX的用法是一样的!但是BX此外还可以作为通用寄存器使用!

 

因为寄存器SI和DI的出现,在表示内存单元时对于偏移地址的表示我们可以弄出更多的花样!

 

前面我们介绍了“[寄存器]”“[常量]”“[寄存器+常量]”的组合!现在我们可以使用“[寄存器+寄存器]”“[寄存器+寄存器+常量]”组合了!

——“[寄存器+寄存器]”:

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT    
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
    mov bx,offset data
    mov si,offset data
    mov cx,4
output:
	mov dl,[bx+si]
	mov ah,2
	int 21h
	inc bx
	inc si
	loop output    
    
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

——“[寄存器+寄存器+常量]”:

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT    
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
    mov bx,offset data
    mov si,offset data
    mov cx,4
output:
	mov dl,[bx+si+2]
	mov ah,2
	int 21h
	inc bx
	inc si
	loop output    
    
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

 

你可能会想“[寄存器+寄存器+寄存器]”“[寄存器+寄存器+寄存器+常量]”组合是否可以呢?试一下!(☆▽☆)

然后它报错了!(;′⌒`)


指针寄存器:BP、SP

①SP(堆栈指针寄存器):

这个寄存器一般和SS配合使用,分别用于表示堆栈的段地址及偏移地址!详情可以看下一节关于SS和SP相关介绍!

②BP(基址指针寄存器):

首先BP寄存器作为通用寄存器的一员,是可以放置数据的,但是不能分成高8位和低8位单独使用。

除此之外,BP还用于表示偏移地址,通过以 […] 的方式访问内存单元;而且在 […] 中使用了寄存器 BP 的话,没有特定指出段地址的话,此时段地址默认使用SS寄存器中的值。(BX,SI,DI 的段地址默认使用 DS 寄存器中的值。

在使用时,我们可以通过显式的指出使用哪一个段寄存器中的值作为段地址!

mov ax,ds:[bp]
mov ax,ss:[bp]
mov ax,[bp]

 ds:[bp]这里明确给出了段地址位于 DS 中,所以这里代表段地址为 DS ,偏移量为 BP 寄存器对应内存单元中的值(数据区),而如果单单是使用 [BP] 的话,则代表段地址为 SS,偏移量为 BP 寄存器对应内存单元中的值(堆栈区)。

 

在写完这一段的时候,我自己又在想,如果不显式的指出段寄存器,在不同的使用情境下,BP寄存器是否会有不同的作用?

 

于是我设计了下面的测试代码!

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT  
 
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
    mov al,'Y'
    mov ah,0
    push ax
    
    ;内存验证区
    mov bp,offset data
    mov dx,[bp]
    mov ah,2
    int 21h
    ;——————    
    
    ;堆栈验证区    
    mov al,'X'
    mov ah,0
    push ax
    
    mov bp,sp
    
    mov dx,[bp]
    mov ah,2
    int 21h
    ;——————
    
    pop ax
    pop ax    
   
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

在上述代码中,我没有显式的指出段寄存器;但是两次使用BP寄存器的作用完全不一样

  • 第一次使用是以DS段寄存器中的值作为段地址的,因此显示的是数据区中数据;

  • 第二次使用是以SS段寄存器中的值作为段地址的,因此显示的是堆栈区中数据。

因此我得出结论——在你没有显式指出段寄存器是谁时,段地址不一定默认使用SS寄存器中的值。还和你具体使用的情景有关,例如上面代码中mov bp,offset datamov bp,sp;因为offset涉及数据区,sp寄存器涉及堆栈区,所以进一步影响了BP在作为偏移地址时,对应哪一个段寄存器中的值为段地址。(因为个人才疏学浅,没有更多的证明方式,所以大胆地给出了自己的推断,如果该结论有错,欢迎大家在评论区留言交流(*^_^*)!)


临时小结:关于BX、DI、SI、BP作为偏移地址的用法

在8086CPU中,只有BX、DI、SI、BP这四个寄存器才可以在“[...]”中来进行内存单元的寻址。

 

“[...]”中,这四个寄存器可以单独出现(可以搭配常量):

  • [常量]
  • [寄存器]
  • [寄存器+常量]

也可以同时使用两个寄存器

  • “[寄存器+寄存器]”
  • “[寄存器+寄存器+常量]”

但是使用两个寄存器时,寄存器的搭配是有限制的,不能乱用;只能以4种组合出现:

注意:使用时顺序可颠倒,[bx+si]和[si+bx]是一样的!

当你使用三个或以上寄存器时,它会报错

 

“[...]”中使用寄存器BP,而且指令中没有显式的给出段地址,那么段地址就默认在寄存器 SS 中;BX,SI,DI 的段地址默认在寄存器 DS 中。

 

这几天看资料,又发现一个有趣的用法,特来补充一下!

 

——当你使用“[寄存器+寄存器]”形式时,你可以表达成“[寄存器][寄存器]”形式。

如:MOV DX,[si+bx]可以写成MOV DX,[si][bx]或者MOV DX,[bx][si]形式(因为MOV DX,[si+bx]原本就可以写成MOV DX,[bx+si])。

举个例子吧!

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT  
 
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
   	MOV SI,1
   	MOV BX,OFFSET DATA
   	mov cx,4
   	
again:
   	MOV DX,[si][bx]
   	inc si
   	mov ah,2
   	int 21h
   		loop again
   
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

但是使用“[寄存器][寄存器]”形式时,你依旧要按照下面搭配方式使用!

注意:使用时顺序可颠倒,[bx+si]和[si+bx]是一样的!

 不然,会报错使用不了!

 

——当你使用“[寄存器+寄存器+常量]”形式时,你可以表达成“常量[寄存器+寄存器]”“常量[寄存器][寄存器]”形式。

如:MOV DX,[si+bx+1]可以写成MOV DX,1[si][bx]或者MOV DX,1[bx][si]形式

举个例子吧!

DATAS SEGMENT
    data db 'ABCDEFGHIJKLMN'
DATAS ENDS

STACKS SEGMENT  
 
STACKS ENDS

CODES SEGMENT
    ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    
   	MOV SI,1
   	MOV BX,OFFSET DATA
   	mov cx,4
   	
again:
   	MOV DX,1[si][bx]
   	inc si
   	mov ah,2
   	int 21h
   		loop again
   
    MOV AH,4CH
    INT 21H
CODES ENDS
    END START

但是使用“常量[寄存器+寄存器]”“常量[寄存器][寄存器]”形式时,你依旧要按照下面搭配方式使用!

注意:使用时顺序可颠倒,[bx+si]和[si+bx]是一样的!

 不然,也会报错使用不了!

 

三、段寄存器:

8086CPU在访问内存时需要由相关的部件提供内存单元的段地址和偏移地址,再送入地址加法器中合成物理地址。段地址在8086CPU中段寄存器中存放;8086CPU有4个段寄存器CS、DS、SS、ES。

CS和IP:(指示了读取指令的地址!)

CS被称为代码段寄存器,IP称为指令指针寄存器。

在8086PC机中,任何时刻,设CS中的内容为M,IP中的内容为N,8086CPU将从内存Mx16+N单元开始,读取一条指令并执行。

也可以表述为:任何时刻,CPU将CS:IP指向的内容当做指令执行。

运行使用举例:

————执行mov ax,0123H

————执行mov bx,0003H 

————执行mov ax,bx 

————执行add ax,bx 

通过上面的例子我们可以将8086CPU的工作过程简述为:

①从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器。

②IP=IP+所读到指令的长度,从而指向下一条指令。

③执行指令,转到①,重复上述过程。

 

那么8086CPU刚刚启动时,或者复位时;CS和IP中的值是怎么样的呢?

 

在8086CPU加电启动或者复位后(即CPU刚刚开始工作时);CS和IP分别被设置为CS=FFFFH,IP=0000H;即在8086PC机在刚刚启动时,CPU从内存FFFFH单元读取指令执行,FFFF0H单元中的指令是8086PC机开机之后执行的第一条指令

 

那么如何修改CS和IP寄存器中的值来进一步控制执行哪一个指令呢?

 

对于一个寄存器AX的修改,我们可以利用mov指令对其进行修改(譬如:mov ax,3AH);对于8086CPU中大部分寄存器我们都可以使用mov指令对其进行修改,这里的mov指令被称为传送指令。但是对于寄存器CS和IP的修改,我们不能使用mov指令进行修改;8086CPU提供了另外的指令对于CS和IP寄存器的值进行修改;能够改变CS、IP内容的指令被统称为转移指令。以其中的jmp指令为例:

若想同时修改CS、IP的内容,可以使用形如“jmp 段地址:偏移地址”的指令完成;如:

  • jmp 2AE3:3,执行之后:CS=2AE3H,IP=0003H,CPU将会从2AE33H处读取指令

“jmp 段地址:偏移地址”指令的功能为:用指令中给出的段地址修改CS,偏移地址修改IP。

 

如果仅仅是想修改IP中的内容,可以使用形如“jmp 某一个合法寄存器”指令完成,如:

  • jmp ax

  • 指令执行之前:ax=1000H,CS=2000H,IP=0003H。

  • 指令执行之后:ax=1000H,CS=2000H,IP=1000H。

“jmp 某一个合法寄存器”指令的功能为:用寄存器中的值修改IP。


DS和[address]:(指示了访问内存数据的地址!)

在8086PC中,内存地址由段地址和偏移地址组成。8086CPU中有一个DS寄存器,通常用来存放要访问数据的段地址

以下面的代码进行举例讲解:

mov bx,1000H
mov ds,bx
mov al,[0]

这三条指令将10000H中的数据读到了al中(这10000H的计算方法和上面讲解的CS、IP的计算方式一样!)。

 

代码前两句我们使用mov指令完成了两种传送功能:①将数据直接送入寄存器中;②将一个寄存器中的内容送入另一个寄存器中。

代码第三句中,我们利用mov指令完成了——将一个内存单元的内容送入了一个寄存器中的功能

  • 其中“[...]”表示一个内存单元,“[...]”中的0表示内存单元的偏移地址该偏移地址结合寄存器DS中的段地址,就可以定位一个内存单元了

如果想将一个寄存器中的内容送入一个内存单元中,操作代码和上面类似:确定好DS(即段地址)的值,再利用mov [偏移地址],寄存器;将数据送进去。

 

如果你足够细心的话,你会发现一个多余的操作——为什么不直接把数据赋给寄存器DS呢?这样就不用另外借助一个寄存器BX了!

 

其实这不是多余操作,因为8086CPU不支持将数据直接送入段寄存器的操作,而寄存器DS刚好是一个段寄存器;所以你要是想对DS赋值,要借助一个其他的寄存器进行数据中转。(譬如上述代码中的BX寄存器),


SS和SP:(指示了栈顶的地址!)

如今的CPU都有栈的设计,8086CPU也不例外;相应的8086CPU提供了出栈和入栈指令,最基本的两个就是PUSH(入栈)和POP(出栈)。值得注意的是:8086CPU的出栈和入栈操作都是以字(16bit)为单位进行的!

在具体使用时,我们需要知晓栈顶在何处才能正确使用栈!

为了记录栈顶的位置上,8086CPU提供了两个寄存器:段寄存器SS寄存器SP栈顶的段地址存放在SS中,偏移地址存放在SP中。任何时刻,SS:SP指向栈顶元素。在执行push指令和pop指令时,CPU从SS和SP中得到栈顶的位置。

 

接下来以ax为例,介绍一下push和pop的执行机制!

 

push ax的执行,由两步组成:(先移后放!)

①SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元作为新的单元。

②将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。

 

pop ax的执行,也分为两步:(先出后移!)

①将SS:SP指向的内存单元处的数据送入ax中。

②SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

 

上述的push和pop操作存在的隐患——多次push或者pop,终会操作我们设定的堆栈区,这是时候就会发生越界问题了!

 

为了解决这个问题,我们可以设定记录着栈顶上限和栈顶下限的寄存器;这样就可以保证使用不越界了!但是可惜的是,8086CPU并没有没有提供这样的机制,它并不保证我们对于栈的操作不会越界!因此我们在使用栈时,需要通过自己的预先设定足够大的栈空间,以及小心栈空时的出栈操作来避免越界问题!


ES:附加段寄存器;装附加段的起始地址。

ES(Extra Segment) 段寄存器是一个附加段寄存器,当段寄存器不够用时,你可以使用ES来江湖救急,使用时和其他段寄存器一样!

 

四、标志寄存器:

CPU的内部的寄存器中,有一类特殊的寄存器(对于不同的处理机,其个数和结构都可能不同);它具有以下三种作用!

 这种特殊的寄存器在8086CPU中,被称为标志寄存器flag。8086CPU的标志寄存器有16位,其中存储的信息通常又被称为程序状态字PSW)。

flag和其他寄存器不一样,其他寄存器是用来存储数据的,都是一个寄存器作为一个整体具有一个含义的。但是flag寄存器是每一位都有专门的含义的,记录着特定的信息!

flag寄存器各位示意图

flag寄存器在上图中显示空白的位,在8086CPU中没有被使用,因此不具有任何的含义;其他被标识的位都具有特殊的含义。

 

如果你想对于了解每一位的具体功能的话,可以翻阅往期内容,点击下方链接直达!

8086汇编语言:标志寄存器的各个标志位的详细介绍(👈点击这里哦!(*^_^*))

 

Ending... ...

 

题外话:

在这个学期,我学习了8086汇编语言,因为学习课时的问题,以及这门课是一门选修课,所以有的地方老师讲解的不是很透彻,所以自己不明白的地方颇多!对于8086CPU中寄存器的内容教材仅仅只用了一段左右进行介绍,很多不常使用寄存器就是一句话带过,甚至直接没有介绍,顿时对学校选的这本教材感到失望,太功利性了!

然后自己去网上找相关博主对于这个知识点的介绍,然后咋说呢!那个排版一言难尽,很多都是相关书籍的截图和大段复制,显得很混乱,实在是看不下去了!

最后决定自己选一本比较好讲解8086汇编语言的书籍,最终选定了王爽老师的汇编语言!所以看过这本书的朋友可能会发现博客中部分配图直接用了书中的图!

自己花了两天时间把这本书基本看完了,因为8086CPU中的寄存器适用于不同的场景,所以你要看了相关的前提知识介绍之后才能更好的理解寄存器作用;这就导致在这本书中相关寄存器的介绍均匀的分散,而且有的过于偏门的寄存器这本书也没怎么介绍;为了用自己的话将书中介绍的相关寄存器的知识整理并且串起来,还要将没怎么介绍的寄存器知识自己查阅资料,并验证信息的正确性;对于不理解的地方还要自己设计测试代码验证相关的理论... ...

这一套流程做下来,我又断断续续花了四五天,我很享受这个过程,自己也不觉得累,可能这就是热爱吧!

我写这一段话的目的就是——记住自己此时此刻的心境,保持下去,你终将会登上自己的高峰!

最后送在万千博客中点到我这篇博客,并且耐心的看到最后的有缘人一句话吧!“因为害怕自己并非明珠而不敢刻苦琢磨,又因为有几分相信自己是明珠,而不能与瓦砾碌碌为伍!”

 

这次是真的Ending了!(*^_^*)

  • 10
    点赞
  • 36
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Old萬

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值