8086CPU一共有14个寄存器
AX,BX,CX,DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器
8086CPU的AX,BX,CX,DX这4个寄存器都可分为两个可独立使用的8位寄存器
几条汇编指令:
汇编指令控制CPU完成的操作用高级语言的语法描述mov ax,18将18送入寄存器AXAX=18mov ah,78将78送入寄存器AHAH=78add ax,8将寄存器AX中的数值加上8AX=AX+8mov ax,bx将寄存器BX中的数据送入寄存器AXAX=BXadd 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],alsub bx,ax表示bx=bx-ax
mov指令可以有以下几种形式:
mov 寄存器,数据 例:mov ax,8mov 寄存器,寄存器 例:mov ax,bxmov 寄存器,内存单元 例:mov ax,[0]mov 内存单元,寄存器 例:mov [0],axmov 段寄存器,寄存器 例:mov ds,axmov 寄存器,段寄存器 例:mov ax,ds将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 startand指令:逻辑与指令,按位进行与运算
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 startsi和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的修改范围为-128~127近转移:IP的修改范围为-32768~32767操作符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 startCPU执行call指令时,进行两步操作:
将当前的IP或CS和IP压入栈中转移call指令不能实现短转移
192