1

I have a assembly program which prints out the numbers from 1 - 9 on a single line like this: 123456789. What I want is to print these values on one line each; one number for each line.

I tried implementing new line but in my code that throws error Segmentation fault (core dumped). I'm still very fresh in assembly so I don't really know how to find out why this doesn't work. It only prints the first number (1) and then throws that error.

Code I tried for new line:

mov dl, 13
mov ah, 02h
int 21h
mov dl, 10
mov ah, 02h
int 21h

my code:

_start:
    mov ecx, 10
    mov eax, '1'

L1:
    mov [num], eax
    mov eax, 4
    mov ebx, 1
    push ecx

    mov ecx, num
    mov edx, 1
    int 0x80

    mov eax, [num]
    sub eax, '0'
    inc eax
    add eax, '0'
    pop ecx
    loop L1

    mov eax, 1
    int 0x80

section .bss
    num resb 1
mkUltra
  • 13
  • 6
  • 2
    "I tried implementing new line" - Show us what you did? – David Wohlferd Oct 17 '19 at 08:12
  • @DavidWohlferd i edited the post to make it clear what i tried. – mkUltra Oct 17 '19 at 08:16
  • 1
    Why are you raising a different interrupt in the "code I tried" section than in the "my code" section? Is the "code I tried" for DOS and the "my code" for Linux? – reirab Oct 17 '19 at 08:25
  • Also, why are you only loading the 16-bit regs in "code I tried" rather than the full 32-bit regs in "my code?" – reirab Oct 17 '19 at 08:27
  • Why are you not using the same interrupt in the loop and the print? If the print works with `int 0x80` then your `CR `and `LF` should also print with `int 0x80` – Simson Oct 17 '19 at 08:31
  • @reirab This was something i overlooked, thanks! changing this made the code not throw an error, but I still strugge to find out where to put it. – mkUltra Oct 17 '19 at 08:43
  • 1
    @I am using Linux, sorry for the confusion. – mkUltra Oct 17 '19 at 09:19

1 Answers1

2

You should be using the same system call to print the newline that you're using to print the numbers.

Also, newline in Linux is just LF (char 10,) not CR (char 13) followed by LF like Windows/DOS uses.

How to print to stdout on Linux in x86 assembly

This answer describes what each of the arguments does for the Linux print system call, which is what you're calling by raising int 0x80.

System calls use registers to pass in their arguments. From the linked answer, eax is the system call number (4 = print), ebx is the destination stream (1 = stdout), ecx is a pointer to the data to print, and edx is the length of the data to print.

So, the code that's actually doing the print in your loop is:

mov [num], eax  ; Moves the character in eax to the buffer pointed to by num.
mov eax, 4      ; Moves 4 into eax, i.e. selects the print system call.
mov ebx, 1      ; Moves 1 into ebx, i.e. selects stdout as the destination.
mov ecx, num    ; Moves the address where your text is stored into ecx.
mov edx, 1      ; Moves the number of bytes to characters (1) into edx.
int 0x80        ; Executes a Linux system call using the above parameters.

To print a newline, you'd just need to have the line feed character (character 10 decimal) in eax before this code rather the character for a number. So, for example, adding mov eax, 10 before this code would print a line feed instead of a number.

How to make this work with your existing loop

num in your "my code" section is the buffer where you're storing the data to print. However, this code is also using this memory in order to keep track of the last number it printed. So, there are two ways around losing that information between loops:

Option 1: Simply increase the size of your buffer from 1 byte to 2 and put the line feed in the second byte. You can then just move 2 into edx instead of 1 to tell Linux that you'd like to print 2 characters, thus printing both the number and the line feed on each loop iteration.

Option 2: Allocate another single-byte buffer to store the line feed. Move the line feed character there, then make a second system call after the system call that prints the number in the loop to print the line feed. If your new buffer were called "lfbuffer", for example, then you would add this code after the int 0x80 line in your existing loop:

mov byte [lfbuffer], 10  ; Moves the a line feed to the buffer pointed to by lfbuffer.
mov eax, 4           ; Moves 4 into eax, i.e. selects the print system call.
mov ebx, 1           ; Moves 1 into ebx, i.e. selects stdout as the destination.
mov ecx, lfbuffer    ; Moves the address where your line feed is stored into ecx.
mov edx, 1           ; Moves the number of bytes to characters (1) into edx.
int 0x80             ; Executes a Linux system call using the above parameters.
Community
  • 1
  • 1
reirab
  • 1,535
  • 14
  • 32
  • 1
    `mov [lfbuffer], eax` is a dword store. You suggested using a single-byte buffer so this will step on 3 bytes after that. Also, `mov byte [lfbuffer], 10` works. Or better, statically initializer it instead of wasting instructions. Like `section .rodata` / `lfbuffer: db 10`. But obviously more efficient is just appending a newline to the string you're already passing to the first `write`, like you suggested first. – Peter Cordes Oct 17 '19 at 16:10
  • @PeterCordes Ah, right. That's what I get for doing assembly in the early morning hours. I was just looking at the original code and didn't even think about it already being a 32-bit store. Fixed. In that case, either the original code is already stomping 3 bytes of memory or the `num` buffer already has enough space to store the line feed after the number character. – reirab Oct 17 '19 at 16:23
  • 1
    The original code only has 1 byte of BSS space reserved, but as a static executable there's probably nothing after it so it really has a whole page. (It has no `.data` section which could share a page with BSS) – Peter Cordes Oct 17 '19 at 16:32
  • @PeterCordes Ah, yes, right again. And apparently I missed that entire section when originally reading the question. Another indication that I probably shouldn't deal with assembly in the early morning hours. - haha – reirab Oct 17 '19 at 16:55