【Linux x86汇编踩坑】文件读写(二)读取文件转换大小写
前言
转换大小写的思路很简单,来尝试用汇编写一个转换大小写的程序吧
转换大小写
由于在汇编中字符也是用ascii码存储的,那么思路就很清晰了,比如a对应的ascii码是97,而A对应的ascii码是65,那么将a转换为A只需要让a +(-32)即可,这个-32是他们之间的差,由于字母之间的ascii码是连续的,那么从a-z的小写字母我们都可以让它们加上-32来转换为大写字母。
知道了实现原理之后我们来实现一下这个函数。
#转换大小写
.type upperConversion, @function
#确定需要转换的字符的上下界限
.equ UP_CRITICAL, 'z'
.equ LOWER_CRITICAL, 'a'
#转换需要相加的数
.equ UPPER_CONVERSION, 'A' - 'a'
upperConversion:
pushl %ebp
movl %esp, %ebp
movl $BUFFER_SIZE, %ebx
#如果给定的缓冲区的长度为0
cmpl $0, %ebx
je conversion_exit
#索引
movl $0, %edi
#读取字节
read_byte:
movb BUFFER_DATA(,%edi,1), %cl
cmpb $UP_CRITICAL, %cl
jg next_byte
cmpb $LOWER_CRITICAL, %cl
jl next_byte
#转换大小写
addb $UPPER_CONVERSION, %cl
movb %cl, BUFFER_DATA(,%edi,1)
next_byte:
incl %edi
cmpl %edi, %ebx
jne read_byte
conversion_exit:
popl %ebp
ret
需要注意的是,BUFFER_DATA和BUFFER_SIZE分别对应我申请的缓冲区大小和缓冲区长度,具体代码在【Linux x86汇编踩坑】文件读写(一)读取文件并输出中,下面我也会附上完整的代码。
整体来看,上述代码用了一个循环read_byte来循环读取缓冲区里的字节,判断是否在a-z的范围内:
cmpb $UP_CRITICAL, %cl
jg next_byte
cmpb $LOWER_CRITICAL, %cl
jl next_byte
如果在范围内则进行转换大小写的操作,也就是给小写字母加上-32,然后将转换后的字符覆盖到原来缓冲区的位置
addb $UPPER_CONVERSION, %cl
movb %cl, BUFFER_DATA(,%edi,1)
如果在a-z的范围内,也就是不属于小写字母了,那么我们可以跳过,直接读取下一个字符
next_byte:
incl %edi
cmpl %edi, %ebx
jne read_byte
conversion_exit标签用于结束整个函数,把ebp弹出栈,通过ret把把函数返回地址弹出,随后eip寄存器指向函数的返回地址。
conversion_exit:
popl %ebp
ret
整个过程还是比较简单的,下面我附上完整的代码,整个程序的功能主要是读取一个文件的所有字符,并将小写字符转换为大写字符后进入标准输出流输出
toupper.s
.section .data
#系统调用号
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6
#文件打开选项
.equ FILE_READONLY, 0
.equ FILE_WRITE, 03101
#系统调用中断
.equ SYS_INTERRUP, 0x80
#标准文件描述符
.equ STD_IN, 0
.equ STD_OUT, 1
.equ STD_ERR, 2
#文件结束符
.equ END_OF_FILE, 0
.section .bss
#申请缓冲区20byte
.equ BUFFER_SIZE, 50
.lcomm BUFFER_DATA, BUFFER_SIZE
.section .text
.globl _start
_start:
#文件名入栈
movl %esp, %ebp
pushl 8(%ebp)
# 打开文件
call openFile
addl $4, %esp
# 文件描述符保存在%eax
pushl %eax
# 读取文件
call readFile
addl $4, %esp
#转换大小写
call upperConversion
call writeFile
movl %eax, %ebx
movl $SYS_EXIT, %eax
int $SYS_INTERRUP
#系统调用打开文件
.type openFile, @function
openFile:
pushl %ebp
movl %esp, %ebp
#系统调用号
movl $SYS_OPEN, %eax
#文件名
movl 8(%ebp), %ebx
#读写意图
movl $FILE_READONLY, %ecx
#系统权限
movl $0666, %edx
#系统中断
int $SYS_INTERRUP
popl %ebp
ret
#读取文件
.type readFile, @function
readFile:
pushl %ebp
movl %esp, %ebp
read_loop:
#文件描述符
movl 8(%ebp), %ebx
#系统调用号
movl $SYS_READ, %eax
#缓冲区
movl $BUFFER_DATA, %ecx
#缓冲区大小
movl $BUFFER_SIZE, %edx
#系统调用
int $SYS_INTERRUP
cmpl $END_OF_FILE, %eax
jle loop_exit
jmp read_loop
#读取完毕
loop_exit:
#关闭文件
movl $SYS_CLOSE, %eax
movl 8(%ebp), %ebx
int $SYS_INTERRUP
popl %ebp
ret
#写入文件
.type writeFile, @function
writeFile:
pushl %ebp
movl %esp, %ebp
#文件描述符
movl $1, %ebx
#系统调用号
movl $SYS_WRITE, %eax
#缓冲区
movl $BUFFER_DATA, %ecx
#缓冲区大小
movl $BUFFER_SIZE, %edx
#系统调用
int $SYS_INTERRUP
popl %ebp
ret
#转换大小写
.type upperConversion, @function
#确定需要转换的字符的上下界限
.equ UP_CRITICAL, 'z'
.equ LOWER_CRITICAL, 'a'
#转换需要相加的数
.equ UPPER_CONVERSION, 'A' - 'a'
upperConversion:
pushl %ebp
movl %esp, %ebp
movl $BUFFER_SIZE, %ebx
#如果给定的缓冲区的长度为0
cmpl $0, %ebx
je conversion_exit
#索引
movl $0, %edi
#读取字节
read_byte:
movb BUFFER_DATA(,%edi,1), %cl
cmpb $UP_CRITICAL, %cl
jg next_byte
cmpb $LOWER_CRITICAL, %cl
jl next_byte
#转换大小写
addb $UPPER_CONVERSION, %cl
movb %cl, BUFFER_DATA(,%edi,1)
next_byte:
incl %edi
cmpl %edi, %ebx
jne read_byte
conversion_exit:
popl %ebp
ret
在运行整个程序之前,先来编辑一下需要转换大小写的文件data,在data里写上这么一句,很简单的字符串对吧
hello world, MAN
尝试一下
as toupper.s -o toupper.o
ld toupper.o -o toupper
./toupper data
HELLO WORLD, MAN
successful!!!