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中。
-
- 把1000H传送到bx中
-
- bx把内容传送给ds
-
- [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大小端存放内容
大端: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 第一个程序
汇编语言由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中断的本质
- 中断时一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。
- 如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。
- 处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。
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)
如图,栈的两个基本操作:
- 入栈 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
如图,
SS中的内容:1000H
SP中的内容:000EH
栈顶的段地址:1000:000E ,即1000EH
假设AX 中的内容是2266H
-
push ax - 2
如图,CPU执行push ax,(分析):
SP = SP - 2
SP 中的内容变为:000CH
SS:SP指向1000:000C,
栈顶的地址变为1000:000CH,即1000CH
这里是小端模式,存储方式高高低低,数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
-
push ax - 3
如图,CPU执行push ax,(分析):
- 把ax的数据送入SS:SP指向的内存单元
- 高字节存放在高地址,低字节存放低地址,1000DH中的内容是高八位22,1000DH中的内容是低八位66。
7.4 POP流程
-
pop ax -1
如图,
SS中的内容:1000H
SP中的内容:000CH
栈顶的段地址:1000:000CH,即1000EH
-
pop ax -2
如图,CPU执行pop ax,(分析):
将SS:SP指向的内存单元处的数据送入ax
ax =2266H
-
pop ax -3
如图,CPU执行 pop ax,(分析):
SP = SP + 2
SS:SP 指向 1000EH
7.5 思考
如图,如果将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 的时候,会超出栈空间
-
执行pop栈顶超界
当pop的时候,栈顶超出栈空间
- 栈顶超界问题
1.当栈满的时候再使用push指令入栈,或栈空的时候再使用pop指令出栈,都将发生栈顶越界问题
2.栈顶越界是危险的,入栈出栈时,数据和代码意外改写,将会引发一连串的错误。
3.8086CPU不保证我们对栈的操作不会越界。
注意:1.编程的时候注意栈顶超界,2.用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界3.执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。
7.7 push、pop指令
-
push指令的表现形式:
- push 寄存器 ;将一个寄存器中的数据入栈
- push 段寄存器;将一个段寄存器中的数据入栈
- push 内存单元 ; 将一个内存子单元处的字入栈
-
pop指令的表现形式:
- pop 寄存器 ;出栈,用一个寄存器接收出栈的数据
- pop 段寄存器 ;出栈,用一个段寄存器接收出栈的数据
- 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
- 段地址是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指令等,栈段当作栈空间来用。-
案例一:
补全代码,可以将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点了,
夜深了,该睡觉了。
文章继续更新中....