混子汇编语言学习之07笔记

作者: 那个混子 | 来源:发表于2021-11-02 23:45 被阅读0次

    看了第七章内容,大概记录一下

    引言

    这段时间学的有点慢,不过慢慢的也看了7章的内容了,下面也是通过例子熟悉学习,这本书是循序渐进的,还不错,这章也是基于前面几章内容递进的,稍微慢慢的复杂起来一点了。

    第七章 更灵活的定位内存地址的方法

    1、and 和 or

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

    mov al, 01100011B
    and al, 00111011B
    执行后:al=00100011B即都为1才为1
    

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

    mov al, 01100011B
    or al, 00111011B
    执行后:al=01111011B 即只要有一个为1就为1
    

    2、关于ASCII码

    一句话就是计算机对数据编码的一种约束规则,就是类似于生活中要求我们绿灯行,红灯停一样的规定,没有什么稀奇的,也不要去追究为什么是那个,记住就行,下面是资料中描述介绍。

    世界上有很多编码方案,有一种方案叫做ASCII编码,是在计算机系统中通常被采用的。简单地说,所谓编码方案,就是一套规则,它约定了用什么样的信息来表示现实对象。比如说,在ASCII编码方案中,用61H表示“a”,62H表示“b”。一种规则需要人们遵守才有意义。
    在文本编辑过程中,我们按一下键盘的a键,就会在屏幕上看到“a”。我们按下键盘的a键,这个按键的信息被送入计算机,计算机用ASCII码的规则对其进行编码,将其转化为61H存储在内存的指定空间中;文本编辑软件从内存中取出61H,将其送到显卡上的显存中;工作在文本模式下的显卡,用ASCII码的规则解释显存中的内容,
    61H被当作字符“a”,显卡驱动显示器,将字符“a”的图像画在屏幕上。我们可以看到,显卡在处理文本信息的时候,是按照ASCII码的规则进行的。这也就是说,如果我们要想在显示器上看到“a”,就要给显卡提供“a”的ASCIⅡ码,61H。如何提供?当然是写入显存中。

    以字符形式给出的数据
    assume cs:code,ds:data 
    data segment 
        db 'unIx'   ;相当于“db 75H,6EH,49H,58H”
        db 'foRK'
    data ends 
    
    code segment
    start:  mov al, 'a'  ;相当于“mov al, 61H”,“a”的ASCI码为61H;
            mov b1, 'b'
            
            mov ax, 4c00h 
            int 21h 
    code ends
    end start
    

    3、大小写转换问题

    • 法一:小写字母的ASCII码值比大写字母的ASCII码值大20H(如果是十进制就是相差32)
    • 法二:大写字母ASCII码的第5位为0,小写字母的第5位为1(其他一致)
      上述的方法,第二种自己之前也不知道。
      下面就着重了解第二种方法的实现。
      分析:无论字母是大写还是小写,只要把字母对应的ASIIC码值对应的二进制的第5位置0,它就变成了大写字母,反之,将第五位置1则变为小写字母了。基于这个,基于对and、or语法的了解,很容易实现。
    实现目的:
    将 datasg 中的第一个字符串转化为大写,第二个字符串转化为小写。
    
    assume cs:codesg,ds:datasg    
    datasg segment                   ;定义datasg 数据段
        db 'BaSiC'
        db 'iNfOrMaTion'
    datasg end
    
    codesg segment                  ;定义codesg段码段
        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'有11个字母
        s0:     mov al, [bx]
                or al, 00100000B    ;将a1中的ASCII码的第5位置为1,变为小写字母
                mov [bx], al 
                inc bx
                loop s0
                
                mov ax, 4c00h 
                int 21h 
    codesg ends
    

    4、[bx+idata]

    [bx+idata]表示一个内存单元, 例如:mov ax, [bx+200]
    该指令也可以写成如下格式:

    • mov ax, [200+bx]
    • mov ax, 200[bx]
    • mov ax, [bx].200
      例子含义说明:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为bx中的数值加上 200,段地址在ds 中。
      数学化的描述为:(ax)=((ds)*16+(bx)+200)
      下面是书上的一个例子,第一遍看的时候,我去算了一下,不太理解为啥等于对应的值,看了后面的内容后,又倒过来看,才发现这里是取地址里面的值,并不是说内存地址编号。现在明白了
    汇编语言与C语言处理数组的对比
    • (1)汇编语言
      用[bx+idata]的方式进行数组的处理
    assume cs:codesg,ds:datasg 
    datasg segment 
        db 'BaSiC';转为大写
        db 'MinIx';转为小写
    datasg ends
    
    codesg segment
        start:
            mov ax, datasg 
            mov ds, ax 
            mov bx, 0  ;初始ds:bx
        
            mov cx, 5
        s:  mov al, 0[bx]  
            and al, 11011111b ;转为大写字母
            mov 0[bx], al ;写回
            mov al, 5[bx]  ;[5 + bx]
            or al, 00100000b ;转为小写字母
            mov 5[bx], al 
            inc bx
            loop s
            
            mov ax, 4c00h 
            int 21h
    codesg ends
    end start
    
    • (2)C语言实现
    int main()
    {
        char a[] = "BaSic";
        char b[] = "MinIX";
        
        int i = 0;
        
        do
        {
            a[i] = a[i] & 0xDF;
            b[i] = b[i] | 0x20;
            i++;
        } while(i < 5);
    
        return 0;
     } 
    

    结论:
    C语言: a[i],b[i]
    汇编语言:o[bx],5[bx]
    通过比较,我们可以发现,[bx+idata]的方式为高级语言实现数组提供了便利机制。

    睡觉了,这一章内容稍微有点多,明晚继续!
    接着前面的记录!

    5、SI、DI

    介绍:si和di是8086CPU中和bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用。(可以等同于bx来看,就是提供偏移地址的寄存器)
    SI和DI是16位的寄存器,而且不能被拆分为两个8位寄存器。
    那直接来看老子,书上的一个例子:
    实现的功能是用si和di实现将字符串'welcome to masm!'复制到它后面的数据区中。
    简要分析:datasg:0,这是要进行复制的数据的地址。“welcome to masm!”从偏移地址О开始存放,长度为16个字节,所以,它后面的数据区的偏移地址为16,就是字符串 ....”存放的空间。用ds:si 指向要复制的源始字符串,用ds:di 指向复制的目的空间,然后用一个循环来完成复制。代码段如下。

    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            ;定义循环变量为8次,(因为操作的对象是字,占两个字节)
        s:      mov ax, 0[si]        ;    等同于 [0 + si],也类似于下标为0的数组啦
                mov 16[si], ax       ;[16 + si] 使用[bx +idata]方式代替di,使程序更简洁
                add si, 2            ;内存单元是字,一个字占两个字节,故要加2;
                loop s 
                
                mov ax, 4c00h 
                int 21h 
    codesg ends 
    end start
    

    上述中,是简化后的程序,如果没有简化就是同时试音SI和DI两个寄存器了,在这里,我们可以类比C语言中的两个数组变量,这两个数组的地址是连续的。
    2、[bx + si] 和 [bx + di]

    6、几种不同方式寻址

    书中介绍了许多,但是实际操作起来的思路都是一样的,有许多的搭配。下面只是简述。

    • [bx+si]、[bx+di] (两种类似理解)
      [bx+si]表示一个内存单元,它的偏移地址为(bx)+(si)
      mov ax, [bx + si]的含义:将一个内存单元字数据的内容送入ax,段地址在ds中
      也可以写成如下格式:mov ax, [bx][si]
    • [bx+si+idata]和[bx+di+idata]
      [bx+si+idata]表示一个内存单元,它的偏移地址为(bx)+(si)+idata
      mov ax,[bx+si+idata]的含义:将一个内存单元字数据的内容送入ax,段地址在ds中
    • 几种寻址方式
      • [idata]用一个常量来表示地址,可用于直接定位一个内存单元;
      • [bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;
      • [bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;
      • [bx+si]用两个变量表示地址;
      • [bx+si+idata]用两个变量和一个常量表示地址。

    下面以资料中的一个例子结束本章的学习,综合运用了前面的一些知识点。
    资料上写得很详细如何去实现,把内存看成二维数组,然后去操作需要改变的,是有规律的。;
    将datasg段中每个单词改为大写字母
    具体看图

    assume cs:codesg,ds:datasg,ss:stacksg 
    datasg segment
        db 'ibm            ' ;定义对应的字符串,字符串占16字节
        db 'dec            ' 
        db 'dos            '
        db 'vax            '  
                            ;上述中由于datasg 的地址是连续的,所以我们可以
                            ;把上述的4个字符串定义的空间可以看成4行×16列的一个二维数组。
    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                    ;初始ds:bx
                
                                             ;cx为默认循环计数器,二重循环只有一个计数器,
                                             ;所以外层循环先保存cx值,再恢复,我们采用栈保存
                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                   ;下一行
                pop cx                       ;恢复cx值
                loop s0                      ;外层循环的loop指令将cx中的计数值减1
                
                mov ax,4c00H 
                int 21H 
    codesg ends
    end start
    

    上述例子理解起来还是稍微可能有点吃力的,因为不熟,但是上面的思想就类似C语言的两个for循环,编历了一个4×3的二维数组元素。
    很重要的一点是使用栈来保存了中间的循环变量。需要反复揣摩,反复看。

    这一章就暂时结束了,后续慢慢回顾。

    参考资料 王爽《汇编语言第三版》
    欢迎关注本人微信公众号:那个混子
    记录自己学习的过程,分享乐趣、技术、想法、感悟、情感!
    单片机类嵌入式交流学习可加企鹅群:120653336

    相关文章

      网友评论

        本文标题:混子汇编语言学习之07笔记

        本文链接:https://www.haomeiwen.com/subject/duztzltx.html