• x86汇编语言实践(4)


    0 写在前面

      为了更深入的了解程序的实现原理,近期我学习了IBM-PC相关原理,并手工编写了一些x86汇编程序。

      在2017年的计算机组成原理中,曾对MIPS体系结构及其汇编语言有过一定的了解,考虑到x86体系结构在目前的广泛应用,我通过两个月左右的时间对x86的相关内容进行了学习。

      在《x86汇编语言实践》系列中(包括本篇、x86汇编语言实践(1)x86汇编语言实践(2)x86汇编语言实践(3)以及x86汇编语言复习笔记),我通过几个具体案例对x86汇编语言进行实践操作,并记录了自己再编写汇编代码中遇到的困难和心得体会,与各位学习x86汇编的朋友共同分享。

      我将我编写的一些汇编代码放到了github上,感兴趣的朋友可以点击屏幕左上角的小猫咪进入我的github,或请点击这里下载源代码。

    1 程序设计复习1

    1-1 练习要点

    • 字符串中查找指定字符

    • 字符串中删除指定字符(使用快慢指针

    • 子程序调用的堆栈参数传递

    1-2 实现思路

    • 在数据段存储好待查找的CHAR,和目标字符串STR1,并将二者初始化

    • 主程序中首先将CHAR和STR1压栈

    • 调用FIND_CH子程序查找是否有CHAR

    • 若找到CHAR则调用DELX删除STR1中的X

    • 为了在STR1的原内存空间上操作字符串的修改动作,采用快慢指针的方式进行删除。

    1-3 重点难点

    • 参数传递:使用堆栈进行参数传递,需要将参数压栈,注意子程序返回时,必须增加一个常数偏移量RET X。这里的X为压入参数所占的字节数,通常为2的倍数,以保证堆栈平衡

    • 子程序保存现场:在子程序中,往往要用到很多寄存器,但我们希望在子程序返回时,调用子程序位置处周围的变量仍能恢复,这就需要在调用的子程序中保存现场,即子程序中所用到或修改的所有寄存器,都必须压栈处理

    • 子程序中的堆栈寻址:使用BP寄存器寻址,这是为了不修改SP指针,避免弄乱堆栈栈顶指针SP

    • 快慢指针:与高级语言程序设计中的思路类似,首先将快慢指针指向STR1的头部,之后循环STR1的长度LEN次,若快指针SI指向的位置的字符不为CHAR,则将SI复制到慢指针DI,否则只将SI++。这里用到的技巧是可以使用LODSB和STOSB自动实现快指针SI与慢指针DI的自增操作

    1-4 代码实现

      1 STACK     SEGMENT    PARA    STACK
      2         DW        100H DUP(?)
      3 STACK    ENDS
      4 
      5 DATA    SEGMENT    PARA
      6     LEN        EQU 12
      7     CHAR     DB     'X'
      8     STR1     DB     'CHENQIXIAN',13,10,'$'
      9     MSG1     DB     'X IN CHENQIXIAN',13,10,'$'
     10     MSG2    DB     'NOT FOUND X IN CHENQIXIAN',13,10,'$'
     11     STR2    DB    LEN DUP(?)
     12 DATA     ENDS
     13 
     14 CODE     SEGMENT PARA
     15         ASSUME    CS:CODE,DS:DATA,SS:STACK
     16 OUTPUT    MACRO MSG
     17         PUSH     AX
     18         PUSH     DX
     19         MOV     DX,OFFSET MSG
     20         MOV     AH,9
     21         INT     21H
     22         POP        DX
     23         POP     AX
     24         ENDM
     25 
     26 DELX     PROC
     27     DELETEX:
     28         PUSH     BP
     29         MOV     BP,SP
     30         PUSH     SI
     31         PUSH     DI
     32         PUSH     CX
     33         PUSH     AX
     34 
     35         MOV     SI,[BP+4]
     36         MOV     DI,[BP+6]
     37         MOV     CX,LEN
     38 
     39     DELX_LP:
     40         LODSB
     41         CMP     AL,'X'
     42         JE         DELX_CONTINUE
     43         STOSB
     44     DELX_CONTINUE:    
     45         LOOP     DELX_LP
     46 
     47     DELETEXRET:
     48         MOV     AL,'$'
     49         STOSB
     50         POP     AX
     51         POP     CX
     52         POP     DI
     53         POP     SI
     54         POP     BP
     55         RET     4
     56 DELX     ENDP
     57 
     58 FIND_CH PROC
     59     FINDCHAR:
     60         PUSH     BP
     61         MOV     BP,SP
     62         PUSH     AX
     63         PUSH    DI
     64         PUSH     CX
     65 
     66         MOV         AX,[BP+4]
     67         MOV     DI,[BP+6]
     68         MOV        CX,LEN
     69 
     70         CLD
     71         REPNZ     SCASB
     72         JZ         FOUND
     73         OUTPUT  MSG2
     74         JMP     SHORT FINDCHARRET
     75     FOUND:
     76         OUTPUT    MSG1
     77         MOV        DX,OFFSET STR1
     78         PUSH     DX
     79         PUSH     DX
     80         CALL     DELX
     81     FINDCHARRET:
     82         POP     CX
     83         POP     DI
     84         POP     AX
     85         POP     BP
     86         RET     4
     87 FIND_CH ENDP
     88 
     89 MAIN    PROC     FAR
     90     MAINPROC:
     91         MOV     AX,DATA
     92         MOV     DS,AX
     93         MOV     ES,AX
     94 
     95         MOV     DX,OFFSET STR1
     96         PUSH     DX
     97         MOV     DL,CHAR
     98         XOR     DH,DH
     99         PUSH     DX
    100         CALL     FIND_CH
    101 
    102     EXIT:    
    103         MOV     AX,4C00H
    104         INT     21H
    105 MAIN     ENDP
    106 
    107 CODE     ENDS
    108         END     MAIN

    1-5 实现效果截图

    1-5-1 程序运行结果

     

    经验证,发现输出结果符合预期

    1-5-2 查看删除后内存中新的字符串

     

    经验证,发现内存中的结果符合预期

    2 程序设计复习2

    2-1 练习要点

    • 字符的输入输出

    • 数字读入存储逻辑

    • 数字的最优输出方式

    2-2 实现思路

    • 首先为读入字符和输出数字分别单独编写子程序

    • 主程序中循环调用读入字符,由于题目固定读入两位十进制数,因此读入的第一个数乘10加上第二个读入的数,即为读入的数字

    • 在输出上的改进:仍是除10显示,但这次保存余数。为了得到正序输出,将每次的余数压栈,这样在显示的时候就是从高位向低位显示了。此外,在输出时对前导0进行了过滤处理,需要注意的是当遇到第一个非0数字后,需要将标志位置1,这样以后的数字0就可以正常显示

    2-3 代码实现

      1 STACK     SEGMENT    PARA    STACK
      2         DW        100H DUP(?)
      3 STACK    ENDS
      4 
      5 DATA    SEGMENT    PARA
      6     LEN EQU 5
      7     X     DB     0
      8     Y     DB     0
      9     Z     DB     ?
     10     NL     DB    13,10,'$'
     11 DATA     ENDS
     12 
     13 CODE     SEGMENT PARA
     14         ASSUME    CS:CODE,DS:DATA,SS:STACK
     15 NEWLINE MACRO
     16         PUSH     AX
     17         PUSH     DX
     18         MOV     DX,OFFSET NL
     19         MOV     AH,9
     20         INT     21H
     21         POP     DX
     22         POP     AX
     23         ENDM
     24 GETNUM  PROC
     25     INPUT:
     26         MOV     AH,1
     27         INT     21H
     28         SUB         AL,30H
     29         XOR     AH,AH
     30         RET
     31 GETNUM     ENDP
     32 
     33 OUTPUT     PROC
     34     PRINT:
     35         PUSH     DX
     36         PUSH     CX
     37         PUSH    BX
     38         PUSH     AX
     39         NEWLINE
     40         MOV     CX,LEN
     41         MOV     BX,10
     42     PRINT_LP1:
     43         XOR     DX,DX
     44         DIV        BX
     45         PUSH    DX
     46         LOOP     PRINT_LP1
     47 
     48         MOV     CX,LEN
     49         MOV     BX,0
     50     PRINT_LP2:
     51         POP     DX
     52         CMP     DL,0
     53         JNE        PRINT_LP2_1
     54         CMP        BX,0
     55         JZ         PRINT_LP2_2
     56     PRINT_LP2_1:
     57         MOV     BX,1
     58         MOV     AH,2
     59         OR         DL,30H
     60         INT     21H
     61         
     62     PRINT_LP2_2:
     63         LOOP     PRINT_LP2
     64         POP     AX
     65         POP     BX
     66         POP     CX
     67         POP     DX
     68         RET
     69 OUTPUT     ENDP
     70 
     71 
     72 MAIN    PROC     FAR
     73     MAINPROC:
     74         MOV     AX,DATA
     75         MOV     DS,AX
     76         MOV     ES,AX
     77 
     78         CALL     GETNUM
     79         MOV     BL,10
     80         MUL        BL
     81         MOV        X,AL
     82         CALL    GETNUM
     83         ADD        X,AL
     84 
     85         CALL    GETNUM     
     86         MOV        BL,10
     87         MUL        BL
     88         MOV     Y,AL
     89         CALL    GETNUM
     90         ADD        Y,AL
     91 
     92         MOV     AL,X
     93         MOV         BL,Y
     94         MUL     BL
     95 
     96         CALL     OUTPUT
     97 
     98     EXIT:    
     99         MOV     AX,4C00H
    100         INT     21H
    101 MAIN     ENDP
    102 
    103 CODE     ENDS
    104         END     MAIN

    2-4 运行结果

       

    显然,运行结果符合预期。

    3 程序设计复习3

    3-1 练习要点

    • 字符串读取:0AH号中断调用

    • 字符串拷贝

    • 子程序调用参数的传递与保持

    3-2 实现思路

    • 首先为读入字符串和输出字符串分别单独编写子程序

    • 输入待插入字符串后,首先调用第一次拷贝字符串子程序,判断条件为读取到空格即停止拷贝。注意边界条件的判断,以及最后一次拷贝后SI与DI的保持

    • 紧接着在主程序中将SI压栈保存,将SI指向待插入字符串首地址,调用插入子程序。将待插入字符串拼接到目标串尾部

    • 最后将SI弹出栈恢复,即又指向原列表空格后的第一个字符的位置处,调用第二次拷贝字符串子程序。此时边界判断条件为’$’符号

    • 输出目标串

    3-3 代码实现

      1 STACK     SEGMENT    PARA    STACK
      2         DW        100H DUP(?)
      3 STACK    ENDS
      4 
      5 DATA    SEGMENT    PARA
      6     LEN     EQU    32
      7     LIST    DB    'ABOVE ZEBRA$'
      8     TEMP     DB     LEN DUP(?)
      9     NL         DB     13,10,'$'
     10     STR1    DB    LEN-1
     11             DB     ?
     12             DB     LEN DUP(?)     
     13 DATA     ENDS
     14 
     15 CODE     SEGMENT PARA
     16         ASSUME    CS:CODE,DS:DATA,SS:STACK
     17 NEWLINE MACRO
     18         PUSH     DX
     19         PUSH    AX
     20         MOV     DX,OFFSET NL
     21         MOV     AH,9
     22         INT     21H
     23         POP     AX
     24         POP     DX        
     25         ENDM
     26 
     27 OUTPUT     MACRO     MSG
     28         PUSH     DX
     29         PUSH     AX
     30         NEWLINE
     31         MOV     DX,OFFSET MSG
     32         MOV     AH,9
     33         INT     21H
     34         POP     AX
     35         POP     DX
     36         ENDM
     37 
     38 INPUT     PROC
     39     INPUTSTR1:
     40         PUSH     DX
     41         PUSH     AX
     42         PUSH     SI
     43 
     44         MOV     DX,OFFSET STR1
     45         MOV     AH,0AH
     46         INT     21H
     47         MOV     SI,OFFSET STR1+2
     48         MOV     AL,STR1+1
     49         XOR     AH,AH
     50         ADD     SI,AX
     51         MOV     BYTE PTR [SI],'$'
     52 
     53         POP     SI
     54         POP     AX
     55         POP     DX
     56         RET
     57 INPUT     ENDP
     58 
     59 COPY     PROC
     60     STRCPY:
     61         LODSB
     62         CMP     AL,20H
     63         JE         COPYRET
     64         STOSB
     65         JMP     STRCPY
     66     COPYRET:
     67         STOSB
     68         RET
     69 COPY     ENDP
     70 
     71 INSERT     PROC
     72     INSERT_STR1:
     73         MOV     CL,STR1+1
     74         XOR     CH,CH
     75     INSERT_LP:
     76         LODSB
     77         STOSB
     78         LOOP     INSERT_LP
     79         MOV     AL,20H
     80         STOSB
     81         RET
     82 INSERT     ENDP
     83 
     84 COPY2     PROC
     85     STRCPY2:
     86         LODSB
     87         CMP     AL,'$'
     88         JE         COPYRET2
     89         STOSB
     90         JMP     STRCPY2
     91     COPYRET2:
     92         STOSB
     93         RET
     94 COPY2     ENDP
     95 
     96 MAIN    PROC     FAR
     97     MAINPROC:
     98         MOV     AX,DATA
     99         MOV     DS,AX
    100         MOV     ES,AX
    101         OUTPUT     LIST
    102         CALL     INPUT
    103         MOV     SI,OFFSET LIST
    104         MOV     DI,OFFSET TEMP
    105         CALL     COPY
    106         PUSH     SI
    107         MOV     SI,OFFSET STR1+2
    108         CALL     INSERT
    109         POP     SI
    110         CALL     COPY2
    111         OUTPUT     TEMP
    112 
    113     EXIT:    
    114         MOV     AX,4C00H
    115         INT     21H
    116 MAIN     ENDP
    117 
    118 CODE     ENDS
    119         END     MAIN

    3-4 运行结果

     

    4 程序设计复习4

    4-1 练习要点

    • 16进制输出方式

    • 从10向A的转化

    • 2号中断调用输出单个字符

    4-2 实现思路

    • 首先在数据段初始化一个64位数字

    • 注意由于一个字是2个字节16位,因此在输出时,要依次在基地址的基础上+2

    • 由于是循环访问数据段中的除数,因此用SI寄存器记录数据段中除数的位置,每次循环都要使用两次INC指令,保证访问到下一个字中的内容。

    • 访问除数必须用WORD PTR [SI],否则会提示 ’must have size’

    • 判断16进制输出的数字是否大于10,若不大于则直接输出,否则需要加7(在ASCII数值上‘9’和‘A’之间差8),注意从数字转换为ASCII码此处必须用ADD 30H 来代替 OR 30H,否则会出现错误。

    4-3 代码实现

     1 STACK     SEGMENT    PARA    STACK
     2         DW        100H DUP(?)
     3 STACK    ENDS
     4 
     5 DATA    SEGMENT    PARA
     6     NUM     DW     1606H,1160H,1234H,0FFFFH
     7     DIVISOR DW     1000H,100H,10H,1H
     8 DATA     ENDS
     9 
    10 CODE     SEGMENT PARA
    11         ASSUME    CS:CODE,DS:DATA,SS:STACK
    12 
    13 OUTPUT     PROC
    14     PRINT:
    15         MOV     SI,OFFSET DIVISOR
    16         MOV     CX,4
    17     OUTPUT_LP:
    18         XOR     DX,DX
    19         DIV     WORD PTR [SI]
    20         PUSH     DX
    21         CMP     AL,10
    22         JB        OUTPUT_CONTINUE
    23         ADD     AL,7
    24     OUTPUT_CONTINUE:
    25         ADD     AL,30H
    26         MOV     DL,AL
    27         MOV     AH,2
    28         INT     21H
    29         INC     SI
    30         INC     SI
    31         POP     AX
    32         LOOP     OUTPUT_LP
    33         RET
    34 OUTPUT     ENDP
    35 
    36 MAIN    PROC     FAR
    37     MAINPROC:
    38         MOV     AX,DATA
    39         MOV     DS,AX
    40         MOV     ES,AX
    41 
    42         MOV     AX,NUM
    43         CALL     OUTPUT
    44 
    45         MOV     AX,NUM+2
    46         CALL     OUTPUT
    47 
    48         MOV     AX,NUM+4
    49         CALL     OUTPUT
    50 
    51         MOV     AX,NUM+6
    52         CALL     OUTPUT
    53 
    54     EXIT:    
    55         MOV     AX,4C00H
    56         INT     21H
    57 MAIN     ENDP
    58 
    59 CODE     ENDS
    60         END     MAIN

    4-4 运行结果

    【数据段】

     

    【运行结果】

     

                                      

  • 相关阅读:
    jq获取img高度(动态生成的image高度为0原因)
    Idea集成使用SVN教程
    RPC框架pigeon源码分析
    java多线程面试题整理及答案
    深入理解JVM线程模型
    dubbo 2.8.4(dubbox)的jar包制作【添加到maven本地仓库】
    【学习】027 Dubbo
    【学习】026 Zookeeper
    【学习】025 RocketMQ
    【学习】024 springCloud
  • 原文地址:https://www.cnblogs.com/chrischen98/p/10808861.html
Copyright © 2020-2023  润新知