标签:pop des mat 寄存器 汇编 解决 内存地址 循环 恢复
and指令是按位与运算:
and al,00111011B
代表al中的值和数值00111011B进行按位与运算,然后将结果赋值给寄存器al。and指令可将操作对象的相应位设置为0,其他位不变,如将al的第6位设置为0:
and al,10111111B
or指令是按位或运算,同样的它能将操作对象的对应位设置为1,如将al的第6位设置为1:
or al,01000000B
在汇编程序中,我们可以用单引号括起来的字符序列来表达多个数据,如:
db ‘unIX‘ 相当于db 75H,6EH,49H,58H,db是define byte定义一字节数据。
mov al,‘a‘ 相当于mov al,61H
其中的转换规则就是ASCII码,注意同一个字母大小写ascii码不一样。
如果我们的程序有两个段,一个数据段datasg中定义了两个字符串,一个代码段codesg中定义了代码,需要把datasg中第一个字符串转换成大写,第二个字符串转换成小写。
首先我们可以找到大小写字母的ascii码关系,可以发现小写字母的ascii码比大写字母大20H,所以我们应该将第一个字符串中所有小写字母的ascii码都减20H,将第二个字符串中的所有大写字母的ascii码加20H,但是这个思路有一个问题:现在我们还没办法区分大小写字母。
我们发现同一个字母的编码第6位决定了是否是大小写,如果第6位是1就是小写,第6位是0就是小写,根据这个规律我们可以很简单的用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 设置bx为0,也就是指向第一个字符串的起始位置
mov cx,5 设置循环次数为5,因为第一个字符串有5个字母
s: mov al,[bx]
and al,11011111B
mov [bx],al 取出字母,处理后放回去
inc bx bx加1,即将处理下一个字母
loop s
mov bx,5 设置bx为5,指向第二个字符串的起始位置
mov cx,11 设置循环次数为11
s0: mov al,[bx]
or al,00100000B
mov [bx],al 取出字母,处理后放回去
inc bx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
[bx]代表一个内存单元的偏移地址,[bx+idata]也表示一个内存单元的偏移地址,此时偏移地址为bx中的值加上数值idata。
下列指令:
mov ax,[bx+200]
代表将一个内存单元中的值放入寄存器ax中,这个内存地址的段地址在ds中,偏移地址为bx中的数值加200.
有了这种表示方式,我们可以表示一种类似数组的处理方式。
假如还是上个案例,我们要处理的字符串是等大的,长度都是5,此时我们就可以这样处理:
mov ax,datasg
mov ds,ax 让ds指向datasg段
mov bx,0 设置bx指向字符串的起始位置
mov cx,5 设置总的循环次数为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
这样,就让之前的问题得到了简化,前提是要处理的数据很规整,就像处理数组一样。idata[bx]和[bx+idata]是等价的,程序也可以这样写:
mov ax,datasg
mov ds,ax 让ds指向datasg段
mov bx,0 设置bx指向字符串的起始位置
mov cx,5 设置总的循环次数为5
s: mov al,0[bx]
and al,11011111b
mov 0[bx],al 处理第一个字符串,然后放回原来的位置
mov al,5[bx]
or al,00100000b
mov 5[bx],al 处理第二个字符串,然后放回原来的位置
inc bx
loop s
si和di寄存器的作用和bx一样,[si]和[di]一样可以表示内存单元的偏移地址:
mov ax,[si]
代表将内存单元中的数据放入ax寄存器中,该内存单元的偏移地址是si中的数据,段地址是ds中的数据。
同样的,[si+idata]也有效。
假如我们要把字符串复制到它后面的数据区中,可以用si寄存器来完成:
assume cs:codesg,ds:datasg
datasg segment
db ‘welcome to masm!‘
db ‘.....................‘
datasg ends
codesg segment
start: mov ax,datasg
mov ds,ax 令ds指向datasg段
mov si,0
mov cx,8 设置起始偏移量和循环次数
s: mov ax,0[si]
mov 16[si],ax 将字母取出,放入ax寄存器中,再从ax寄存器放入新地址
add si,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
我们可以把两个寄存器相加的值作为偏移地址,如:[bx+si]和[bx+di],同样也可以加上一个数值,即[bx+si+idata]和[bx+di+idata]。
总结一下定位内存地址的几种方式:
1、[idata]用一个常量表示地址,可用于直接定位一个内存单元
2、[bx]用一个变量表示地址的偏移量,也就是间接定位一个内存单元
3、[bx+idata]用一个常量和变量间接表示地址,也可以表示为idata[bx],或[bx].idata
4、[bx+si]是两个变量表示地址
5、[bx+si+idata]是两个变量和一个常量表示地址,也可以表示为[bx].idata[si],一般用于处理类似结构体的数据,bx相当于结构体的地址,idata指明了数据项的地址,而用si进一步定位该数据项中的每一个字或字节。
由此可见,表示地址的方式越来越灵活,这些方式都可以用于不同的场合。
要求将datasg段中的每个单词都改为大写字母:
assume cs:codesg,ds:datasg
datasg segment
db ‘ibm ‘
db ‘dec ‘
db ‘dos ‘
db ‘vax ‘
datasg ends
codesg segment
start:
codesg ends
end start
这个功能当然可以用一个循环来完成,但是灵活性很差,这里我们考虑用双层循环来解决,如果要用双层循环的话就面临一个问题,就是循环次数是默认存在cx寄存器中的,必须还得找一个位置存放另一个循环次数。
这里我们可以采用这样的方法:在每次开始内层循环的时候,将外层循环中的cx数值保存起来,在执行外层循环的loop指令前,再恢复外层循环的cx数值:
mov ax,datasg
mov ds,ax 让ds指向段datasg
mov bx,0
mov cx,4 设置外层循环值是4
s0: mov dx,cx 将外层循环次数保存在寄存器dx中
mov si,0
mov cx,3 内层循环开始前设置循环次数为3
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al 将字母取出,然后处理后放回原地址
inc si
loop s
add bx,16
mov cx,dx 将保存的cx值还给cx
loop s0
mov ax,4c00H
int 21H
上述程序有一些局限性,主要是因为寄存器的数量是有限的,如果用寄存器作为保存关键数字的中转站,在较大的程序中寄存器就有用完的可能,所以我们可以尝试用一个内存单元来保存关键的数值,在数据段我们提前声明好一个空间然后使用:
assume cs:codesg,ds:datasg
datasg segment
db ‘ibm ‘
db ‘dec ‘
db ‘dos ‘
db ‘vax ‘
dw 0 定义一个字来暂存cx寄存器中的数值
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax 让ds指向段datasg
mov bx,0
mov cx,4 设置外层循环值是4
s0: mov ds:[40H],cx 将外层循环次数保存在内存单元中
mov si,0
mov cx,3 内层循环开始前设置循环次数为3
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al 将字母取出,然后处理后放回原地址
inc si
loop s
add bx,16
mov cx,ds:[40H] 将保存的cx值还给cx
loop s0
mov ax,4c00H
int 21H
codesg ends
end start
我们也可以先声明一段空间用做栈,然后用push和pop指令来存入和取出数值。
标签:pop des mat 寄存器 汇编 解决 内存地址 循环 恢复
原文地址:https://www.cnblogs.com/yinyunmoyi/p/12811503.html