• 汇编语言:知识点拾遗


    汇编语言:知识点拾遗

    前言

    读王爽的书已经有一段时间了,马上就要学习有关“中断”的知识了。在继续学习之前,我想把一些我感觉比较有意思的小细节,小技巧总结一下。文章不长,如果以后有新的收获,我会把这一系列继续写下去。

    大小写字转换的技巧

    一般来说,要想实现大小写字母的转换,有两种基本思路。

    1. 根据大小写字母在ascii表中的相对位置进行转换
    2. 根据字母在字母表中相对于首字母a的位置进行转换

    第一种思路:
    如果字母为大写字母,小写字母 = 大写字母 + 20H
    如果字母为小写字母,大写字母 = 小写字母 - 20H

    第二种思路:
    如果字母为大写字母,小写字母 = ‘a’ + 大写字母 - ‘A’
    如果字母为小写字母,大写字母 = ‘A’ + 小写字母 - ‘a’

    王爽的书中有提供了令一种方法:
    假设当前字母在寄存器AL中
    如果AL为大写字母,则and al,11011111B
    如果AL为小写字母,则 or al,00100000B
    此时AL中即为所要转换的结果。

    为什么可以这样做呢?我下面给出一张表格,供大家验证。

    大写十六进制二进制小写十六进制二进制
    A 41H 01000001 a 61H 01100001
    B 42H 01000010 b 62H 01100010
    C 43H 01000011 c 63H 01100011
    D 44H 01000100 d 64H 01100100
    E 45H 01000101 e 65H 01100101
    F 46H 01000110 f 66H 01100110

    JMP、CALL、RET指令的机制

    以下用s表示标号

    • jmp short s——依据位移修改IP进行段内短转移(8位位移)
    • jmp near ptr s——依据位移修改IP进行段内近转移(16位位移)
    • jmp far ptr s——段间远转移,使(CS) = s的段地址,(IP) = s的偏移地址
    • jmp 16位reg——使(IP) = (reg)
    • jmp word ptr 内存单元地址——使(IP) = (内存单元地址)
    • jmp dword ptr 内存单元地址——使(CS) = (内存单元地址+2),(IP) = (内存单元地址)

    详细内容见原书。

    我们可以用汇编语言(尽管不符合语法)去理解ret和call指令

    • ret——相当于pop IP
    • retf——相对于先pop IP,然后pop CS
    • call s——相当于先push IP,然后jmp near ptr s
    • call far ptr s——相当于先push CS,再push IP,最后jmp far ptr s
    • call 16位reg——相当于push IP,然后jmp reg(16位)
    • call word ptr 内存单元地址——相当于push IP,然后jmp word ptr 内存单元地址
    • call dword ptr 内存单元地址——相当于先push CS,再push IP,最后jmp dword ptr 内存单元地址

    由于call与ret常常配合使用,所以之前我以为这两条指令必须成对出现(在设计子程序的时候)。汇编语言是相当自由的语言,当然没有这种约束,这些指令你想怎么用,你就怎么用,只不过它们的配合使用是一种“套路”罢了。
    如果不熟悉call,ret的执行原理,在设计子程序的堆栈传参时,就会很难理解。
    我在课堂上学习这两条指令的时候,老师完全没说堆栈的事,之后我发现有一个实验(输出一个集合的所有子集)是要用递归的技巧的,我当时就要骂人了,老师当时堆栈传参讲的不清不楚(说难听点,讲了和没讲是一样的),果断放弃这个选题,选了一个更简单的实验,但这样怎么体现出我认真的学习态度呢?(不要脸
    后来学习了王爽的书,弄懂了这些原理,堆栈传参就很好理解了。

    最后补充一点:

    • ret n——相当于pop IP,然后add sp,n

    C语言中局部变量也在堆栈中存放。

    标志位DF与串处理指令

    这部分老师在课堂上是不讲的(不知道其他学校是怎么样的)。其实这部分很简单。
    方向标志位DF

    • DF = 1,每次操作后inc si, inc di
    • DF = 2,每次操作后dec si, dec di

    cld指令使DF=0,std指令DF =1

    下面用汇编指令(不符合语法)来理解下面两条指令

    movsb

    功能:

    mov         es:[di], byte ptr ds:[si]
    ;如果DF = 0
                    inc si
                    inc di
    ;如果DF = 1
                    dec si
                    dec di

    movsw

    功能:

    mov         es:[di], word ptr ds:[si]
    ;如果DF = 0
                    add     si,2
                    add     di,2
    ;如果DF = 1
                    sub      si,2
                    sub      di,2

    rep指令

    rep指令常常和以上两条指令配合使用。举个例子:
    rep movsb 相当于

    s:      movsb
            loop  s    ;根据CX决定循环次数

    小结

    串传送指令的注意点

    • 传送的初始位置:ds : si
    • 传送的目的位置:es : di
    • 传送的长度: CX
    • 传递的方向: 标志为DF

    其他注意事项

    由于篇幅原因(其实就是懒得再写下去了),还有一些注意点就不详细讨论了。
    比如:
    - 除法指令的溢出问题
    - 80X25彩色字符模式的显示缓冲区

    总结

    汇编语言是一门相当自由的语言,只要你有耐心,你可以用它完成好多事情。
    这种感觉是在学习王爽的教材中体会得到。在此之前,我只是通过学校课堂大概了解了一下汇编语言,我甚至不知道数据段,代码段,堆栈段是可以通过自己修改段寄存器来“定义”的(就是蠢,没别的解释)。但这种自由也带来一种坏处,就是代码的可读性较差,核心代码往往被其他操作掩盖(比如中间值传递,传参,保护寄存器),因此看似很长的代码,实际实现的功能很简单(这让我怎么显摆),甚至昨天刚写的代码,第二天居然读不懂了(好吧,我只是因为懒,没写注释,但不管怎么说,汇编就是难读!)。
    但不管怎么说汇编还是很有用的。下面就用王爽书中的实验11——编写子程序,结束这篇博文。(我怎么还没写注释!反正没有老师看,偷个懒啦)

    assume      cs:codesg
    
    datasg      segment
        db 'Beginner`s All-purpose Symbolic Instruction Code.',0
    datasg      ends
    
    codesg      segment
    
    main:       mov     ax,datasg
                mov     ds,ax
                mov     si,0
                call    letterc
    
                mov     ax,4c00h
                int     21h
    ;-----------------------------------------------------------
    ;proc_name:     letterc
    ;function:      translate uppercase into lowercase in a string which ends with digit 0
    ;interface:     ds:si points to the first address of the string
    
    letterc:            push    ax
                        push    si
    
        letterc_s:      mov     al,[si]
                        cmp     al,'a'
                        jb      letterc_next
                        cmp     al,'z'
                        ja      letterc_next
                        and     al,11011111b
                        mov     [si],al
        letterc_next:   inc     si
                        cmp     al,0
                        je      letterc_out
                        jmp     short letterc_s
    
        letterc_out:    pop     si
                        pop     ax
                        ret
    ;----------------------------------------------------------
    codesg      ends
    
    end   main
  • 相关阅读:
    Pwn-level3
    【mysql】 mysql忘记密码
    【Linux】linux磁盘管理
    【Linux】Linux系统LVM管理及Raid
    【Git】git撤销与回滚
    【linux】Linux系统SELinux简介
    【Linux】Linux中的网络命令
    【Linux】linux中文本操作利器grep,awk,sed
    【Linux】linux正则表达式及通配符
    【Mysql】mysql数据备份
  • 原文地址:https://www.cnblogs.com/wyf12138/p/6581527.html
Copyright © 2020-2023  润新知