• 霸道的代码,不能加注释


      在读《深入理解程序设计使用LInux汇编语言》[美]Jonathan Bartlett著 郭晴霞 译 人民邮电出版社这本书时,觉得对函数的调用有了一种醍醐灌顶的感觉,不是一般的爽。一路下来到第五章的在程序中使用文件时,按照书上的代码写完之后,一直不能编译,后来倒腾了下发现是由于“擅自更改了注释的位置”,废话不说,直接上代码:

      1 #目的:     本程序将输入文件的所有字母转换为大写字母                                                                                                                                                                                                                                                                                       
      2 #          最后将转换后的文件输出到新的文件
      3 
      4 #处理过程   (1)、打开输入文件
      5 #          (2)、打开输出文件
      6 #          (3)、读取输入文件,如果未达到文件尾部:
      7 #               (a)、将部分文件读入内存缓冲区
      8 #               (b)、读取内存缓冲区的每个字节,若该字节为小写字符,将其转换为大写字符
      9 #               (c)、将内存缓冲区写入输出文件
     10 
     11 .section .data
     12 
     13 #######常数#######
     14 
     15 #系统调用号
     16 .equ SYS_CLOSE, 6
     17 .equ SYS_OPEN, 5
     18 .equ SYS_WRITE, 4
     19 .equ SYS_READ, 3
     20 .equ SYS_EXIT, 1
     21 
     22 #文件打开选项(不同的值可参看/usr/include/asm/fcntl.h)
     23 #可以将选项值相加或进行OR操作来组合使用各选项
     24 .equ O_RDONLY, 0
     25 .equ O_CREAT_WRONLY_TRUNC, 03101
     26 
     27 #标准文件描述符
     28 .equ STDIN, 0
     29 .equ STDOUT, 1
     30 .equ STDERR, 2
     31 
     32 #系统调用中断
     33 .equ LINUX_SYSCALL, 0x80
     34 .equ END_OF_FILE, 0                                         #这是读文件操作的返回值,表明读取到文件结束处
     35 .equ NUMBER_ARGUMENTS, 2
     36 
     37 .section .bss
     38 #缓冲区     将文件加载到这里,这里是存放未初始化的数据的地方
     39 #           也是将这里的文件完成大小写的转换工作
     40 #           将这里转换后的文件(缓冲区)输出到文件中
     41 #           缓冲区的大小有严格规定,这里不超过16000字节
     42 .equ BUFFER_SIZE, 600
     43 .lcomm BUFFER_DATA, BUFFER_SIZE                             #通过指令.lcomm申请缓冲区,需要设置缓冲区的大小
     44 
     45 .section .text
     46 
     47 #栈位置
     48 .equ ST_SIZE_RESERVE, 8
     49 .equ ST_FD_IN, -4
     50 .equ ST_FD_OUT, -8
     51 .equ ST_ARGC, 0                                             #参数数目
     52 .equ ST_ARGV_0, 4                                           #程序名
     53 .equ ST_ARGV_1, 8                                           #输入文件名
     54 .equ ST_ARGV_2, 12                                          #输出文件名
     55 
     56 .globl _start
     57 _start:
     58 #######程序初始化#######
     59 movl %esp, %ebp                                             #保存栈指针
     60 #sub $ST_SIZE_RESERVE,%esp                                 #在栈上为文件描述符分配空间
     61 sub $ST_SIZE_RESERVE, %esp
     62 
     63 open_file:
     64     open_fd_in:
     65         #######打开输入文件######
     66         movl $SYS_OPEN, %eax                                #打开系统调用,将调用号放入到%eax中
     67         movl ST_ARGV_1(%ebp), %ebx                          #将输入文件名放入%ebx中
     68         movl $O_RDONLY, %ecx                                #将只读标志放入%ecx中
     69         movl $0666, %edx                                    #将权限集合放入%edx中
     70         int $LINUX_SYSCALL                                  #调用Linux内核完成系统调用
     71 
     72     store_fd_in:
     73         movl %eax, ST_FD_IN(%ebp)                           #保存给定的文件描述符
     74 
     75     open_fd_out:
     76         #######打开输出文件#######
     77         movl $SYS_OPEN, %eax                                #打开系统调用,将调用号放入到%eax中
     78         movl ST_ARGV_2(%ebp), %ebx                          #将输出文件名放入%ebx中
     79         movl $O_CREAT_WRONLY_TRUNC, %ecx                    #将写入文件标志放入%ecx中
     80         movl $0666, %edx                                    #将权限集合放入%edx中
     81         int $LINUX_SYSCALL                                  #完成函数调用的一切准备工作
     82 
     83     store_fd_out:
     84         movl %eax, ST_FD_OUT(%ebp)                          #保存给定的文件描述符
     85 
     86 #######主循环开始#######
     87 read_loop_begin:
     88 
     89     ###从输入文件中读取一个数据块###
     90     movl $SYS_READ, %eax
     91     movl ST_FD_IN(%ebp), %ebx                               #获取输入文件描述符
     92     movl $BUFFER_DATA, %ecx                                 #放置读取数据的存储地址或指针
     93     movl $BUFFER_SIZE, %edx                                 #设置缓冲区大小
     94     int $LINUX_SYSCALL                                      #通过系统调用,完成一次数据的读取操作,读取的结果就会保存在%eax中
      95                                                                                                                                                                                                                                                                                                                                            
     96     ###若达到文件结束处就退出###
     97     cmpl $END_OF_FILE, %eax                                 #若文件读取结束或出现错误,就跳转到程序结束处
     98     jle end_loop                                            #跳转到文件结束的处理部分
     99 
    100     continue_read_loop:
    101         ###将字符块内容转换成大写形式###
    102         pushl $BUFFER_DATA                                  #缓冲区位置
    103         pushl %eax                                          #缓冲区大小
    104         call convert_to_upper                               #调用转换函数
    105         popl %eax                                           #重新获取缓冲区的大小
    106         addl $4, %esp                                       #恢复%esp
    107 
    108         ###将字符块写入输出文件###
    109         movl %eax, %edx                                     #获取缓冲区大小
    110         movl $SYS_WRITE, %eax                               #将文件描述符放入%eax
    111         movl ST_FD_OUT(%ebp), %ebx                          #
    112         movl $BUFFER_DATA, %ecx                             #
    113         int $LINUX_SYSCALL                                  #
    114 
    115         ###继续循环###
    116         jmp read_loop_begin
    117 
    118     end_loop:
    119         ###关闭文件###
    120         movl $SYS_CLOSE, %eax
    121         movl ST_FD_OUT(%ebp), %ebx
    122         int $LINUX_SYSCALL
    123 
    124         movl $SYS_CLOSE, %eax
    125         movl ST_FD_IN(%ebp), %ebx
    126         int $LINUX_SYSCALL
    127 
    128         ###退出###
    129         movl $SYS_EXIT, %eax
    130         movl $0, %ebx
    131         int $LINUX_SYSCALL
    132 
    133 #目的:     这个函数实际上将字符块内容转换为大写形式
    134 #
    135 #输入:     第一个参数:要转换的内存块的地址
    136 #          第二个参数:缓冲区的长度
    137 #
    138 #输出:     这个函数以大小字符覆盖当前缓冲区
    139 #
    140 #变量:     %eax --- 缓冲区起始地址
    141 #          %ebx --- 缓冲区长度
    142 #          %edi --- 当前缓冲区偏移量
    143 #          %cl  --- 当前正在检测的字节(%ecx)的一个部分
    144 
    145 ###常数###
    146 .equ LOWERCASE_A, 'a'                                       #搜索的下边界
    147 .equ LOWERCASE_Z, 'z'                                       #搜索的上边界
    148 .equ UPPER_CONVERSION, 'A' - 'a'                            #转换常量
    149 
    150 ###栈相关信息###
    151 .equ ST_BUFFER_LEN, 8                                       #缓冲区长度
    152 .equ ST_BUFFER, 12                                          #实际缓冲区大小
    153 
    154 convert_to_upper:
    155     pushl %ebp                                              #保存现场
    156     movl %esp, %ebp                                         #开辟栈帧空间
    157 
    158     ###设置变量###
    159     movl ST_BUFFER(%ebp), %eax
    160     movl ST_BUFFER_LEN(%ebp), %ebx
    161     movl $0, %edi
    162 
    163     cmpl $0, %ebx                                           #若给定的缓冲区长度为0,表明不需要读取
    164     je end_convert_loop                                     #跳转到循环结束
    165 
    166     convert_loop:
    167         movb (%eax, %edi, 1), %cl                           #获取当前字节
    168         cmpb $LOWERCASE_A, %cl
    169         jl next_byte
    170         cmpb $LOWERCASE_Z, %cl
    171         jg next_byte
    172 
    173         addb $UPPER_CONVERSION, %cl                         #将当前字节转换为大写
    174         movb %cl, (%eax, %edi, 1)                           #并存放回原处
    175 
    176         next_byte:
    177             incl %edi                                       #增加%edi的值,进入下一个字节
    178             cmpl %edi, %ebx                                 #继续比较直到文件结束
    179             jne convert_loop
    180 
    181     end_convert_loop:
    182         movl %ebp, %esp
    183         popl %ebp
    184         ret

    代码中都有注释,内容很简单,就不多说了。下面说下我碰到的奇怪的事情吧。

    老规矩:

    1、编译文件,使用命令as toUppers.s -o toUppers.o

      提示有错误:toUppers.s: Assembler messages:                          
    toUppers.s:60: 错误:number of operands mismatch for `sub'    ,把所有可能的情况都考虑完之后,万般无奈之后把注释给删除了之后,代码可以编译了,也就是说使用61行的代码取代60行的代码就OK,虽然解决了一个问题,但是一个更大的问题出现了,不知道有人能解决吗

    2、链接文件,使用命令ld toUppers.o -o toUppers

    3、测试程序,使用命令./toUppers toUppers.s toUppersCase.s

      经验证,  toUppersCase.s中的内容与toUppers.s一样,只是将所有的小写字母变成了大写字符   

  • 相关阅读:
    表单控件和属性
    html5语义化标签
    移动布局
    webpack
    OMobile
    Npm的下载 安装 管理工具
    模块化开发
    百度地图
    离线缓存
    canvas和svg
  • 原文地址:https://www.cnblogs.com/guochaoxxl/p/7068295.html
Copyright © 2020-2023  润新知