4
\$\begingroup\$

How does the following program look to print a list of strings? What places can I improve? Are there easier ways to print something like a linebreak after each string rather than hardcoding the \n into the string itself?

# Program, print out a list of strings, one per line
.data

SYS_EXIT   = 60
SYS_WRITE  = 1
SYS_STDOUT = 1

# Empty string means end of strings
strings:    .asciz  "Once\n", "upon\n", "a\n", "time\n", "...\n", ""

.text
.globl _start

get_string_length:
    mov $0, %eax
  .L1_loop:
    movzbl (%edi, %eax), %ecx
    cmp $0, %cl
    je .L1_exit
    inc %eax
    jmp .L1_loop
  .L1_exit:
    ret

_start:

    mov $strings,   %rbx
  print_loop:
    mov %rbx,       %rdi
    call get_string_length # (rdi=file_descriptor, rsi=starting_address, rdx=size)
    cmp $0, %eax
    jz exit
    mov $SYS_STDOUT,%edi
    mov %rbx,       %rsi
    mov %eax,       %edx
    mov $SYS_WRITE, %eax
    syscall
    lea 1(%eax, %ebx,), %ebx
    jmp print_loop

  exit:
    mov $0,        %edi
    mov $SYS_EXIT, %eax
    syscall
\$\endgroup\$
3
\$\begingroup\$

Are there easier ways to print something like a linebreak after each string rather than hardcoding the \n into the string itself?

Embedded newlines are certainly the easiest way to do this, but definitively not the most versatile way. Embedding the newlines kind of pushes you in the direction that the strings will be used for outputting only. You might want to do lots of other stuff with them too.
I advocate keeping the strings pure and adding the linebreaks, or any other prefix or suffix for that matter, later on. Since you already have to count the characters in the string, you can at the same time copy the string to a buffer (every non-trivial program has some general purpose buffer at the ready). Once you read the zero terminator you store the newline in the buffer. Then you're ready to print the buffer contents for which you now know the length.

In below code PrepStringForOutput is a leaf function which means you can do pretty much anything you like in it. You don't have to follow any register conventions per se.

# Program, print out a list of strings, one per line
.data

SYS_EXIT   = 60
SYS_WRITE  = 1
SYS_STDOUT = 1

# Empty string means end of strings
Strings:    .asciz  "Once", "upon", "a", "time", "...", ""
Buffer:     .ascii  "12345"

.text
.globl _start

; IN (%rbx is asciz) OUT (%rsi is buffer, %rdx is length) MOD (%al) 

PrepStringForOutput:
    mov     $Buffer, %rsi        # Destination
    xor     %edx, %edx           # Length
  .L1_loop:
    mov     (%rbx, %rdx), %al    # Character from asciz string
    test    %al, %al
    jz      .L1_exit
    mov     %al, (%rsi, %rdx)    # Store in buffer
    inc     %edx
    jmp     .L1_loop
  .L1_exit:
    mov     $10, (%rsi, %rdx)    # Adding newline
    inc     %edx
    ret

_start:

    mov     $Strings, %rbx
  PrintLoop:
    cmpb    $0, (%rbx)           # End of list ?
    je      Exit
    call    PrepStringForOutput  # -> %rsi is address, %rdx is length
    add     %rdx, %rbx           # Advancing in the list
    mov     $SYS_STDOUT, %edi
    mov     $SYS_WRITE, %eax
    syscall
    jmp     PrintLoop
  Exit:
    xor     %edi, %edi
    mov     $SYS_EXIT, %eax
    syscall

What places can I improve?

You'll easily spot these in above code...
See the nice tabular layout?

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.