【原创】汇编语言的基本知识(二)
五,loop指令实现循环
1,[bx]和内存单元的描述
(1)完整地描述一个内存单元需要2种信息:1,内存单元的地址;2,内存单元的长度
用[0]表示内存单元的时候,0表示单元的偏移地址,段地址默认在ds中;
[bx]同样也表示一个内存单元,它的偏移地址在bx中,段地址默认在ds中;
2,一般用"()"来表示一个寄存器或一个内存单元中的内容
,3,loop指令的坐拥:1,(cx) = (cx) -1 ;2判断cx中的值,不为0则转至标号出执行程序,为0则向下执行
#计算2的12次方 assumne cs:code code segment mov ax,2 mov cx,11 s: add ax,ax loop s mov ax,4c00h int 21h code ends end
4,在汇编源程序中,数据不能以字母开头,要在前面加0,比如A000H在汇编程序中改写为"0A000H"
5,段前缀:在访问内存的指令中,使用:显示地指明内存单元的段地址,在汇编语言中称为段前缀
mov ax,ds:[bx]
mov ax,cs:[bx]
...
ds: cs:等都是段前缀
6,段前缀的使用,例:将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中。
assume cs:code code segment mov bx,0 mov cx,12 s: mov ax,0ffffh mov ds,ax mov dl,[bx] mov ax,0020h mov ds,ax mov [bx],dl inc bx loop s mov ax,4c00h int 21h code ends end
7,将数据,代码,栈放入不同的段
assume cs:code ,ds:data,ss:stack data segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987 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 mov ax,data mov ds,ax mov bx,0 mov cx,8 s: push [bx] add bx,2 loop s0 mov ax,4c00h int 21h code ends end start
六,更灵活的定位内存地址
1,逻辑指令 and和or
#按位进行与运算 mov al,01100011B and al,00111011B 结果为:al=00100011B #按位进行或运算 mov al,01100011B or al,00111011B 结果为:al=01111011B
2,以字符形式给出数据:ASCLL码
mov al,'a' 相当于执行 mov al,61H, 'a'的asciil码的16进制为61
3,用[bx+idata]的方式进行数组的处理,inc指令
举例:将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写
assume cs:codesg ds:datasg datasg segment db 'BiSiC' db 'MinIX' datasg ends codesg segment start:mov ax,datasg mov ds,ax mov bx,0 mov cx,5 s: mov al,[bx] and al,11011111b mov [bx],al mov al,[5+bx] or al,00100000b mov [5+bx],al inc bx loop s codesg ends end start
4,不同的寻址方式的灵活应用
[idata] 1个常量,直接定位一个内存单元
[bx] 1个变量,间接定位一个内存单元
[bx+idata] 1个变量+1个常量
[bx+si] 2个变量
[bx+si+idata] 2个变量+1个常量
5,程序中经常需要进行数据的暂存,需要一个通用的方案解决寄存器数量有限的问题
(1)当寄存器不够用的时候,就需要用内存单元保存数据了;
(2)一般来说,在需要暂存数据的时候,我们都应该使用栈。
七,数据处理的2个基本问题
1,用reg表示1个寄存器,sreg表示一个段寄存器
reg包括:ax bx cx dx al bl cl dl ah bh ch dh sp bp si di
sreg包括:ds cs es ss
这4个寄存器可以单个出现:bx si di bp
2,数据位置的表达
(1)立即数:对于直接包含在机器指令中的数据(执行前在cpu的指令缓冲器中)
mov ax,1 add bx,2000h or bx,00010000b mov al, 'a'
(2)寄存器:指令要处理的数据再寄存器中,需要给出相应的寄存器名
mov ax,bx mov ds,ax push bx mov ds:[0],bx push ds mov ss,ax mov sp,ax
(3)段地址(SA)和偏移地址(EA):要处理的数据再内存中,用[X]的格式给出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,ss:[bx+si] mov ax,cs:[bx+si+8]
3,指令要处理的数据有多长
cpu指令可以处理两种尺寸的数据:byte(字节)和word(子)。所以在机器指令中要指明是字操作还是字节操作
(1)通过寄存器名指明要处理的尺寸
(2)没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度
mov word ptr ds:[0],1
mov byte ptr ds:[0],1
(3)其他方法:push [1000H]就不用指明,因为push指令只进行word字操作
4,div指令:除法指令
1,除数: 有8位和16位2种,在一个reg或内存单元中 2,被除数: 默认放在AX或DX和AX中 3,结果: 如果除数为8位,则AL存储商,AH存储余数, 如果除数为16位,则AX存储商,DX存储余数
5,伪指令dd
dd定义字节型,dw定义字型
dd定义双字型
6,dup是一个操作数,在汇编语言中同db dw dd等一样,也是由编译器识别处理的符号。与db dw dd配合使用,用来进行数据的重复,比如
db 3 dup (0) ;定义了3个字节,它们的值都是0 #相当于 db 0,0,0 db 3 dup (0,1,2) #相当于 db 0,1,2,0,1,2,0,1,2
db 3 dup ('abc','ABC')
#相当于
db 'abcABCabcABCabcABC'
db 重复次数 dup (重复的数据)
dw 重复次数 dup (重复的数据)
dd 重复次数 dup (重复的数据)
八,转移指令的原理
1,可以修改IP,或同时修改CS和IP的指令统称为转移指令。一般有一下几类:
无条件转移指令(jmp)
条件转移指令
循环指令(loop)
过程
终端
2,操作符offset在汇编语言中是由编译器处理的符号,它的功能是取得标号的位移地址。
3,jmp为无条件转移指令,可以只修改IP,也可以同时修改CS和IP,jmp指令要给出2种信息
(1)转移的目的地址
(2)转移的距离(段间转移、段内段转移,段内近转移)
4,段内段转移:jmp short 标号
assume cs:codesg codesg segment start:mov ax,0 jmp short s add ax,1 s: inc ax codesg ends end start
在"jmp short 标号"指令所对应的机器码中,并不包含转移的目的地址,而包含的是转移的位移
实际上,jmp short 标号的功能为:(IP)=(IP)+8位位移
(1)8位位移=标号处的地址-jmp指令后的第一个字节的地址
(2)short指明此处的位移为8位位移
(3)8位位移的范围为-128~127,用补码表示
(4)8位位移有比哪一程序在编译时算出(此处可以深究下)
5,段内近转移:jmp near ptr 标号;其功能为:(IP)=(IP)+16位位移
6,段间转移(远转移):jmp far ptr 标号,功能为
(CS)=标号所在段的段地址;(IP)=标号所在段的偏移地址
7,转移地址在寄存器中:jmp 16位ref 。功能为:(IP)=(16位reg)
8,转移地址在内存中:
(1)jmp word ptr 内存单元地址,属于段内转移
(IP)=(内存单元地址)
(2)jmp dword ptr 内存单元地址,属于段间转移
(CS)=(内存单元地址+2)
(IP)=(内存单元地址)
9,jcxz指令:有条件转移指令,所有有条件指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址
格式:jcxz 标号(如果(CX)=0,转移到标号处执行)
操作:当(CX)=0时,(IP)=(IP)+8位位移
相当于:if((CX)==0) jmp short 标号
10,loop指令,所有循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址
格式:loop 标号
操作:
(1)(CX)=(CX)-1
(2)如果(CX)不等于0,(IP)=(IP)+8位位移
相当于:
(CX)--;
if((CX)<>0) jmp short 标号
11,根据位移进行转移的意义:方便程序段在内存中的浮动装配
12,call和ret指令都是转移指令,通常一起使用实现子程序的设计
(1)ret指令用栈中的数据,修改IP的内容,从而实现近转移,
相当于执行 pop IP
(IP)=((SS)*16+(SP)) (SP)=(SP)+2
(2)retf指令用栈中的数据,修改CS和IP的内容,从二实现远转移
相当于执行 pop IP pop CS
(IP)=((SS)*16+(SP)) (SP)=(SP)+2 (CS)=((SS)*16+(SP)) (SP)=(SP)+2
(3)call指令:根据位移进行转移时
格式:call 标号
(SP)=(SP)-2 ((SS)*16+(SP))=(IP) (IP)=(IP)+16位位移
相当于进行
push IP
jmp near ptr 标号
13,call far ptr 标号 :目的地址在指令中,实现的是段间转移,
(SP)=(SP)-2 ((SS)*16+(SP)) = (CS) (SP)=(SP)-2 ((SS)*16+(SP)) = (IP) (CS)=标号所在段的段地址 (IP)=标号所在段的偏移地址
相当于进行
push CS
push IP
jmp far ptr 标号
14,call 16位reg:转移地址在寄存器中
(SP)=(SP)-2 ((SS)*16+(SP)) = (IP) (IP)=(16位reg)
相当于 push IP
jmp 16位reg
15,call word ptr 内存单元地址:转移地址在内存中
相当于
push IP
jmp word ptr 内存单元地址
16,call dword ptr 内存单元地址:转移地址在内存中
相当于
push CS
push IP
jmp dword ptr 内存单元地址
17,call和ret的配合使用
assume cs:code code segment main: ... ... call sub1 ... ... mov ax,4c00h int 21h sub1: ... ... call sub2 ... ... ret sub2: ... ... ... ret code ends end main
(1)从上面可以看出,call和ret指令共同支持了汇编语言编程中的模块化设计。
(2)参数和结果的传递:参数存在什么地方,计算结果存放在什么地方;答案是寄存器
(3)对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反
调用者将参数送入参数寄存器,从结果寄存器中取得返回值
子程序从参数寄存器中取得参数,将返回值送入结果寄存器
assume cs:code data segment dw 1,2,3,4,5,6,7,8 dd 0,0,0,0,0,0,0,0 data ends code segment start:mov ax,data mov ds,ax mov si,0 mov di,16 mov cx,8 s: mov bx,[si] call cube mov [di],ax mov [di].2,dx add si,2 add di,4 loop s mov ax,4c00h int 21h cube: mov ax,bx mul bx mul bx ret code ends end start
18,mul指令:乘法
(1)2个相乘的数,要么都是8位要么都是16位,
如果是8位,一个默认在AL中,另一个在8位reg或内存字节单元中
如果是16位,一个默认在AX中,另一个在16位reg或内存字单元中
(2)结果:如果是8位乘法,结果在AX中;如果是16位乘法,结果高位默认在DX中存放,低位在AX中存放。
格式:mul reg或者mul 内存单元
举例:
mov al,100 mov bl,10 mul bl