Assembly Can';无法在Linux NASM中打印单个字符

Assembly Can';无法在Linux NASM中打印单个字符,assembly,x86,nasm,Assembly,X86,Nasm,我尝试制作一个程序,它接受一些输入,在字符串中找到奇数位置并打印相应的字符,因此您输入'somewords'并打印'oeod'。 最后我做了一个循环,循环遍历字符串,然后将计数器除以2,如果余数不等于0,则在计数器的位置打印字符 它不打印单个字符,而不打印任何内容 完整代码: SECTION .bss inp: resb 255 SECTION .data msg db "Enter the string: ", 0h SECTION .text global _start _start:

我尝试制作一个程序,它接受一些输入,在字符串中找到奇数位置并打印相应的字符,因此您输入'somewords'并打印'oeod'。
最后我做了一个循环,循环遍历字符串,然后将计数器除以2,如果余数不等于0,则在计数器的位置打印字符

它不打印单个字符,而不打印任何内容

完整代码:

SECTION .bss
inp: resb 255

SECTION .data
msg db "Enter the string: ", 0h

SECTION .text
global _start

_start:
    mov    eax, msg
    call   stprint 

    mov    edx, 255  ; take user input 
    mov    ecx, inp 
    mov    ebx, 0 
    mov    eax, 3 
    int    80h 

    call   findodd

    mov    ebx, 0
    mov    eax, 1
    int    80h

findodd:
    push   eax
    push   ecx
    push   edx
    push   esi
    push   ebx

    mov    ecx, 0     ; counter
    mov    esi, 2     ; divider

.iterstring:  
    mov    eax, inp           ; move input to eax
    cmp    byte [eax+ecx], 0  ; check for end of the string in position
    je     .finish            ; if equal, finish
    inc    ecx  

    push   eax
    mov    eax, ecx   ; move counter to eax 
    xor    edx, edx   ; divide it by 2
    idiv   esi  
    pop    eax
    cmp    edx, 0     ; check the remainder
    jnz    .printchar ; print character if != 0
    jmp    .iterstring

.printchar:  
    push   eax
    push   ebx
    movzx  ebx, byte [eax+ecx] ; move single byte to ebx

    push   ecx
    mov    ecx, ebx  ; move ebx to print
    mov    edx, 1    ; print the character
    mov    ebx, 1
    mov    eax, 4
    int    80h

    pop    ecx
    pop    eax
    pop    ebx
    jmp    .iterstring  

.finish:  
    pop    eax  
    pop    ecx   
    pop    edx
    pop    esi
    pop    ebx
    ret  

; print string function (taken from tutorial)
; if I try to print single character with it I get SEGFAULT
stprint:
    push    edx
    push    ecx
    push    ebx
    push    eax
    call    stlen

    mov     edx, eax
    pop     eax

    mov     ecx, eax
    mov     ebx, 1
    mov     eax, 4
    int     80h

    pop     ebx
    pop     ecx
    pop     edx
    ret

stlen:
    push    ebx
    mov     ebx, eax

nextch:
    cmp     byte [eax], 0
    jz      finish
    inc     eax
    jmp     nextch

finish:
    sub     eax, ebx
    pop     ebx
    ret

我尝试使用bl、al和cl,但运气不佳。我还试着做了一些检查。例如,在.iterstring中打印计数器:

nasm-f elf lr3.asm&&ld-m elf_i386 lr3.o-o lr3&&l3
输入字符串:test
1.
2.
3.
4.
5.
所以看起来迭代工作得很好

我最幸运的是,我回答了类似的问题(),对代码做了这样的更改:

.printchar:
  push   eax
  push   ebx
  push   esi
  mov    eax, inp
  movzx  ebx, byte [eax+ecx]
  mov    esi, ecx ; see below

  push   ecx
  push   ebx
  mov    ecx, esp
  mov    edx, 1    ; print the character
  mov    ebx, 1
  mov    eax, 4
  int    80h

  pop    ecx
  pop    ebx
  pop    eax
  pop    ebx
  mov    ecx, esi  ; without this it just prints 1 character and ends
  pop    esi       ; so ecx is not restored with pop for some reason?
  jmp    .iterstring
但它会打印除第一个字符以外的所有字符:

nasm-f elf lr3.asm&&ld-m elf_i386 lr3.o-o lr3&&l3
输入字符串:somewords
默兹
我陷入困境,无法理解我的错误是什么

编辑,最终代码:

findodd:
    push   eax
    push   ecx
    push   edx
    push   esi
    push   ebx
    mov    esi, 0     ; counter
    mov    eax, inp

.iterstring:
    inc    esi
    cmp    byte [eax+esi], 0
    jz     .finish
    test   esi,1
    jz     .iterstring

    movzx   ecx, byte [eax+esi]
    push    ecx
    mov     ecx, esp
    mov     edx, 1
    mov     ebx, 1
    push    eax
    mov     eax, 4
    int     80h
    pop     eax
    pop     ecx
    jmp     .iterstring

.finish:
    pop    eax
    pop    ecx
    pop    edx
    pop    esi
    pop    ebx
    ret
现在它按预期工作:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
oeod
我不得不删除更多的push-pop指令,还必须将计数器移到esi中,因为按下然后弹出寄存器并不总是恢复它们在堆栈中的值,这对我来说很奇怪。
当我尝试移动到
字节[eax+ecx]
的ecx地址时,它工作了,但当我将其更改为
字节[eax+1]
时,它会发生故障,因为在pop后恢复eax会中断。当我按下ecx打印一条消息,然后弹出它回来时,结果是SEGFULT,而gdb在弹出消息后显示ecx内有垃圾代码。

对于当前代码,它可以正常工作。

这行代码不正确:

mov    ecx, ebx  ; move ebx to print
write
int 80h/eax=4
)希望
ecx
包含要写入的数据的地址(请参阅)。但是您正在传递数据本身

在修改后的代码中,您将字符放在堆栈上,然后在
ecx
中传递其地址,因此这是正确的。 但是,当您到达
.printchar
时,您已经增加了
ecx
。这就是代码不打印第一个字符的原因

作为补充说明,您对偶数/奇数的检查是不必要的复杂。可以简化为:

test ecx,1      ; set EFLAGS based on ecx AND 1
jnz .printchar

您使用的是64位内核吗?@Joshua如果它是在没有
配置IA32\u仿真的情况下构建的,例如在WSL(Linux的Windows子系统)上,那么这将是一个问题。这些nasm+ld命令将创建以32位模式运行的32位可执行文件。因此,没有IA32支持的内核实际上根本不会运行它。64位内核不能解释这些症状,因为OP提供了一个很好的,而不仅仅是“不工作”:)如果在底部使用
jz.iterstring
,而不是在相反的条件下跳过
jmp
,那么
iterstring
循环将更加有效。与惯用的
do{}while()
循环风格相比,这是不必要的过度复杂。另外,使用
div
除以2的幂也很可怕:使用AND获得余数,即低位。或者更好的方法是,使用
test al,1
测试奇数/偶数,直接检查EAX的低位。或者更简单地说,将循环展开2,而不是在两种行为之间切换分支,您只需按顺序执行。首先只需检查0终止符(偶数字节),然后检查并打印奇数字节。然后循环重复。您不需要将数据复制到任何地方,只要让ECX指向数组即可。(虽然将奇数字符复制到tmp数组并对整个输出进行一次
写入
系统调用会更有效。但使用SSE2
psrlw xmm0,8
/
packuswb
可以更有效地将奇数字节从2个16字节向量打包成一个。谢谢你,@peter cordes。我只知道基本知识,但会把sse2挖下来,谢谢你指出。