assemblyx86outputnasmfactorial

I wrote a program in assembly but there is an unexpected character at the beginning of my output


I started learning assembly today and I wrote a program to print the factorial of a number. It worked great for single-digit factorials but when I made some changes for it to work with larger numbers, I started getting an unexpected character at the beginning of the factorial. The character that the factorial begins with changes for every number.

Here is my code:

section .data
    msg db 'Enter a number: ',0     ;content of user prompt
    lenmsg equ $ - msg              ;length of user prompt
    disp db 'Factorial: ',0         ;content of output
    lendisp equ $ - disp            ;length of output
    newline db 0xa                  ;newline char

section .bss
    num resb 3              ;2 bytes for number , 1 byte for newline
    fac resb 12             ;12 bytes for Factorial

section .text
    global _start

_start:

    ;Display User Prompt
    mov edx,lenmsg              ;number of bytes to write
    mov ecx,msg                 ;content to write
    mov ebx,1                   ;file descriptor : stdout
    mov eax,4                   ;system call : sys_write
    int 0x80                    ;call kernel

    ;Accept User Input
    mov edx,2                   ;number of bytes to read
    mov ecx,num                 ;input stored at num
    mov ebx,0                   ;file descriptor : stdin
    mov eax,3                   ;system call : sys_read
    int 0x80                    ;call kernel
    mov byte [num+eax-1],0      ;replace newline with null
    
    ;Calculate Factorial
    movzx eax, byte [num]       ;move num to eax, replace leading places with zeroes
    sub eax,'0'                 ;convert ascii to integer
    mov ecx,eax                 ;transfer to ecx , acts as counter
    mov eax,1                   ;eax takes value of 1, acts as factorial
    cmp ecx,0                   ;checks if input is 0
    jz output                   ;if input is 0 it goes directly to output
    loop:
        mul ecx                 ;multiply
        dec ecx                 ;decrease counter
        jnz loop                ;if ecx > 0, loop through

output:

    ;Convert integer to ASCII
    mov ebx,fac+11              ;fac is 12 bytes long, fac+11 goes to end of fac
    mov byte [ebx],0            ;add null character
    xor ecx,ecx                 ;clear ecx , acts as counter
    convert:
        xor edx,edx             ;clear edx
        mov ecx,10              ;divisor
        div ecx                 ;eax /= ecx, edx contains remainder
        add dl,'0'              ;last 8 bits of edx contains the digit, convert it into a string by adding ascii number of '0'
        dec ebx                 ;move buffer pointer, we save each digit in a different position
        mov [ebx],dl            ;store character
        inc ecx                 ;ecx works as a general purpose register AND as a counter at the SAME TIME
        test eax,eax            ;works like bitwise AND
        mov [fac],bl            ;store result to fac
        jnz convert             ;if eax != 0, continue looping


    ;Display Output Message
    mov edx,lendisp             ;length of output message
    mov ecx,disp                ;content of output message
    mov ebx,1                   ;file descriptor : stdout
    mov eax,4                   ;system call : sys_write
    int 0x80                    ;call kernel

    ;Display Factorial
    mov edx,ecx                 ;length of factorial
    mov ecx,fac                 ;content of factorial
    mov ebx,1                   ;file descriptor : stdout
    mov eax,4                   ;system call : sys_write
    int 0x80                    ;call kernel

    ;Display Newline
    mov edx,1                   ;length of newline
    mov ecx,newline             ;content of newline
    mov ebx,1                   ;file descriptor : stdout
    mov eax,4                   ;system call : sys_write
    int 0x80                    ;call kernel

    ;Exit Code
    mov eax,1                   ;system call : sys_exit
    int 0x80                    ;call kernel

This is the output that I got:

Enter a number: 2
Factorial: -2

Enter a number: 3
Factorial: -6

Enter a number: 4
Factorial: ,24

Enter a number: 5
Factorial: +120

Enter a number: 6
Factorial: +720

Enter a number: 7
Factorial: *5040

Enter a number: 8
Factorial: )40320

Enter a number: 9
Factorial: (362880

I am using NASM version 2.16.01 on Ubuntu 24.04.1 LTS.

Please help me.


Solution

  • When you're ready to display the factorial, you currently first display a message. This destroys the count that you think you have established in ECX during the conversion loop.
    The conversion loop also has some meaningless content!

    This is what you need:

      ;Display Output Message
      mov  edx, lendisp
      mov  ecx, disp
      mov  ebx, 1
      mov  eax, 4
      int  0x80
    
      ;Convert integer to ASCII
      mov  ecx, fac+12        ; Better build address in ECX, ready for output
      xor  ebx, ebx           ; Count in any temp register
      mov  edi, 10            ; CONST throughout the loop
    convert:
      xor  edx, edx
      div  edi
      add  dl,'0'
      dec  ecx
      mov  [ecx], dl
      inc  ebx
      test eax, eax
      jnz  convert
    
      ;Display Factorial
      mov  edx, ebx
      mov  ebx, 1
      mov  eax, 4
      int  0x80
    

    See that by putting the message before the conversion, you can more freely use the registers.


    Your factorial loop should not be multiplying by 1

    Since both 0! and 1! are 1, separate both at once. Then stop the loop earlier:

      mov  eax, 1
      cmp  ecx, eax
      jbe  output
    loop:
      imul eax, ecx   ; Non-widening is better (you don't use EDX)
      dec  ecx
      cmp  ecx, 1
      jne  loop
    output: