汇编笔记

  • 8086CPU一共有14个寄存器

  • AX,BX,CX,DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器

  • 8086CPU的AX,BX,CX,DX这4个寄存器都可分为两个可独立使用的8位寄存器

  • 几条汇编指令:

    汇编指令控制CPU完成的操作用高级语言的语法描述
    mov ax,18将18送入寄存器AXAX=18
    mov ah,78将78送入寄存器AHAH=78
    add ax,8将寄存器AX中的数值加上8AX=AX+8
    mov ax,bx将寄存器BX中的数据送入寄存器AXAX=BX
    add ax,bx将AX和BX中的数值相加,结果存在Ax中AX=AX+BX
  • 在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的

  • 物理地址=段地址x16+偏移地址

  • 一个段的起始地址一定是16的倍数

  • 偏移地址为16位,16位地址的寻址能力为64KB

  • 一个段的长度最大为64KB

  • 段寄存器:CS,DS,SS,ES

  • CSIP指示了CPU当前要读取指令的地址

  • CPU将CS:IP指向的内容当作指令执行

  • 同时修改CSIP:

    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
  • 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指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶(向下生长)
  • pushpop指令的格式可以是如下形式:

    • push 寄存器
    • pop 寄存器
    • push 段寄存器
    • pop 段寄存器
    • push 内存单元
    • pop 内存单元
  • 栈为空时候,SS:SP指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2

  • 10000H~1000FH这段空间当作栈,交换AXBX中的数据

    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=1000hSP=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
    
    
  • sidi是8086CPU中和bx功能相近的寄存器,sidi不能够分成两个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在汇编指令中可以为wordbyte

      字操作
      	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或内存单元中
    • 被除数:默认放在AXDXAX中,如果除数为8位,被除数则为16位,默认在AX中存放;如果除数为16位,被除数则为32位,在DXAX中存放,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,或同时修改CSIP的指令统称为转移指令

  • 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操作符取得了标号starts的偏移地址03,所以指令mov ax,offset start相当于指令mov ax,0,因为start是代码中的标号,它所标记的指令是代码中的第一条指令,偏移地址为0mov ax,offset s相当于指令mov ax,3,因为s是代码段中的标号,它所标记的指令是代码段中的第二条指令,第一条指令长度为3个字节,则s的偏移地址为3

  • jmp指令:是无条件的转移指令,可以只修改IP,也可以同时修改CSIPjmp指令要给出两种信息:

    • 转移的目的地址
    • 转移的距离(段间转移、段内短转移、段内近转移)

    不同的给出目的地址的方法,和不同的转移的位置,对应有不同格式的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,什么也不做(程序向下执行)
  • retcall指令都是转移指令,它们都修改IP,或同时修改CSIP,它们经常被共同用来实现子程序的设计

  • ret指令用栈中的数据,修改IP的内容,从而实现近转移

  • retf指令用栈中的数据,修改CSIP的内容,从而实现远转移

  • CPU执行ret指令时,进行下面两步操作:

    1. (IP)=((ss)*16+(sp))
    2. (sp)=(sp)+2

    相当于:pop IP

  • CPU执行reft指令时,进行下面四步操作:

    1. (IP)=((ss)*16+(sp))
    2. (sp)=(sp)+2
    3. (CS)=((ss)*16+(sp))
    4. (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指令时,进行两步操作:

    1. 将当前的IPCSIP压入栈中
    2. 转移
  • call指令不能实现短转移

  • 192

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值