zoukankan      html  css  js  c++  java
  • 汇编语言-07灵活定位内存地址

    and和or指令

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

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

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

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

    ASCII码

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

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

    以字符形式给出的数据

    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。

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

    大小写转换

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

    可以看出来,小写字母的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位寄存器来使用。

    下面三组实现相同功能:

     下面三组实现相同功能:

    用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实现多层循环

    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
  • 相关阅读:
    w3wp.exe占用CPU100%的解决办法
    Visual Studio 2005 查找和替换窗口 显示不了
    IIS:w3wp.exe进程占用cpu和内存过多的处理办法
    C# form ComboBox
    从尾到头打印链表,不允许逆置原链表
    [置顶] ATL窗口thunk机制的剖析与实现
    flex自定义用ArrayCollection做数据源的带checkbox的tree(功能强大的完美版^_^)
    oracle的PremaredStatement.executeBatch为什么返回2
    窗体Controls的OfType<>方法的使用
    HDU 1421 动态规划(DP) 搬寝室
  • 原文地址:https://www.cnblogs.com/aeolian/p/12895984.html
Copyright © 2011-2022 走看看