霸道的代码,不能加注释

  在读《深入理解程序设计使用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一样,只是将所有的小写字母变成了大写字符