Aaron
Aaron

Reputation: 33

Can't get single character to print in Linux NASM

I try to make a program which takes some input, finds odd positions in the string and prints corresponding character, so you enter 'somewords' and it prints 'oeod'.
I ended up making a loop which iterates through the string, then divides the counter by 2 and prints the character in counter's position if remainder isn't equal 0.

Instead of single character it prints nothing.

Full code:

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

I tried to use bl, al and cl with no luck. I also tried to make some checks. For example, print the counter in .iterstring:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: test
1
2
3
4
5

So it seems like iteration works fine.

The most luck I've got with answer for similar question (How to print a character in Linux x86 NASM?) making such changes to the code:

.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

But it prints everything except for first characters:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
mewords        

I'm stuck and can't get what are my mistakes.

Edit, final code:

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

Now it works as intended:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
oeod

I had to remove more push-pop instructions and also had to move counter into esi because pushing and then popping the registers not always restored their value in the stack, which is weird for me.
When I tried to move into ecx address of byte [eax+ecx] it worked, but when I changed it to byte [eax+1] it would segfault because restoring eax after pop would break. When I pushed ecx to print out a message and then popped it back, it ended up with segfault and gdb showed that there was rubbish code inside ecx after popping it out.
With current code it works fine though.

Upvotes: 3

Views: 504

Answers (1)

Michael
Michael

Reputation: 58447

This line is incorrect:

mov    ecx, ebx  ; move ebx to print

write (int 80h / eax=4) expects ecx to contain the address of the data to write (see this table). But you're passing the data itself.

In your modified code you're placing the character on the stack and then passing its address in ecx, so that's correct. However, you've already incremented ecx by the time you get to .printchar. That's why your code doesn't print the first character.

As a side note, your check for even/odd numbers is unncessarily complicated. It could be simplified to just:

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

Upvotes: 3

Related Questions