Search code examples
algorithmassemblyx86-16factorial

How does the algorithm of this code work?


I found this code on an old website (have no access to owner), It computes the factorial of a user entered number (up to 255) and it works fine. My problem is that I can figure out how the algorithm works, I do not understand it. I would appreciate it if someone would tell me how it works in human language.

data segment
 b1 db 512 dup(0)
 b2 db 512 dup(0)
 msg db 0dh,0ah,"Number:$"
 fac db 0dh,0ah,"Factorial",0dh,0ah,"$"
data ends

bignum_get macro var
 local exit,get,loo,skip
 xor cx,cx
 lea si,var
 mov di,si
 mov ah,01h
get:
 int 21h
 cmp al,0dh
 je exit
 sub al,30h
 mov [si],al
 inc si
 inc cx
 jmp get
exit:
 shr cx,1
 jz skip
 dec si
loo:
 mov ah,[si]
 mov al,[di]
 mov [si],al
 mov [di],ah
 dec si
 inc di
 loop loo
skip:
endm

geti macro
 local exit,get
 xor bx,bx
 mov dx,bx
 mov cx,00ah
get:
 push bx
 ;Read character
 mov ah,01h
 int 21h
 xor ah,ah
 pop bx
 ;If enter stop
 cmp al,0dh
 jz exit
 sub al,30h
 ;Multply By 10
 push ax
 mov ax,bx
 mul cx
 mov bx,ax
 pop ax
 ;add
 add bx,ax
 ;redo
 jmp get
exit:
 mov ax,bx
endm

bignum_put macro var
 local put,en,nxt,exit
 lea si,var
 mov di,si
 mov cx,0200h
 add di,cx
 dec di
en:
 cmp BYTE PTR [di],00h
 jne nxt
 dec di
 jmp en
nxt:
 mov ah,02h
put:
 mov dl,[di]
 add dl,30h
 int 21h
 cmp si,di
 je exit
 dec di
 loop put
exit:
endm



bignum_add macro b1,b2
 local ader,exit
 lea si,b1
 lea di,b2

 mov cx,0200h
 mov bl,10
ader:
 mov al,[di]
 mov ah,[si]
 add al,ah
 jz exit
 xor ah,ah
 div bl
 mov [si],ah
 inc si
 add [si],al
 inc di
 loop ader
exit:
endm

bignum_mul macro b1
 local loo,exit
 cmp dl,01h
 jz exit
 lea si,b1
 mov dh,10
 xor bx,bx
 mov cx,0200h
loo:
 mov al,[si]
 xor ah,ah
 mul dl
 add ax,bx
 div dh
 mov [si],ah
 inc si
 mov bl,al
 loop loo
exit:
endm

puts macro msg
 push ax
 push dx
 mov dx,offset msg
 mov ah,09h
 int 21h
 pop dx
 pop ax
endm


assume cs:code,ds:data
code segment
start:
 mov ax,data
 mov ds,ax

 lea si,b1
 mov BYTE PTR [si],01h

 puts msg
 geti
 mov dl,al
loo:
 push dx
 bignum_mul b1
 pop dx
 dec dl
 jnz loo

 puts fac
 bignum_put b1
 mov ah,4ch
 int 21h
code ends
end start

Solution

  • Which part is unclear? First it reads an integer using geti which utilizes the well known x = 10*x + c - '0' formula to convert string to number. Then it initializes the bignum accumulator to 1 and multiplies it by the input number using bignum_mul in a loop, counting down to 1, calculating 1*x*(x-1)*(x-2)*...*1. bignum_mul itself multiplies each digit by the given integer, then splits it into two through dividing by 10 so it gets the correct digit as the remainder and the carry as the quotient. The latter is moved into bx and added in the next iteration.

    bignum_add is not used here, it would add two bignums using similar logic to the multiplication. The rest are just helpers doing obvious things.