4.汇编之DS寄存器和[address]、字数据的传递、大小端、指令、数据段、中断、栈

1.DS寄存器

1.1 CPU要读写一个内存单元时,必须要先给出这个内存单元的地址,在8086中,内存地址由段地址和偏移地址组成。
1.2 8086中有一个DS段寄存器,通常用来存放要访问数据的段地址。
1.3 案例

mov bx,1000H
mov ds,bx
mov al,[0]
上面3条指令将10000H(1000:0)中的数据读到al中。

    1. 把1000H传送到bx中
    1. bx把内容传送给ds
    1. [0]表示一个内存单元,0表示内存单元的偏移地址,mov al,[0] 等价于 mov al, ds:[0],[]中的0说明这个内存单元的偏移地址是0,它的段地址默认房子ds中,指令执行时,8086会自动从ds中取出。并传送给al.(mov al ,[address]的意思讲DS:address中的内存数据复制给al寄存器中)
  • 4.由于al是8位寄存器,所以是将一个字节的数据赋值给al寄存器。
1.4练习
  • 案例一
    将al中的数据送入内存单元10000H中
    mov bx,1000H
    mov ds,bx
    mov [0],al
    段地址:1000H
    偏移地址:0H
    段地址在ds寄存器中
    al把数据传送到内存单元为10000H中。

  • 案例二
    将寄存器ah的值送入20022H中,怎么写?
    段地址:2000H
    偏移地址: 22H
    mov ax ,2000H
    mov ds ,ax
    mov [22H],ah

2.字数据的传递

2.1 像mov这样的指令在寄存器和内存之间进行字节型数据的传送。
2.2

如图,写出下面执行后寄存器ax,bx,cx中的值。
mov ax,1000H
mov ds,ax
mov ax,[0]
mov bx,[2]
mov cx,[1]
add bx,[1]
add cx,[2]

分析:
段地址:1000H
mov ax,[0] 取出10000的值赋值给ax,由于2个字节,低八位高八位,所以ax = 1123
mov ax,1123 // ax =1123
mov bx,6622 // bx = 6622
mov cx,2211 // cx = 2211
add bx,2211 // bx = 8833
add cx,6622 // cx = 8833
所以 ax=1123,bx=8833,cx=8833

3.大小端

3.1大端模式

指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高低\低高) (Big Endian)。

3.2小端模式

小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高高\低低) (Little Endian)

3.3大小端存放内容
大小端存放内容.png

大端:PowerPC,IBM,Sun
小端:X86,DEC
ARM既可以工作在大端模式,也可以工作在小端模式。

4.指令

4.1mov指令(传送指令)

mov指令一下表现形式:

  • mov 寄存器,数据
    比如: mov ax,8
  • mov 寄存器,寄存器
    比如: mov ax,bx
  • mov 寄存器,内存单元
    比如: mov ax,[0]
  • mov 内存单元,寄存器
    比如 mov [0],ax
  • mov 段寄存器,寄存器
    比如: mov ds,ax
  • mov 寄存器,段寄存器
    比如: mov ax,ds

注意:mov 内存单元,内存单元 是不允许的,比如 mov [0] [1]

4.2 add指令(加法指令)

add指令同mov 一样,都有两个操作对象。
它的表现形式

  • add 寄存器,数据
    比如: add ax ,8
  • add 寄存器,寄存器
    比如: add ax,bx
  • add 寄存器,内存单元
    比如: add ,ax,[0]
  • add 内存单元,寄存器
    比如: add [0],ax
4.3sub指令(减法指令)

sub指令同mov一样,也是有两个操作对象
它的表现形式:

  • sub 寄存器,数据
    比如:sub ax,9
  • sub 寄存器,寄存器
    比如:sub ax,bx
  • sub 寄存器,内存单元
    比如:sub ax,[0]
  • sub 内存单元,寄存器
    比如:sub [0],ax

4.数据段

4.1 对于8086来说,在编程时,可以根据需要,将一组内存单元定义为一个段
4.2 我们可以将一组长度为N(N<=64KB)、地址连续,起始地址为16的倍数的内存单元当作专门存储数据的内存空间,称为数据段。

比如:123B0H ~ 123B9H这段内存空间来存放数据,我们可以认为123B0H~123B9H这段内存是一个内存段,它的段地址为123BH,长度为10个字节。

4.3如何访问数据段中的数据呢?

用ds存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。

5.编写完整的汇编程序

5.1使用汇编语言编写一个完整的程序,步骤:
  • 编写源代码,文件名扩展名为.asm
  • 编译、链接(可以使用微软的MASM编译器)
  • 调试,运行
5.2 第一个程序
第一个程序.png

汇编语言由2类指令组成

  • 汇编指令
    比如 mov,add,sub等
    有对应的机器指令,可以被编译为机器指令,最终被CPU执行

  • 伪指令
    比如上图,assume,segment,ends,end等
    没有对应的机器指令,由编译器解析,最终不被CPU执行。

  • segmen和ends的作用是定义一个段,segment代表一个段的开始,ends代表一个段的结束,使用格式为
    段名 segment
    :
    段名 ends

  • 一个有意义的汇编程序中,至少要有一个段作为代码段存放代码

  • assume:将用作代码段的code段和CPU中的cs寄存器关联起来。

  • end : 编译器遇到end时,就结束对源程序的编译

  • 上图退出程序代码
    mov ah,4ch
    int 21h -->>interupt(中断)
    int 不是数据类型int,而是interupt中断的意思

6.中断

6.1什么是中断?

中断时由于软件或硬件的信号,使得CPU暂停当前的任务,转而去执行另一段子程序。
在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,CPU暂时中止当前程序的执行转处理这个新情况的过程就叫做中断。

6.2中断的分类
  • 硬中断(外中断),由外部设备(比如网卡、硬盘)随机引发的,比如当网卡收到数据包的时候,就会发出一个中断。
  • 软中断(内中断),由执行中断指令产生的,可以通过程序控制出发。
6.3中断的本质
中断本质.png
  • 中断时一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。
  • 如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。
  • 处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。
6.3怎么产生中断?
  • 可以通过指令int n 产生中断
  • n是中断码,内存中有一张中断向量表,用来存放中断码对应中断处理程序的入口地址。
  • CPU在接受中断信号后,暂停当前正在执行的程序,跳转到中断码对应的中断向量表地址处,去执行中断处理程序
6.4常见中断
  • int 10h用于执行BIOS中断
  • int 3是“断点中断”,用于调试程序
  • int 21h 用于执行DOS系统功能调用,AH寄存器存储功能号

7.栈

7.1栈?

栈是一种具有特殊的访问方式的存储空间(后进先出,Last In Out First,LIFO)


栈的模型.jpg

如图,栈的两个基本操作:

  • 入栈 PUSH
  • 出栈 POP

为什么后进先出呢?

  • 入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。
  • 栈顶的元素总是最后入栈,需要出栈时,从栈顶取出。

比如:push ax -->> 将寄存器ax中的数据送入栈中。
pop ax -->> 从栈顶取出数据送入ax。

7.2 8086中如何为CS,DS,SS分配段地址?
  • 8086会将CS作为代码段的段地址,将CS:IP指向的指令作为下一条需要取出执行的指令。
  • 8086会将DS作为数据段的段地址,mov ax,[address]的内存数据放到ax寄存器中
  • 8086会将SS作为栈段的段地址,任意时候,SS:SP指向栈顶元素。(push指令和pop指令执行时,CPU从SS和SP中得到栈顶的地址。)
7.3 push ax 流程
  • push ax - 1


    push ax--1.png

如图,

  • SS中的内容:1000H

  • SP中的内容:000EH

  • 栈顶的段地址:1000:000E ,即1000EH

  • 假设AX 中的内容是2266H

  • push ax - 2


    push ax -2.png

    如图,CPU执行push ax,(分析):

  • SP = SP - 2

  • SP 中的内容变为:000CH

  • SS:SP指向1000:000C,

  • 栈顶的地址变为1000:000CH,即1000CH

这里是小端模式,存储方式高高低低,数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。

  • push ax - 3


    push ax -3.png

    如图,CPU执行push ax,(分析):

  • 把ax的数据送入SS:SP指向的内存单元
  • 高字节存放在高地址,低字节存放低地址,1000DH中的内容是高八位22,1000DH中的内容是低八位66。
7.4 POP流程
  • pop ax -1


    pop ax -1.png

    如图,

  • SS中的内容:1000H

  • SP中的内容:000CH

  • 栈顶的段地址:1000:000CH,即1000EH

  • pop ax -2


    pop ax -2.png

    如图,CPU执行pop ax,(分析):

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

  • ax =2266H

  • pop ax -3


    pop ax -3.png

    如图,CPU执行 pop ax,(分析):

  • SP = SP + 2

  • SS:SP 指向 1000EH

7.5 思考
栈空间.png

如图,如果将10000H~1000FH这段空间当作栈,初始状态栈是空的,此时,SS = 1000H,SP是多少?
分析:

  • 栈空间是10000H~1000FH,SS=1000H
  • 栈空间大小为16字节,栈最底部的字单元地址为1000:0000EH
  • SS:SP 指向栈顶,当栈中只有一个元素的时候,SS=1000H,SP=000EH
  • 栈为空,出栈后,SP = SP+2,SP原来为000EH
  • 加2后,SP=10H
  • 所以,栈为空的时候,SS=1000H,SP=10H
7.6 栈顶超界的问题
  • 执行push栈顶超界


    执行push栈顶超界.png

如图,当push 的时候,会超出栈空间

  • 执行pop栈顶超界


    执行pop栈顶超界.png

当pop的时候,栈顶超出栈空间

  • 栈顶超界问题
    1.当栈满的时候再使用push指令入栈,或栈空的时候再使用pop指令出栈,都将发生栈顶越界问题
    2.栈顶越界是危险的,入栈出栈时,数据和代码意外改写,将会引发一连串的错误。
    3.8086CPU不保证我们对栈的操作不会越界。

注意:1.编程的时候注意栈顶超界,2.用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界3.执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。

7.7 push、pop指令
  • push指令的表现形式:

    1. push 寄存器 ;将一个寄存器中的数据入栈
    2. push 段寄存器;将一个段寄存器中的数据入栈
    3. push 内存单元 ; 将一个内存子单元处的字入栈
  • pop指令的表现形式:

    1. pop 寄存器 ;出栈,用一个寄存器接收出栈的数据
    2. pop 段寄存器 ;出栈,用一个段寄存器接收出栈的数据
    3. pop 内存单元 ;出栈,用一个内存字单元接收出栈的数据
  • 案例一:
    mov ax,1000H
    mov ds,ax
    push [0]
    pop [2]
    分析:
    第一步:1000H送入ax中
    第二步:内存单元的段地址要放在ds中
    第三步:将1000:0处的字压入栈中
    第四步:出栈,出栈的数据送入1000:2处

  • 案例二:
    将10000H~1000FH这段空间当作栈,初始状态栈是空的,将AX,BX,DS中的数据入栈。
    分析:
    mov ax, 1000H
    mov ss, ax
    mov sp,0010H
    push ax
    push bx
    push ds

  1. 段地址是1000H,传送给ax,因为不能直接向段寄存器传送,所以需要ax中转。
    2.因为栈为空,所以sp=0010H
    3.将ax,bx,ds中的数据入栈
  • 案例三:
    将10000H~10000FH这段空间当作栈,初始状态栈是空的,设置AX=001AH,BX=001BH,将AX,BX中的数据入栈,然后将AX,BX清零,从栈中恢复AX,BX原来的内容。
    分析:
    mov ax ,1000H ;1000H传送给ax
    mov ss,ax ;ax传送给ss段寄存器
    mov sp,0010H;初始化栈顶
    mov ax,001AH ;001AH传送给ax
    mov bx,001BH ; 001BH传送给bx
    push ax ;ax入栈
    push bx ;bx入栈
    sub ax ,ax ;ax清零
    sub bx,bx ;bx清零
    pop bx ;bx恢复内容
    pop ax;ax恢复内容
7.8 栈段
  • 如何使用push,pop等栈操作指令访问我们定义的栈段?
    用SS存放栈段的段地址,用SP存放栈顶的偏移地址

  • 段的理解?
    1.段存放数据-->>数据段,将它的段地址放在DS中,用mov,add,sub等指令,数据段中内容当作数据来访问。
    2.段存放代码-->>代码段,段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就会执行代码段中的指令
    3.段当作栈-->>栈段,段地址在SS中,偏移地址放在SP中,CPU在需要进行栈操作的时候,比如执行push,pop指令等,栈段当作栈空间来用。

  • 案例一:


    案例.png

补全代码,可以将10000H1000FH中的8个字,逆序复制到20000H2000FH
mov ax,1000H
mov ds,ax
;由于是逆序排序
(mov ax,2000H)
(mov ss,ax)
(mov sp,0010H)
push [0]
push [2]
push [4]
push [6]
push [8]
push [A]
push [C]
push [E]

8.总结

以上内容包含DS寄存器和[address]、字数据的传递、大小端、指令、数据段、中断、栈等知识点。
这章的内容比较多,如果文章能够看一遍下来,这些知识点基本可以掌握。
下一章,我们继续讲解指令使用。
写完这篇文章已经凌晨2点了,
夜深了,该睡觉了。
文章继续更新中....

推荐阅读更多精彩内容