本人微信公众号"aeolian"~

汇编语言-07灵活定位内存地址

and和or指令

and 指令:逻辑与指令,按位进行与运算。

《汇编语言-07灵活定位内存地址》

通过该指令可将操作对象的相应位设为0,其他位不变。

《汇编语言-07灵活定位内存地址》

or指令:逻辑或指令,按位进行或运算。

《汇编语言-07灵活定位内存地址》

通过该指令可将操作对象的相应位设为1,其他位不变。

《汇编语言-07灵活定位内存地址》

ASCII码

信息存储在计算机中,要对其进行编码,将其转化为二进制信息进行存储。而计算机要将这些存储的信息再显示给我们看,就要再对其进行解码。只要编码和解码采用同样的规则,就可以将人能理解的信息存入到计算机,再从计算机中取出。

世界上有很多编码方案,有一种方案叫做ASCII编码,是在计算机系统中通常被采用的。简单地说,所谓编码方案,就是一套规则,它约定了用什么样的信息来表示现实对象。比如说,在ASCII编码方案中,用61H表示“a”,62H表示”b”。一种规则需要人们遵守才有意义。

《汇编语言-07灵活定位内存地址》

以字符形式给出的数据

assume cs:code,ds:data

data segment
    db 'unIX'
    db 'foRK'
data ends

code segment
    start:mov al,'a'
        mov bl,'b'
                
        mov ax,4c00h
        int 21h
              
code ends

end start

“db ‘unIX’ ‘”相当千”db 75H,6EH,49H,58H”,”u”、”n”、”I”、”X”的ASCll码分别为75H、6EH、49H、58H;
“db ‘foRK’ “相当于”db 66H,6FH,52H,4BH”,”f”、”o”、”R”、”k”的ASCII码分别为66H、6FH、52H、4BH;
“mov al,’a’ “相当千”mov al,61H”,”a”的ASCII码为61H;
“mov bl,’b’ “相当千”mov al,62H”,”b”的ASCII码为62H。

《汇编语言-07灵活定位内存地址》

用r命令分析一下,因为“ds=075A”,所以程序从076A开始。cs为076B是指令执行的入口(end start决定),代码段的起始位置为076A,查看下数据即可。

大小写转换

字符串“BaSiC”,对其中的小写字母所对应的ASCII码进行减20H的处理,将其转为大写,对其中的大写字母不进行改变;字符串”iNtDrMaTiOn”, 对其中的大写字母所对应的ASCII码进行加20H 的处理,将其转为小写,而对千其中的小写字母不进行改变。

《汇编语言-07灵活定位内存地址》

 

可以看出来,小写字母的ASCII码值比大写字母的ASCII码值大20H。这样,我们可以想到,如果将“a”的ASCII码值减去20H,就可以得到“A”;如果将“A”的ASCII码值加上20H 就可以得到“a”。

必须判断是大写字母还是小写字母,才能决定进行何种处理,而现在又没有可以使用的用于判断的指令。

重新观察,寻找新的规律。可以看出,就ASCII 码的二进制形式来看,除第5位(位数从0开始计算)外,大写字母和小写字母的其他各位都一样。大写字母ASCII码的第5位为0, 小写字母的第5位为1。这样,我们就有了新的方法,一个字母,不管它原来是大写还是小写,将它的第5位置0,它就必将变为大写字母:将它的第5 位置1,它就必将变为小写字母。在这个方法中,我们不需要在处理前判断字母的大小写。比如:对于“BaSiC”中的“ B”按要求,它已经是大写字母了,不应进行改变,将它的第5位设为0,它还是大写字母,因为它的第5位本来就是0。

可以用and和or指令把某一位置改为1或0

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,ds:bx指向,BaSiC' 的第一个字母
    mov cx,5   #设置循环次数5,因为'BaSiC'有5个字母

  s:mov al,[bx]    #将ASCII码从ds:bx所指向的单元中取出
    and al,11011111B    #将al中的ASCII码的第5位置设为0,变成大写
    mov [bx],al    #将转变后的ASCII码写回原单元
    inc bx    #(bx)加1,ds:bx指向下一个字母
    loop s

    mov bx,5    #设置(bx)=5,ds:bx指向'iNfOrMatiOn'的第一个字母
    mov cx,11    #设置循环次数11,因为'iNfOrMaTiOn'有十一个字母

 s0:mov ax,[bx]
    or al,00100000B    #将al中的ASCII码的第5位置设为1,变为小写字母
    mov [bx],al
    inc bx
    loop s0

    mov ax,4c00h
    int 21h

codesg ends

end start

[bx+idata]

用[bx]的方式来指明一个内存单元,还可以用一种更为灵活的方式来指明内存单元:[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata(bx中的数值加上idata) 。

mov ax,[bx+200] 的含义:

将一个内存单元的内容送入ax, 这个内存单元的长度为2 个字节(字单元),存放一个字,偏移地址为bx中的数值加上200,段地址在ds中。

数学化的描述为: (ax)=((ds)*l6+(bx)+200)

该指令也可以写成如下格式(常用):

mov ax, [200+bx]

mov ax,200[bx]

mov ax, [bx].200

用[bx+idata]的方式进行数组的处理

将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写。

assume cs:codesg,ds:datasg

datasg segment
    db 'BaSIC'
    db 'MinIX'
datasg ends

codesg segment
  start:

codesg ends

end start

有了[bx+idata]这种表示内存单元的方式,我们就可以用更高级的结构来看待所要处理的数据。观察datasg 段中的两个字符串,一个的起始地址为0,另一个的起始地址为5。我们可以将这两个字符串看作两个数组,一个从0地址开始存放,另一个从5开始存放。那么我们可以用[0+bx]和[5+bx]的方式在同一个循环中定位这两个字符串中的字符。

assume cs:codesg,ds:datasg

datasg segment
    db 'BaSIC'
    db 'MinIX'
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    #第五位变为0,即转为大写
    mov [bx],al
    mov al,[5+bx]  #定位第二个字符串中的字符
    or al,00100000b    #第五位转为1,即转为小写
    mov [5+bx],al
    inc bx
    loop s

codesg ends

end start

也可以写成

assume cs:codesg,ds:datasg

datasg segment
    db 'BaSIC'
    db 'MinIX'
datasg ends

codesg segment
  start:mov ax,datasg
    mov ds,ax    #设置ds的段地址为datasg的地址
    mov bx,0    #设置偏移量

    mov cx,5
  s:mov al,0[bx]  #定位第一个字符串中的字符,0代表第一个数组的起始位置
    and al,11011111b    #第五位变为0,即转为大写
    mov 0[bx],al
    mov al,5[bx]  #定位第二个字符串中的字符,5代表第二个数组的起始位置
    or al,00100000b    #第五位转为1,即转为小写
    mov 5[bx],al
    inc bx
    loop s

codesg ends

end start

C语言

char a[5]="BaSiC";
char b[5]="MinIX";
main()
{
    int i;
    i=0;
    do
    {
     a[i]=a[i]&0xDF;
     b[i]=b[i]|10x20;
     i++;
    }
    while(i<5);
}

c语言和汇编语言的相似之处,数组名称就相当于起始地址。

  • C 语言: a[i],b[i]
  • 汇编语言: 0[bx],5[bx]

[bx+idata]的方式为高级语言实现数组提供了便利机制。

SI和DI

si和di是8086CPU中和bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用。

下面三组实现相同功能:

《汇编语言-07灵活定位内存地址》

 

 下面三组实现相同功能:

《汇编语言-07灵活定位内存地址》

用si和di实现将字符串’welcome to masm!'复制到它后面的数据区中。

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    #设置偏移量
        mov di,16   #设置第二个数组的偏移量
        
        mov cx,8    #循环八次,因为一个字母一个字节,一次可以读两个字节
        
    s:mov ax,[si]    #将第一个数组的内容移入ax
        mov [di],ax    #将ax赋值给第二个数组的相同位置
        add si,2    #数组增量为2
        add di,2    #增量为2
        loops
        
        mov ax,4c00h
        int 21h
        
codesg ends

end start

在程序中,用16位寄存器进行内存单元之间的数据传送,一次复制2个字节,一共循环8次。

改进

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,O    #设置偏移量
        
        mov cx,8    #循环八次,因为一个字母一个字节,一次可以读两个字节
        
    s:mov ax,0[si]    #将第一个数组的内容移入ax
        mov 16[si],ax    #将ax赋值给第二个数组的相同位置
        add si,2    #增量为2
        loops
        
        mov ax,4c00h
        int 21h
        
codesg ends

end start

[bx+si]和[bx+di]

用[bx(si或di)]和[bx(si或di)+idata] 的方式来指明一个内存单元,我们还可以用更为灵活的方式:[bx+si]和[bx+di]。

[bx+si]表示一个内存单元,它的偏移地址为(bx)+(si)(即bx中的数值加上si 中的数值)。

指令mov ax,[bx+si] 的含义如下:

将一个内存单元的内容送入ax,这个内存单元的长度为2 字节(字单元),存放一个字,偏移地址为bx中的数值加上si中的数值,段地址在ds中。

数学化的描述为: (ax)=((ds)*16+(bx)+(si))

该指令也可以写成如下格式(常用):

mov ax,[bx][si]

[bx+si+idata]和[bx+di+idata]

[bx+si+idata]表示一个内存单元,它的偏移地址为(bx)+(si)+idata(即bx中的数值加上si中的数值再加上idata)。
指令mov ax,[bx+si+idata] 的含义如下:
将一个内存单元的内容送入ax, 这个内存单元的长度为2 字节(字单元),存放一个字,偏移地址为bx 中的数值加上si 中的数值再加上idata, 段地址在ds 中。

数学化的描述为: (ax)=((ds)*16+(bx)+(si)+idata)

该指令也可以写成如下格式(常用):

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

不同的寻址方式的灵活应用

(1) [idata]用一个常量来表示地址,可用于直接定位一个内存单元;
(2) [bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;
(3) [bx+data]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;
(4) [bx+si]用两个变量表示地址;
(5) [bx+si+idata]用两个变量和一个常量表示地址。

多层循环嵌套

用dx实现多层循环

《汇编语言-07灵活定位内存地址》

assume cs:codesg,ds:datasg

datasg segment
    db 'ibm             '   #十六个字符,占用16个字节,不足的用空格补到16个
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends

codesg segment
  start:mov ax,datasg
    mov ds,ax  #设置数据段地址
    mov bx,0  #设置偏移量
    
    mov cx,4
    
  s0:mov dx,cx    #将外层循环的cx值保存在dx中
    mov si,0
    
    mov cx,3    #cx设置为内层循环的次数
    
   s:mov al,[bx+si] 
    and al,11011111b    #转为大写字母
    mov [bx+si],al
    inc si
    loop s
    
    add bx,16    #加16到下一个字母,因为每个db占用16个字节
    mov cx,dx    #将dx存储的外层循环恢复到cx中
    loop s0
      
codesg ends

end start

用dx来暂时存放cx中的值,如果在内层循环中,dx寄存器也被使用,该怎么办?

CPU中的寄存器数量毕竟是有限的,如8086CPU只有14个寄存器。在上面的程序中,si、cx、ax、bx,显然不能用来暂存cx中的值,因为这些寄存器在循环中也要使用;cs、ip、ds也不能用,因为cs:ip时刻指向当前
指令, ds指向datasg 段;可用的就只有: dx 、di 、es 、ss 、sp 、bp等6 个寄存器了。

使用内存空间实现多层循环

可以考虑将需要暂存的数据放到内存单元中,需要使用的时候,再从内存单元中恢复。这样我们就需要开辟一段内存空间。

assume cs:codesg,ds:datasg

datasg segment
    db 'ibm             '   #十六个字符,占用16个字节,不足的用空格补到16个
    db 'dec             '
    db 'dos             '
    db 'vax             '
    dw 0    #前面每个站16字节,所以这个空间偏移量为40H
datasg ends

codesg segment
  start:mov ax,datasg
    mov ds,ax  #设置数据段地址
    mov bx,0  #设置偏移量
    
    mov cx,4
    
  s0:mov ds:[40H],cx    #将外层循环的cx值保存在ds:[40H]中
    mov si,0
    
    mov cx,3    #cx设置为内层循环的次数
    
   s:mov al,[bx+si] 
    and al,11011111b    #转为大写字母
    mov [bx+si],al
    inc si
    loop s
    
    add bx,16    #加16到下一个字母,因为每个db占用16个字节
    mov cx,ds:[40H]    #将datasg:40H单元存储的外层循环恢复到cx中
    loop s0
      
codesg ends

end start

一般来说,在需要暂存数据的时候,都应该使用栈。

使用栈实现多层循环

assume cs:codesg,ds:datasg,ss:stacksg

datasg segment
    db 'ibm             '   #十六个字符,占用16个字节,不足的用空格补到16个
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends

stacksg segment    #定义一个段,用来做栈段,容量为16个字节
  dw 0,0,0,0,0,0,0,0
stacksg ends

codesg segment
  start:mov ax,stacksg
    mov ss,ax
    mov sp,16   #设置栈段和栈顶
    
    mov ax,datasg
    mov ds,ax  #设置数据段地址
    mov bx,0  #设置偏移量
    
    mov cx,4    
    
  s0:push ,cx    #将外层循环的cx值压栈
    mov si,0
    
    mov cx,3    #cx设置为内层循环的次数
    
   s:mov al,[bx+si] 
    and al,11011111b    #转为大写字母
    mov [bx+si],al
    inc si
    loop s
    
    add bx,16    #加16到下一个字母,因为每个db占用16个字节
    pop cx    #从栈顶弹出原cx的值,恢复cx
    loop s0    #外层循环的loop指令将cx中的计数值减一
      
codesg ends

end start

 

点赞

Leave a Reply

Your email address will not be published. Required fields are marked *