参考教程:通俗易懂的汇编语言(王爽老师的书)_哔哩哔哩_bilibili
一、[bx+idata]方式寻址
1、[bx+idata]的含义
(1)[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata,也即BX寄存器中的数值加上idata。(这为高级语言实现数组提供了便利机制)
(2)举例:“mov ax, [bx+200]”及“mov ax, [200+bx]”的含义。
①将一个内存单元的内容送入AX。
②[bx+200]这个内存单元的长度为2字节(字单元),存放一个字。
③内存单元的段地址在DS中,偏移地址为200加上BX中的数值。
④数学化的描述为:(ax) = ((ds) * 16 + 200 + (bx))。
(3)“[bx+idata]”的其它几种写法:
①[idata+bx],如“mov ax, [idata+bx]”。
②idata[bx],如“mov ax, idata[bx]”。
③[bx].idata,如“mov ax, [bx].idata”。
2、[bx+idata]方式寻址使用举例
(1)目标:对数据段定义的两个长度相同的字符串,将第一个字符串中的小写字母转换为大写字母,将第二个字符串中的大写字母转换为小写字母。
(2)汇编程序:
assume cs:codesg, ds:datasg
datasg segment
db ‘BaSiC’ ;字符串1
db ‘mInIx’ ;字符串2
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax ;初始化段寄存器DS,它要存放数据段的段地址
mov bx, 0 ;存放数据段偏移地址
mov cx, 5
s: mov al, [bx] ;将字符串1中的字符依次送入al,每轮循环送入一个
and al 11011111b ;将al中的第6位二进制数置0
mov [bx], al ;将大写转换后的字符送回它原来的位置
mov al, [5+bx] ;将字符串2中的字符依次送入al,每轮循环送入一个
or al 00100000b ;将al中的第6位二进制数置1
mov [5+bx], al ;将小写转换后的字符送回它原来的位置
inc bx ;(bx) = (bx) + 1
loop s
mov ax, 4c00h
int 21h
codesg ends
end start
二、变址寄存器SI和DI
1、SI和DI介绍
(1)SI和DI是8086CPU中和BX功能相近的寄存器,区别是SI和DI不能够分成两个8位寄存器来使用,而BX可以。
①BX:通用寄存器,在计算存储器地址时,常作为基址寄存器用。
②SI:源变址寄存器。
③DI:目标变址寄存器。
(2)举例:下列3组指令执行的功能是一样的。
mov bx, 0 mov ax, [bx+123]
mov si, 0 mov ax, [si+123]
mov di, 0 mov ax, [di+123]
2、SI和DI的应用举例
(1)目标:用寄存器SI和DI实现将字符串’welcome to masm!’复制到它后面的数据区中。
(2)汇编程序:
assume cs:codesg, ds:datasg
datasg segment
db ‘welcome to masm!’
db ‘................’ ;为复制版字符串预留位置
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax ;初始化段寄存器DS,它要存放数据段的段地址
mov si, 0 ;ds:si指向原始字符串首地址
mov di, 16 ;ds:si指向目的空间首地址
mov cx, 8 ;一次复制两个字节(两个字符),循环8次即可
s: mov ax, [si] ;将源字符串中的两个字符送入ax,每轮循环送入两个
mov [di], ax ;将ax中的两个字符送到目的空间,每轮循环送出两个
add si, 2 ;(si) = (si) + 2 复制完前面两个字符,指向后面两个字符
add di, 2 ;(di) = (di) + 2 字符填入空间后,指向后面仍为空的空间
loop s
mov ax, 4c00h
int 21h
codesg ends
end start
三、[bx+si]和[bx+di]方式寻址及[bx+si+idata]和[bx+di+idata]方式寻址
1、[bx+si]和[bx+di]的含义
(1)[bx+si]表示一个内存单元,它的偏移地址为(bx)+(si),也即BX寄存器中的数值加上SI寄存器中的数值;[bx+di]表示一个内存单元,它的偏移地址为(bx)+(di),也即BX寄存器中的数值加上DI寄存器中的数值。
(2)举例:“mov ax, [bx+si]”的含义。
①将一个内存单元的内容送入AX。
②[bx+si]这个内存单元的长度为2字节(字单元),存放一个字,内存单元的段地址在DS中,偏移地址为BX中的数值加上SI中的数值。
③数学化的描述为:(ax) = ((ds) * 16 + (bx) + (si))。
(3)举例:“mov [bx+di], ax”的含义。
①将一个内存单元的内容送入[bx+di]。
②[bx+di]这个内存单元的长度为2字节(字单元),存放一个字,内存单元的段地址在DS中,偏移地址为BX中的数值加上DI中的数值。
③数学化的描述为:((ds) * 16 + (bx) + (di)) = (ax)。
(4)“[bx+si]”可写为“[bx][si]”,“[bx+di]”可写为“[bx][di]”。
2、[bx+si+idata]和[bx+di+idata]的含义
(1)[bx+si+idata]表示一个内存单元,它的偏移地址为(bx)+(si)+idata,也即BX寄存器中的数值加上SI寄存器中的数值加上idata;[bx+di+idata]表示一个内存单元,它的偏移地址为(bx)+(di)+idata,也即BX寄存器中的数值加上DI寄存器中的数值加上idata。
(2)举例:“mov ax, [bx+si+200]”的含义。
①将一个内存单元的内容送入AX。
②[bx+si+200]这个内存单元的长度为2字节(字单元),存放一个字,内存单元的段地址在DS中,偏移地址为BX中的数值加上SI中的数值加上200。
③数学化的描述为:(ax) = ((ds) * 16 + (bx) + (si) + 200)。
(3)举例:“mov [bx+di+200], ax”的含义。
①将一个内存单元的内容送入[bx+di+200]。
②[bx+di+200]这个内存单元的长度为2字节(字单元),存放一个字,内存单元的段地址在DS中,偏移地址为BX中的数值加上DI中的数值加上200。
③数学化的描述为:((ds) * 16 + (bx) + (di) + 200) = (ax)。
(4)“[bx+si+idata]”的其它几种写法:
①[idata+bx+si],如“mov ax, [idata+bx+si]”。
②idata[bx][si],如“mov ax, idata[bx][si]”。
③[bx].idata[si],如“mov ax, [bx].idata[si]”。
④[bx+idata+si],如“mov ax, [bx+idata+si]”。
⑤[bx][si].idata,如“mov ax, [bx][si].idata”。
(5)“[bx+di+idata]”的其它几种写法:
①[idata+bx+di],如“mov [idata+bx+di], ax”。
②idata[bx][di],如“mov idata[bx][di], ax”。
③[bx].idata[di],如“mov [bx].idata[di], ax”。
④[bx+idata+di],如“mov [bx+idata+di], ax”。
⑤[bx][di].idata,如“mov [bx][di].idata, ax”。
四、不同的寻址方式的灵活应用
1、对内存的寻址方式汇总
2、灵活应用不同的寻址方式案例
(1)编程将datasg段中每个单词的头一个字母改为大写字母,每个单词字符串的长度均为16,如下图所示。
assume cs:codesg, ds:datasg
datasg segment
db ‘1. file ’
db ‘2. edit ’
db ‘3. search ’
db ‘4. view ’
db ‘5. options ’
db ‘6. help ’
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax ;初始化段寄存器DS,它要存放数据段的段地址
mov bx, 0 ;bx存放数据段偏移地址
mov cx, 6 ;一共改6个字母,循环6次即可
s: mov al, [bx+3] ;将源字符串中的首个字母送入al,每轮循环送入1个
and al, 11011111b ;将al中的字母变成大写形式
mov [bx+3], al ;将转为大写的字母送回原位
loop s
mov ax, 4c00h
int 21h
codesg ends
end start
(2)编程将datasg段中每个单词改为大写字母,每个单词字符串的长度均为16,如下图所示。(本例涉及嵌套循环,因此会有不同循环共用CX的问题,解决多重循环问题需使用栈结构)
assume cs:codesg, ds:datasg, ss:stacksg
stacksg segment
dw 0, 0, 0, 0, 0, 0, 0, 0
stacksg ends
datasg segment
db ‘ibm ’
db ‘dec ’
db ‘dos ’
db ‘vax ’
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax ;初始化段寄存器DS,它要存放数据段的段地址
mov ax, stacksg
mov ss, ax ;初始化段寄存器SS,它要存放栈段的段地址
mov sp, 16 ;初始化栈顶指针
mov bx, 0 ;bx存放数据段偏移地址
mov cx, 4 ;一共改4个单词,每个外层循环改一个单词
s0: push cx ;将外层循环的CX值压入栈中
mov si, 0 ;si存放目标字母相对于单词首字母的“偏移地址”
mov cx, 3 ;每个单词长度为3,一次内层循环改一个字母
s: mov al, [bx+si] ;将源字符串中的一个字母送入al
and al, 11011111b ;将al中的字母变成大写形式
mov [bx+si], al ;将转为大写的字母送回原位
inc si ;(si) = (si) + 1
loop s
add bx, 16 ;改完一个单词后,bx中的偏移地址指向下一个单词开头
pop cx ;将外层循环的CX值从栈中弹出
loop s0
mov ax, 4c00h
int 21h
codesg ends
end start
五、用于内存寻址的寄存器用法及数据的表达
1、用于内存寻址的寄存器用法
(1)只有BX、BP、SI、DI可以用在[...]对内存单元寻址,BX以外的通用寄存器、段寄存器不可以用在[...]中。
(2)bx可以与si、di相加,bp可以与si、di相加,除此之外,这几个寄存器之间的运算都是不被允许的。
(3)BX寄存器用于内存寻址时,如不使用段前缀指定,默认段地址为数据段地址;BP寄存器用于内存寻址时,如不使用段前缀指定,默认段地址为栈段地址。
2、数据及其位置的表达
(1)对于直接包含在机器指令中的数据,称其为立即数(idata),数据包含在指令中,如“mov ax, 1”。
(2)若指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名即可,如“mov ax, bx”。
(3)若指令要处理的数据在内存中,由SA:EA(段地址:偏移地址)确定内存单元。
3、指令处理数据的长度
(1)在没有任何指定的情况下,指令处理数据的长度取决于能确定长度的操作数的长度(基本都是寄存器),如下所示。
(2)在没有寄存器参与的内存单元访问指令中,建议用word ptr(字长度)或byte ptr(字节长度)显性地指明所要访问的内存单元的长度,否则,CPU无法得知所要访问的单元是字单元还是字节单元。
六、DIV指令
1、DIV指令的功能和用法
(1)DIV是除法指令,使用DIV做除法的时候,被除数默认放在AX或DX、AX中,除数放在其它寄存器或内存单元中,结果分为商和余数,具体如何放置见下表:
被除数的长度为16位 | 被除数的长度为32位 | |
被除数存放的位置 | AX | DX(被除数高16位)和AX(被除数低16位) |
除数存放的位置 | 8位长度的内存单元或寄存器 | 16位长度的内存单元或寄存器 |
商存放的位置 | AL | AX |
余数存放的位置 | AH | DX |
(2)DIV指令格式:DIV <寄存器或内存单元地址>。
(3)使用DIV指令时,切记提前在默认的寄存器中设置好被除数,且默认寄存器不作别的用处。
2、DIV指令使用举例
(1)一些示例指令:
(2)举例:用DIV指令计算data段中第一个数据除以第二个数据后的结果,商存放在第三个数据的存储单元中。
assume cs:code, ds:data
data segment
dd 100001
dw 100
dw 0
data ends
code segment
start: mov ax, data
mov ds, ax ;初始化段寄存器DS,它要存放数据段的段地址
mov ax, ds:[0] ;获取data段中第一个数据
mov dx, ds:[2] ;获取data段中第二个数据
div word ptr ds:[4] ;DIV指令将除法的商存在AX中
mov ds:[6], ax ;商存放在数据段第三个数据的存储单元中
mov ax, 4c00h
int 21h
code ends
end start
七、DUP指令
1、DUP指令的功能和用法
(1)DUP指令和db、dw、dd等数据定义伪指令配合使用,用来进行数据的重复定义。
(2)DUP使用格式如下,其中重复数据列表中的数据用逗号分隔,DUP会定义<重复次数>次重复数据列表中的数据。
<数据定义伪指令> <重复次数> DUP (<重复数据列表>)
2、DUP指令使用举例
(1)一些示例指令:
(2)举例:采用DUP指令定义一个容量为200个字节的栈段。
stack segment
db 200 dup (0)
stack ends