3

I am new to assembly programming. I'm trying to make a simple program in x64 assembly to print an inverted triangle to console. I expect my program to output something like this:

********
 *******
  ******
   *****
    ****
     ***
      **
       *

This is my effort so far:

;--------------------------------------
; compile and run:
; $ nasm -f elf64 invtriangle.asm
; $ ld -s -o invtriangle invtriangle.o
; $ ./invtriangle
;--------------------------------------

                bits 64
                global _start

                section .text
_start:
                mov     r8, 0          ; copy maximum number of whitespace characters allowed for first line to register 'r8'
                mov     r9, 8          ; copy maximum number of stars allowed for first line to register 'r9'
                mov     r10, 0         ; number of whitespace characters written on line so far
                mov     r11, 0         ; number of star characters written on line so far
                mov     rbx, output    ; copy address of the string pointer to register 'rbx'
writeline:
writewhitespace:
                cmp     r8, 0          ; check if the value of register 'r8' is zero
                je      writestars     ; if so, skip writing whitespaces
                mov     byte[rbx], ' ' ; write a whitespace character
                inc     rbx            ; advance pointer to next address to write
                inc     r10            ; increment number of whitespace characters written on line so far by 1
                cmp     r10, r8        ; compare register values: check if we reached the maximum number of whitespace characters allowed for the current line
                jne     writewhitespace; if not, continue writing whitespaces
writestars:
                mov     byte[rbx], '*' ; write s star
                inc     rbx            ; advance pointer to next address to write
                inc     r11            ; increment number of star characters written on line so far by 1
                cmp     r11, r9        ; compare register values: check if we reached the maximum number of star characters allowed for the current line
                jne     writestars     ; if not, continue writing stars
lineend:
                mov     byte[rbx], 10  ; write a new line character (ascii value for new-line is 10)
                inc     rbx            ; advance pointer to next address to write
                inc     r8             ; the next line will be one whitespace longer
                dec     r9             ; the next line will be one star shorter
                mov     r10, 0         ; reset the counter (number of whitespace characters written on line)
                mov     r11, 0         ; reset the counter (number of star characters written on line)
                cmp     r8, maxlines   ; did we exceed the maximum number of lines?
                jng     writeline      ; if not, continue writing lines
                mov     rax, 1         ; system call for write (64 bit)
                mov     rdi, 1         ; file descriptor = stdout
                mov     rsi, output    ; copy the address of string 'output' to output
                mov     rdx, nbytes    ; copy number of bytes in output
                syscall                ; invoke OS to write
                mov     rax, 60        ; system call for exit (64 bit)
                mov     rdx, 0         ; exit status = 0
                syscall                ; invoke OS to exit

                section .bss
maxlines        equ     8              ; maximum number of lines to write
nbytes          equ     72             ; (8 + 8 + 8 + 8 + 8 + 8 + 8 + 8) + 8 = 72 bytes (don't forgot to take account of new-line characters!)
output          resb    nbytes         ; initialize a string pointer by reserving number of bytes

When I run the assembly program above I kept getting a segmentation fault and I can't figure out the cause of it. I tried to change registers and I got the same result. Interestingly when I increase the maximum number of stars allowed for first line from 8 to 10 in register r9, the program ran without segmentation fault but the output wasn't what I expected (I don't know why it happened). I'm not sure if I calculated the variable nbytes correctly in bss section. I will appreciate any form of assistance, thanks!

I am using 64-bit Ubuntu OS to run the program above.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Real Donald Trump
  • 412
  • 1
  • 5
  • 15
  • 3
    One other bug is that system calls take their first arg in RDI, not RDX. Your exit system call is using the wrong register. Use `strace ./a.out` to see the system calls you make. **Which instruction faults?** A debugger like GDB will show you; look at register values to see what address you tried to load or store. Just changing your source code without using a debugger to find the specific problem first is a waste of your time. – Peter Cordes Apr 20 '20 at 22:13
  • I just used strace and this is what I got ` SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x601000}` I can't find which line it occurred. Also how can I generate debugging info for gdb? i used -g option in nasm and it doesn't produce much debugging info. – Real Donald Trump Apr 20 '20 at 22:38
  • 1
    @JimmyYang: You don't need debugging info for an assembly language program. Debug info in C tells the debugger about the types of objects, the locations of local variables, etc. Assembly language doesn't have those things. Just look at the instructions you're executing and compare with the source. Useful commands are `display/i $pc`, `info registers`, `si`, `ni`. – Nate Eldredge Apr 20 '20 at 22:39
  • 1
    @JimmyYang: `strace` only traces system calls. So that tells you you faulted before the first `syscall`. Use a debugger to see what's actually going on, and single-step until you get there. e.g. use `layout reg` disassembly view in GDB, or there are various front-ends. The bottom of https://stackoverflow.com/tags/x86/info has some tips on using GDB. – Peter Cordes Apr 20 '20 at 22:46
  • @PeterCordes you said that system calls take their first arg in rdi not rdx. Does that mean if I want to call exit system call I put exit status in register `rdi` instead of `rdx`? – Real Donald Trump Apr 20 '20 at 22:53
  • Yes, that's what I said. You could have googled yourself to confirm that. [What are the calling conventions for UNIX & Linux system calls on i386 and x86-64](https://stackoverflow.com/q/2535989) – Peter Cordes Apr 20 '20 at 23:01

1 Answers1

1

Hint: What happens to your writestars loop at the last line, when zero stars are to be printed (so that r9 is 0)?

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82