-
8086CPU一共有14个寄存器
-
AX,BX,CX,DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器
-
8086CPU的AX,BX,CX,DX这4个寄存器都可分为两个可独立使用的8位寄存器
-
几条汇编指令:
汇编指令 控制CPU完成的操作 用高级语言的语法描述 mov ax,18 将18送入寄存器AX AX=18 mov ah,78 将78送入寄存器AH AH=78 add ax,8 将寄存器AX中的数值加上8 AX=AX+8 mov ax,bx 将寄存器BX中的数据送入寄存器AX AX=BX add ax,bx 将AX和BX中的数值相加,结果存在Ax中 AX=AX+BX -
在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的
-
物理地址=段地址x16+偏移地址
-
一个段的起始地址一定是16的倍数
-
偏移地址为16位,16位地址的寻址能力为64KB
-
一个段的长度最大为64KB
-
段寄存器:CS,DS,SS,ES
-
CS
和IP
指示了CPU当前要读取指令的地址 -
CPU将
CS:IP
指向的内容当作指令执行 -
同时修改
CS
和IP
:jmp 段地址:偏移地址
例如:
jmp 1000:3
-
仅修改
IP
:jmp 某一合法寄存器
例如:
jmp ax
-
子单元:存放一个字型数据(16位)的内存单元,有两个地址连续的内存单元组成
-
将起始地址为N的字单元简称为N地址单元
-
CPU要读写一个内存单元时候,必须先给出内存单元的地址
-
内存地址由段地址和偏移地址组成
-
DS寄存器通常用来存放要访问数据的段地址
-
将10000H(1000:0)中的数据读到al中
mov bx,1000H mov ds,bx mov al,[0]
-
mov指令可完成两种传送:
- 将数据直接送入寄存器
- 将一个寄存器中的内容送到另一个寄存器
-
[0]
中的0
表示内存单元的偏移地址,内存单元的段地址是DS
中的数据 -
8086CPU不支持将数据直接送入段寄存器
-
只要在
mov
指令中给出16位的寄存器就可以进行16位数据的传送 -
将
al
中的数据送入内存单元10000H
中:mov bx,1000H mov ds,bx mov [0],al
-
sub bx,ax
表示bx=bx-ax
-
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 寄存器,数据 例:
-
将
123B0H~123B9H
的内存单元定义为数据段,现在要累加这个数据段中的前3个单元中的数据mov ax,123BH mov ds,ax mov al,0 add al,[0] add al,[1] add al,[2]
-
累加数据段中的前3个字型数据
mov ax,123BH mov ds,ax mov ax,0 add ax,[0] add ax,[2] add ax,[4]
-
8086CPU的入栈和出栈都是以字为单位进行的
-
任意时刻,
SS:SP
指向栈顶元素 -
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 寄存器
- push 段寄存器
- pop 段寄存器
- push 内存单元
- pop 内存单元
-
栈为空时候,
SS:SP
指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2 -
将
10000H~1000FH
这段空间当作栈,交换AX
和BX
中的数据mov ax,1000H mov ss,ax mov sp,0010H ;初始化栈顶,1000:000E+2=1000:0010H push ax push bx pop ax pop bx
-
在
10000H
处写入字型数据2266H
:mov ax,1000H mov ds,ax mov ax,2266H mov [0],ax
或
mov ax,1000H mov ss,ax mov sp,2 mov ax,2266H push ax
-
如果将
10000H~1FFFFH
这段空间当作栈段,此时SS=1000h
,SP=0000H
,因为:(向下生长)- 栈最底部字单元的地址为
1000:FFFEH
,所以+2后变为10000H
,取低16位,即SP=0000H
- 栈最底部字单元的地址为
-
简单的汇编语言源程序
assume cs:codesg codesg segment mov ax,0123H mov bx,0456H add ax,bx add ax,ax mov ax,4C00H int 21H codesg ends end
-
[bx]表示一个内存单元,它的偏移地址在
bx
中,段地址在ds
中 -
为了描述简洁,用符号“()”来表示一个寄存器或一个内存单元中的内容,例如:
(ax)
表示ax
中的内容 -
idata
表示常量 -
mov ax,[bx]
:bx
中存放的数据作为一个偏移地址EA
,段地址SA
默认在ds
中,将SA:EA
处的数据送入ax
中 -
inc bx
的含义是bx
中的内容加1,相当于bx=bx+1
-
CPU执行loop指令的时候,要进行两步操作:
(cx)=(cx)-1
- 判断cx中的值,不为零则转至标号处执行程序
-
编程计算 2 12 2^{12} 212
assume cs code code segment mov ax,2 mov cx,11 s: add ax,ax loop s mov ax,4c00h int 21h code ends end
-
用加法计算 123 × 236 123\times236 123×236,结果存在
ax
中assume cs:code code segment mov ax,0 mov cx,123 s: add ax,236 loop s mov ax,4c00h int 21h code ends end
-
计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中
assume cs:code code segment mov ax,0ffffh mov ds,ax mov bx,0 mov dx,0 mov cx,12 s: mov al,[bx] mov ah,0 add dx,ax inc bx loop s mov ax,4c00h int 21h code ends end
-
用于显式地指明内存单元的段地址的**“ds:”,“cs:”,“ss:”,“es:”**,在汇编语言中称为段前缀
-
段前缀使用
- mov ax,ds:[bx]
- mov ax,cs:[bx]
- mov ax,ss:[bx]
- mov ax,es:[bx]
- mov ax,ss:[0]
- mov ax,cs:[0]
-
将内存
ffff:0~ffff:b
单元中的数据复制到0:200~0:20b单元中assume cs:code code segment mov ax,0ffffh mov ds,ax mov ax,0020h mov es,ax mov bx,0 mov cx,12 s: mov dl,[bx] mov es:[bx],dl inc bx loop s mov ax,4c00h int 21h code ends end
-
编程计算以下8个数据的和,结果存在ax寄存器中
0123h 0456h 0789h 0abch 0defh 0fedh 0cbah 0987h
assume cs:code code segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h start: mov bx,0 mov ax,0 mov cx,8 s: add ax,cs:[bx] add bx,2 loop s mov ax,4c00h int 21h code ends end start ;指明程序的入口在start处
dw
:定义字型数据- 这8个数据由于它们在代码段中,程序在运行时候CS中存放代码段的段地址,所以可以从CS中得到它们的段地址
-
利用栈,将程序中定义的数据逆序存放
assume cs:codesg codesg segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 start: mov ax,cs mov ss,ax mov sp,30h ;将栈顶ss:ip指向cs:30h,2eh+2=30h mov bx,0 mov cx,8 ;循环8次 s: push cs:[bx];入栈 add bx,2 ;地址+2,因为定义的是字型数据 loop s mov bx,0 mov cx,8 s0: pop cs:[bx] add bx,2 loop s0 mov ax,4c00h int 21h codesg ends end start
-
将数据、代码、栈放入不同的段,实现上面所述
assume cs:code,ds:data,ss:stack data segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h data ends stack segment dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 stack ends code segment start: mov ax,stack mov ss,ax mov sp,20h ;将栈顶ss:ip指向cs:20h,1eh+2=20h mov ax,data mov ds,ax ;ds指向data段 mov bx,0 ;ds:bx指向data段中的第一个单元 mov cx,8 ;循环8次 s: push [bx] ;ds:bx数据入栈 add bx,2 ;地址+2 loop s mov cx,8 mov bx,0 ;清零 s0: pop [bx] ;出栈到ds:bx add bx,2 ;地址+2 loop s0 mov ax,4c00h int 21h code ends end start
-
and
指令:逻辑与指令,按位进行与运算 -
or
指令:逻辑或指令,按位进行或运算 -
大小写转换
assume cs:codesg,ds:datasg datasg segment db 'BaSiC' db 'iNfOrMatiOn' datasg ends codesg segment start: mov ax,datasg mov ds,ax ;设置ds指向datasg段 mov bx,0 mov cx,5 s: mov al,[bx] and al,11011111B mov [bx],al inc bx loop s mov bx,5 ;设置(bx)=5,ds:bx指向'iNfOrMatiOn'的第一个字母 mov cx,11 s0: mov al,[bx] or al,00100000B mov [bx],al inc bx ;相当于bx=bx+1 loop s0 mov ax,4c00h int 21h codesg ends end start
-
[bx+idata]
:表示一个内存单元,它的偏移地址为**(bx)+idata** -
mov ax,[bx+200]
:讲一个内存单元的内容送入ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为bx中的数值加上200,段地址在ds中,也可以写成:mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
-
大小写转换程序
assume cs:codesg,ds:datasg datasg segment db 'BaSiC' db 'MinIX' datasg ends codesg segment start: mov ax,datasg mov ds,ax mov bx,0 mov cx,5 ;5个字符 s: mov al,[bx]; and al,11011111B mov [bx],al mov al,[5+bx] ;也可以这样:mov,al,5[bx];mov al,[bx].5 or al,00100000B mov [5+bx],al inc bx loop s mov ax,4c00h int 21h codesg ends end start
-
si
和di
是8086CPU中和bx
功能相近的寄存器,si
和di
不能够分成两个8位寄存器来使用,例:下面三组指令实现了相同的功能 1. mov bx,0 mov ax,[bx] 2. mov si,0 mov ax,[si] 3. mov di,0 mvo ax,[di] 下面的三组指令也实现了相同的功能 1. mov bx,0 mov ax,[bx+123] 2. mov si,0 mov ax,[si+123] 3. mov di,0 mov ax,[di+123]
-
寻址方式小结:
寻址方式 含义 名称 常用格式举例 [idata] EA=idata,SA=(ds) 直接寻址 [idata] [bx] EA=(bx),SA=(ds) 寄存器间接寻址 [bx] [si] EA=(si),SA=(ds) 寄存器间接寻址 [si] [di] EA=(di),SA=(ds) 寄存器间接寻址 [di] [bp] EA=(bp),SA=(ss) 寄存器间接寻址 [bp] [bx+idata] EA=(bx)+idata,SA=(ds) 寄存器相对寻址 [bx].idata [si+idata] EA=(si)+idata,SA=(ds) 寄存器相对寻址 idata[si] [di+idata] EA=(di)+idata,SA=(ds) 寄存器相对寻址 idata[di] [bp+idata] EA=(bp)+idata,SA=(ss) 寄存器相对寻址 [bp][idata] [bx+si] EA=(bx)+(si),SA=(ds) 基址变址寻址 [bx][si] [bx+di] EA=(bx)+(di),SA=(ds) 基址变址寻址 [bx][di] [bp+si] EA=(bp)+(si),SA=(ss) 基址变址寻址 [bp][si] [bp+di] EA=(bp)+(di),SA=(ss) 基址变址寻址 [bp][di] [bx+si+idata] EA=(bx)+(si)+idata,SA=(ds) 相对基址变址寻址 [bx].idata[si] [bx+di+idata] EA=(bx)+(di)+idata,SA=(ds) 相对基址变址寻址 idata[bx][di] [bp+si+idata] EA=(bp)+(si)+idata,SA=(ss) 相对基址变址寻址 [bp].idata[si] [bp+di+idata] EA=(bp)+(di)+idata,SA=(ss) 相对基址变址寻址 idata[bp][di] -
指令
mov ax,[bx+si+idata]
:讲一个内存单元的内容送入ax,这个内存单元的程度为2字节(字单元),存放一个字,偏移地址为bx中的数值加上si中的数值再加上idata,段地址在ds中,也可写成:mov ax,[bx+200+si]
mov ax,[200+bx+si]
mov ax,200[bx][si]
mov ax,[bx].200[si]
mov ax,[bx][si].200
-
汇编语言中数据位置的表达
-
立即数
mov ax,1 mov add bx,2000h or bx,00010000b mov al,'a'
-
寄存器
mov ax,bx mov ds,ax push bx mov ds:[0],bx push ds mov ss,ax mov sp,ax
-
段地址(SA)+偏移地址(EA)
段地址默认在ds中 mov ax,[0] mov ax,[di] mov ax,[bx+8] mov ax,[bx+si] mov ax,[bx+si+8] 段地址默认在ss中 mov ax,[bp] mov ax,[bp+8] mov ax,[bp+si] mov ax,[bp+si+8] 显性给出段地址 mov ax,ds:[bp] mov ax,es:[bx]
-
-
指令要处理的数据长度根据:
-
通过寄存器名指明要处理的数据的大小:
字操作 mov ax,1 mov bx,ds:[0] mov ds,ax mov ds:[0],ax inc ax add ax,1000 字节操作 mov al,1 mov al,bl mov al,ds:[0] mov ds:[0],al inc al add al,100
-
在没有寄存器名存在的情况下,用操作符
X ptr
指明内存单元的长度,X
在汇编指令中可以为word
或byte
:字操作 mov word ptr ds:[0],1 inc word ptr [bx] inc word ptr ds:[0] add word ptr [bx],2 字节操作 mov byte ptr ds:[0],1 inc byte ptr [bx] inc byte ptr ds:[0] add byte ptr [bx],2
-
其他方法
有些指令默认了访问的是字单元还是字节单元,比如,
push [1000h]
就不用指明访问的是字单元还是字节单元,因为push
指令只进行字操作
-
-
div
指令:div是除法指令,使用div做除法的时候应注意以下问题:- 除数:有8位和16位两种,在一个reg或内存单元中
- 被除数:默认放在AX或DX和AX中,如果除数为8位,被除数则为16位,默认在AX中存放;如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位
- 结果:如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数;如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数
-
div格式:
-
div reg
-
div 内存单元
-
例:
div byte ptr ds:[0]
含义:**(al)=(ax)/((ds)*16+0)的商,(ah)=(ax)/((ds)*16+0)**的余数
-
-
伪指令
dd
:用来定义dword(double word,双字)型数据的 -
dup
:和db、dw、dd等数据定义伪指令配合使用的,用来进行数据的重复,例:-
db 3 dup (0)
定义了3个字节,它们的值都是0,相当于
db 0,0,0
-
db 3 dup (0,1,2)
定义了9个字节,它们的值是0、1、2、0、1、2、0、1、2
-
格式:
- db 重复的次数 dup (重复的字节型数据)
- dw 重复的次数 dup (重复的字型数据)
- dd 重复的次数 dup (重复的双字型数据)
-
-
可以修改
IP
,或同时修改CS
和IP
的指令统称为转移指令 -
8086CPU的转移行为有以下几类:
- 只修改IP时,称为段内转移,比如:
jmp ax
- 同时修改CS和IP时,称为段间转移,比如:
jmp 1000:0
- 只修改IP时,称为段内转移,比如:
-
由于转移指令对
IP
的修改范围不同,段内转移又分为:- 短转移:IP的修改范围为
-128~127
- 近转移:IP的修改范围为
-32768~32767
- 短转移:IP的修改范围为
-
操作符offset:在汇编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址。例如
assume cs:codesg codesg segment start: mov ax,offset start ;相当于mov ax,0 s: mov ax,offset s ;相当于mov ax,3 codesg ends end start
在上面的程序中,
offset
操作符取得了标号start
和s
的偏移地址0
和3
,所以指令mov ax,offset start
相当于指令mov ax,0
,因为start是代码中的标号,它所标记的指令是代码中的第一条指令,偏移地址为0
;mov ax,offset s
相当于指令mov ax,3
,因为s是代码段中的标号,它所标记的指令是代码段中的第二条指令,第一条指令长度为3个字节,则s
的偏移地址为3 -
jmp
指令:是无条件的转移指令,可以只修改IP
,也可以同时修改CS
和IP
,jmp
指令要给出两种信息:- 转移的目的地址
- 转移的距离(段间转移、段内短转移、段内近转移)
不同的给出目的地址的方法,和不同的转移的位置,对应有不同格式的jmp指令
-
段内短转移:
jmp short 标号
(转到标号处执行指令),对IP的修改范围是**-128**~127,也就是说,它向前转移时可以最多越过128个字节,向后转移可以最多越过127个字节 -
jmp far ptr 标号
实现的是段间转移,又称为远转移,功能如下:(CS)=标号所在段的段地址;(IP)=标号在段中的偏移地址
far ptr
指明了指令用标号的段地址和偏移地址修改CS和IP -
转移地址在内存中的
jmp
指令:-
jmp word ptr 内存单元地址
(段内地址),功能:从内存单元地址处开始存放着一个字,是转移的目的偏移地址,例如:mov ax,0123h mov ds:[0],ax jmp word ptr ds:[0]
执行后,(IP)=0123h
-
jmp dword ptr 内存单元地址
(段间转移)功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址,(CS)=(内存单元地址+2),(IP)=(内存单元地址)
-
-
jcxz
指令为有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址,对IP的修改范围都为:-128~127
- 指令格式:
jcxz
标号(如果(cx)=0,转移到标号处执行) - 操作:当(cx)=0时,(IP)=(IP)+8位位移
- 8位位移=标号处的地址-jcxz指令后的第一个字节的地址
- 8位位移的范围为-128~127,用补码表示
- 当(cx)≠0时,什么也不做(程序向下执行)
- 指令格式:
-
loop指令为循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对IP的修改范围都为:-128~127
- 指令格式:
loop 标号
((cx)≠0,转移到标号处执行) - 操作:
- (cx)=(cx)-1
- 如果(cx)≠0,(IP)=(IP)+8位位移
- 8位位移=标号处的地址-
loop
指令后的第一个字节的地址 - 8位位移的范围为-128~127,用补码表示
- 如果(cx)=0,什么也不做(程序向下执行)
- 指令格式:
-
ret
和call
指令都是转移指令,它们都修改IP
,或同时修改CS
和IP
,它们经常被共同用来实现子程序的设计 -
ret
指令用栈中的数据,修改IP
的内容,从而实现近转移 -
retf
指令用栈中的数据,修改CS
和IP
的内容,从而实现远转移 -
CPU执行
ret
指令时,进行下面两步操作:- (IP)=((ss)*16+(sp))
- (sp)=(sp)+2
相当于:
pop IP
-
CPU执行
reft
指令时,进行下面四步操作:- (IP)=((ss)*16+(sp))
- (sp)=(sp)+2
- (CS)=((ss)*16+(sp))
- (sp)=(sp)+2
相当于:
pop IP pop CS
-
下面的程序中,
ret
指令执行后,(IP)=0
,CS:IP
指向代码段的第一条指令assume cs:code stack segment db 16 dup (0) stack ends code segment mov ax,4c00h int 21h start: mov ax,stack mov ss,ax mov sp,16 mov ax,0 mov bx,0 ret code ends end start
-
下面的程序中,
retf
指令执行后,CS:IP
指向代码的第一条指令assume cs:code stack segment db 16 dup (0) stack ends code segment mov ax,4c00h int 21h start: mov ax,stack mov ss,ax mov sp,16 mov ax,0 push cs push ax mov bx,0 retf code ends end start
-
CPU执行
call
指令时,进行两步操作:- 将当前的
IP
或CS
和IP
压入栈中 - 转移
- 将当前的
-
call
指令不能实现短转移 -
192