Linux 输入字符串并输出为大写
我正在尝试编写一个程序,将小写字符串转换为大写,使用缓冲区存储初始字符串。我遇到的问题是,我的程序将打印出一个无限循环的字符,这些字符必须与我给定的字符串相似 我认为守则中存在的其他问题如下:Linux 输入字符串并输出为大写,linux,assembly,io,x86,nasm,Linux,Assembly,Io,X86,Nasm,我正在尝试编写一个程序,将小写字符串转换为大写,使用缓冲区存储初始字符串。我遇到的问题是,我的程序将打印出一个无限循环的字符,这些字符必须与我给定的字符串相似 我认为守则中存在的其他问题如下: 有些子程序在调用结束时使用ret。我遇到的问题是,找出这些子例程中哪些实际上不需要ret,并且更好地与jmp一起使用。老实说,我对这两者的语义有点困惑。例如,使用ja调用的子例程是否需要在调用结束时进行ret 我还试图打印出用于转换值的循环的每个迭代中发生的迭代次数。不管出于什么原因,我将inc计数器,
- 有些子程序在调用结束时使用
。我遇到的问题是,找出这些子例程中哪些实际上不需要ret
,并且更好地与ret
一起使用。老实说,我对这两者的语义有点困惑。例如,使用jmp
调用的子例程是否需要在调用结束时进行ja
ret
- 我还试图打印出用于转换值的循环的每个迭代中发生的迭代次数。不管出于什么原因,我将
计数器,并决定使用inc
例程打印它,遗憾的是,它没有做任何事情PrintNumIter
bits 32
[section .bss]
buf: resb 1024 ;allocate 1024 bytes of memory to buf
[section .data]
;*************
;* CONSTANTS *
;*************
;ASCII comparison/conversion
LowercaseA: equ 0x61
LowercaseZ: equ 0x7A
SubToUppercase: equ 0x20
;IO specifiers/descriptors
EOF: equ 0x0
sys_read: equ 0x3
sys_write: equ 0x4
stdin: equ 0x0
stdout: equ 0x1
stderr: equ 0x2
;Kernel Commands/Program Directives
_exit: equ 0x1
exit_success: equ 0x0
execute_cmd: equ 0x80
;Memory Usage
buflen: equ 0x400 ;1KB of memory
;*****************
;* NON-CONSTANTS *
;*****************
iteration_count: db 0
query : db "Please enter a string of lowercase characters, and I will output them for you in uppercase ^.^: ", 10
querylen : equ $-query
[section .text]
global _start
;===========================================
; Entry Point
;===========================================
_start:
nop ;keep GDB from complaining
call AskUser
call Read
call SetupBuf
call Scan
call Write
jmp Exit
;===========================================
; IO Instructions
;===========================================
Read:
mov eax, sys_read ;we're going to read in something
mov ebx, stdin ;where we obtain this is from stdin
mov ecx, buf ;read data into buf
mov edx, buflen ;amount of data to read
int execute_cmd ;invoke kernel to do its bidding
ret
Write:
mov eax, sys_write ;we're going to write something
mov ebx, stdout ;where we output this is going to be in stdout
mov ecx, buf ;buf goes into ecx; thus, whatever is in ecx gets written out to
mov edx, buflen ;write the entire buf
int execute_cmd ;invoke kernel to do its bidding
ret
AskUser:
mov eax, sys_write
mov ebx, stdout
mov ecx, query
mov edx, querylen
int execute_cmd
ret
PrintNumIter:
mov eax, sys_write
mov ebx, stdout
push ecx ;save ecx's address
mov ecx, iteration_count ;print the value of iteration_count
mov edx, 4 ;print 4 bytes of data
int execute_cmd
pop ecx ;grab the value back in
ret
;===========================================
; Program Preperation
;===========================================
SetupBuf:
mov ecx, esi ;place the number of bytes read into ecx
mov ebp, buf ;place the address of buf into ebp
dec ebp ;decrement buf by 1 to prevent "off by one" error
ret
;===========================================
; Conversion Routines
;===========================================
ToUpper:
sub dword [ebp + ecx], SubToLowercase ;grab the address of buf and sub its value to create uppercase character
Scan:
call PrintNumIter ;print the current iteration within the loop
cmp dword [ebp + ecx], LowercaseA ;Test input char against lowercase 'a'
jb ToUpper ;If below 'a' in ASCII, then is not lowercase - goto ToLower
cmp dword [ebp + ecx], LowercaseZ ;Test input char against lowercase 'z'
ja ToUpper ;If above 'z' in ASCII, then is not lowercase - goto ToLower
dec ecx ;decrement ecx by one, so we can get the next character
inc byte [iteration_count] ;increment the __value__ in iteration count by 1
jnz Scan ;if ecx != 0, then continue the process
ret
;===========================================
;Next:
; dec ecx ;decrement ecx by one
; jnz Scan ;if ecx != 0 scan
; ret
;===========================================
Exit:
mov eax, _exit
mov ebx, exit_success
int execute_cmd
您的问题直接归因于这样一个事实:在处理完字符串缓冲区后,您从未将nul终止符追加到它的末尾(据我所知,
read
syscall不会读回null)
不幸的是,由于您的控制流很奇怪,这有点难做到,但是更改SetupBuf
应该可以做到(注意,您可能应该检查您没有溢出buf
,但是对于1KB,我怀疑您是否需要担心学习程序):
只需注意
关于另一个似乎困扰代码的问题(您已经注意到了),您的奇怪控制流。因此,简单的指导原则(注意:不是规则,只是指导原则)有望帮助您减少spagetti代码:
(和条件跳转)应仅用于在同一过程中转到标签,否则您将开始绑定,因为您无法向后展开。其他唯一可以使用跳转的时间是用于尾部调用,但在这个阶段,您不必担心这一点,它更容易混淆JMP
- 当您要执行另一个过程时,始终使用
,这允许您使用CALL
/RETN
指令正确返回调用站点,从而使控制流更具逻辑性RET
print_num: ;PROC: num to print in ecx, ecx is caller preserved
push ecx
push num_format ; "%d\n"
call _printf
sub esp,8 ;cleanup for printf
retn
print_loop_count: ;PROC: takes no args
mov ecx,0x10 ;loop 16 times
do_loop: ;LABEL: used as a jump target for the loop
;good idea to prefix jump lables with "." to differentiate them
push ecx ;save ecx
call print_num ;value to print is already in ecx
pop ecx ;restore ecx
dec ecx
jnz do_loop ;again?
retn
您的问题直接归因于这样一个事实:在处理完字符串缓冲区后,您从未将nul终止符追加到它的末尾(据我所知,
read
syscall不会读回null)
不幸的是,由于您的控制流很奇怪,这有点难做到,但是更改SetupBuf
应该可以做到(注意,您可能应该检查您没有溢出buf
,但是对于1KB,我怀疑您是否需要担心学习程序):
只需注意
关于另一个似乎困扰代码的问题(您已经注意到了),您的奇怪控制流。因此,简单的指导原则(注意:不是规则,只是指导原则)有望帮助您减少spagetti代码:
(和条件跳转)应仅用于在同一过程中转到标签,否则您将开始绑定,因为您无法向后展开。其他唯一可以使用跳转的时间是用于尾部调用,但在这个阶段,您不必担心这一点,它更容易混淆JMP
- 当您要执行另一个过程时,始终使用
,这允许您使用CALL
/RETN
指令正确返回调用站点,从而使控制流更具逻辑性RET
print_num: ;PROC: num to print in ecx, ecx is caller preserved
push ecx
push num_format ; "%d\n"
call _printf
sub esp,8 ;cleanup for printf
retn
print_loop_count: ;PROC: takes no args
mov ecx,0x10 ;loop 16 times
do_loop: ;LABEL: used as a jump target for the loop
;good idea to prefix jump lables with "." to differentiate them
push ecx ;save ecx
call print_num ;value to print is already in ecx
pop ecx ;restore ecx
dec ecx
jnz do_loop ;again?
retn
你应该使用调试器一步一步地调试你的程序,以便找出它的行为与你期望的不同之处;我刚刚开始学习x86汇编。一直在检查寄存器和地址等等。你对asm很了解吗?如果你仔细阅读了代码,你可能发现一行(或多行)特定的代码没有达到你的预期/意图?是的,我找到了。问题在于,它并不像剖析一个函数和简单地“看到”发生了什么那样简单。汇编是另一个野兽。我不知道这是否会有帮助,但我个人使用
jmp
转到另一个标签(在您的情况下,读:
,写:
,等等,而不是ret
to\u start:
)。我非常怀疑这是问题所在,但这是我的两分钱你应该使用调试器一步一步地调试你的程序,以便找出它的行为与你期望的不同之处;我刚刚开始学习x86汇编。一直在检查寄存器和地址等等。你对asm很了解吗?如果你仔细阅读了代码,你可能发现一行(或多行)特定的代码没有达到你的预期/意图?是的,我找到了。问题在于,它并不像剖析一个函数和简单地“看到”发生了什么那样简单。汇编是另一个野兽。我不知道这是否会有帮助,但我个人使用jmp
转到另一个标签(在您的情况下,读:
,写:
,等等,而不是ret
to\u start:
)。我非常怀疑这是问题所在,但这是我的两分钱