【汇编语言】内存寻址方式
处理字符
assume cs:codesg,ds:datasg
datasg segment
db '1234'
db 'ABCD'
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov ax,0
mov al,'a'
mov bl,'b'
mov ax,4c00h
int 21h
codesg ends
end start
常用db
指令定义字符串。字符类型的数据会被编译器自动转为ASCII码。
ASCII码在设计上满足一些规律:
在16
进制下,3xh
就代表了字符10
进制数'x'
,例如'1'
的ASCII码为49
也就是31h
在16
进制下,大写字母和小写字母的ASCII码相差20h
,例如'A'
的ASCII码为41h
,'a'
的ASCII码为61h
and指令、or指令
位运算指令,用法与add
类似。这两个指令可以在不需要分支结构的情况下,实现大小写字符转换:
转大写:
and al,11011111b
转小写:
or al,00100000b
寻址方式
[bx+idata]方式
mov ax,[bx+200] ;(ax)=((ds)*16+(bx)+200)
和以下三种写法都是等价的:
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
SI和DI寄存器
SI
和DI
寄存器被称为变址寄存器,常执行与地址有关的操作,是与BX
功能相近的寄存器。
BX
:通用寄存器,常作为基址寄存器使用
SI
:源变址寄存器(Source Index)
DI
:目的变址寄存器(Destination Index)
SI
和DI
不能够像BX
一样分成BH
和BL
两个8
位寄存器使用。
[bx+si]和[bx+di]方式
mov ax,[bx+si] ;(ax)=((ds)*16+(bx)+(si))
和以下写法等价:
mov ax,[bx][si]
[bx+si+idata]和[bx+di+idata]方式
mov ax,[bx+si+200] ;(ax)=((ds)*16+(bx)+(si)+200)
BP寄存器
BP
和BX
类似,也是基址寄存器。它们的区别在于BX
的默认段寄存器是DS
,而BP
的默认段寄存器是SS
。BP
多用于栈操作。
到现在为止,已经学习了BX
、SI
、DI
、BP
四个寄存器,它们都是与地址操作有关的寄存器。只有这四个寄存器可以以[...]
的格式对内存进行寻址。
按照基址寄存器和变址寄存器分类,则BX
和BP
是基址寄存器,SI
和DI
是变址寄存器。在使用时,基址寄存器和变址寄存器可以任意组合,例如[bx+si]
、[bx+di]
、[bp+si]
、[bp+di]
,但内部不能互相组合,例如[bx+bp]
、[si+di]
是错误的写法。
总结
指明要访问数据的大小
观察下面的代码:
mov bx,0
mov [bx],100h
编译会报错!这是因为按照上面代码的参数,没有办法确定操作的数据是字型还是字节型(如果第二个操作数是寄存器,则可以根据是AX
还是AH
/AL
判断)。因此,此时需要显式的指出操作的数据类型,语法为:
mov word ptr [bx],100h
add byte ptr [bx],23h
使用dup关键字设置重复数据
在之前开辟数据空间或栈空间时,都是计算好大小后手动输入对应数量的0
(或其他初值);dup
关键字可以简洁的完成设置重复数据的操作,格式为:db/dw/dd 重复次数 dup(数据)
,例如:
dd 4 dup('a')
dw 4 dup('b')
db 8 dup('c')
db 4 dup(1,2,3,4)
db 4 dup('asm',32,'8086')
初始化后的数据为:
练习
大小写转换
编程操作数据段中的字符串,将第一个字符串小写字母转换为大写字母,第二个字符串大写字母转换为小写字母。
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'AsSeMbLeR'
datasg ends
codesg segment
start:
;...
codesg ends
end start
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'AsSeMbLeR'
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
s1: mov al,[bx]
and al,11011111b
mov [bx],al
inc bx
loop s1
mov bx,5
mov cx,9
s2: mov al,[bx]
or al,00100000b
mov [bx],al
inc bx
loop s2
mov ax,4c00h
int 21h
codesg ends
end start
二重循环将字符串全部转大写
编程操作数据段中的字符串,把所有字母都改为大写。
assume cs:codesg,ds:datasg
datasg segment
db 'Hello '
db 'kitty '
db 'aBcDe '
db 'uCase '
datasg ends
codesg segment
start:
;...
codesg ends
end start
assume cs:codesg,ds:datasg,ss:stcksg
datasg segment
db 'Hello '
db 'kitty '
db 'aBcDe '
db 'uCase '
datasg ends
stcksg segment
dw 0,0,0,0,0,0,0,0
stcksg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,4 ;遍历4个字符串
str: push cx ;保存外层循环的CX
mov di,0
mov cx,5 ;遍历5个字符
chr: mov al,[bx+di]
and al,11011111b
mov [bx+di],al
inc di
loop chr
add bx,10h ;下一个字符串的首字符的地址
pop cx ;恢复外层循环的CX
loop str
mov ax,4c00h
int 21h
codesg ends
end start
由于字符串长度相同,考虑使用二重循环批量操作。然而由于只有一个寄存器CX
控制着循环计数器,所以进入内层循环时需要先保存外层循环的CX
,内层循环结束后再恢复外层循环的CX
。保存的方式可以是借助其他寄存器例如DX
,但由于寄存器资源比较宝贵,常见的做法是利用栈保存数据。