model small
.data
x db "enter a no. $"
y db "factorial $"
.code
.startup

mov ah,09h
lea dx,x
int 21h

mov ah,01h
int 21h
sub al,30h
mov bl,0ah
mul bl
mov bl,al
mov ah,01h
int 21h
sub al,30h
add al,bl

mov ah,09h
lea dx,y
int 21h

mov bl,al

l:
dec bl
mul bl
cmp bl,01h
jne l

mov ah,09h
lea dx,y
int 21h
cmp al,64h
jl l2

mov ch,al
mov bh,0ah
div bh
mov dh,ah
div bh
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov dl,ah
add dl,30h
mov ah,02h
int 21h
mov dl,dh
add dl,30h
mov ah,02h
int 21h
jmp e

l2:

mov bh,0ah
div bh
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov dl,ah
add dl,30h
mov ah,02h
int 21h

e:
.exit
end

Dani AI

Generated

Short summary of what’s going wrong and a safe, practical fix.

’s routine mixes DOS calls and arithmetic without preserving the registers or matching operand sizes. That causes the input value and intermediate results to be clobbered by interrupts, and DIV/MUL are used without ensuring the correct high word (DX/AH) is set — both of which lead to wrong results or CPU exceptions. ’s suggestion to preserve registers and to initialize the high byte is on target.

Below is a compact MASM/TASM-style example that:

  • reads a single decimal digit (0–8) using DOS buffered input (int 21h, AH=0Ah),
  • computes factorial in 16 bits (AX), checking DX after MUL for overflow,
  • converts AX to an ASCII string and prints it with int 21h, AH=09h.
    It keeps the math and I/O separation clear so interrupts don’t destroy your working registers.
.model small
.stack 100h
.data
 prompt   db 'Enter number (0-8): $'
 tooBig   db 'Value too large (max 8).$'
 labelRes db 'Factorial = $'
 inbuf    db 1,0,0        ; DOS 0Ah buffer: max=1, len, char
 outbuf   db 6 dup(0)     ; up to 5 digits + terminator

.code
start:
 mov ax,@data
 mov ds,ax

 mov dx,OFFSET prompt
 mov ah,09h
 int 21h

 lea dx,inbuf
 mov ah,0Ah
 int 21h

 mov al,[inbuf+2]
 sub al,'0'
 mov cl,al
 cmp cl,8
 ja TooBig

 mov ax,1
 cmp cl,1
 jbe PrintResult
 mov bx,2
MulLoop:
 mul bx           ; DX:AX = AX * BX
 or dx,dx
 jnz TooBig       ; overflow if DX != 0
 inc bx
 cmp bx,cx
 jbe MulLoop

PrintResult:
 mov dx,OFFSET labelRes
 mov ah,09h
 int 21h

 lea si,outbuf+5
 mov cx,0
 mov bx,10
ConvLoop:
 xor dx,dx
 div bx           ; DX:AX / 10 -> AX=quotient, DX=remainder
 add dl,'0'
 mov [si],dl
 dec si
 inc cx
 test ax,ax
 jnz ConvLoop

 inc si
 mov di,si
 add di,cx
 mov byte ptr [di],'$'
 mov dx,si
 mov ah,09h
 int 21h
 jmp Exit

TooBig:
 mov dx,OFFSET tooBig
 mov ah,09h
 int 21h

Exit:
 mov ah,4Ch
 int 21h
end start

Troubleshooting notes: test in DOSBox or a real DOS environment; assemble with TASM/MASM and link. Watch for overflow — 8! fits in 16 bits (40320); 9! does not. If larger inputs are required, implement multiword (32/64-bit) math or a big-integer multiply routine. Also remember: DOS interrupts can clobber AH/AL and other registers, so preserve any registers you need across calls (push/pop or avoid relying on register contents after int 21h).

Hello, as to your question:

add al,bl  ; AL now contains value user entered in DECimal
mov ah,09h
lea dx,y
int 21h
; but AL is destroyed after here
; after this MS-DOS call AL is overwritten with dollar-sign char of
; printed string, DOS uses fast putchar to write to console
mov bl,al

; if AL was not destroyed the following routine you entered would 
; calculate correctly the factorial of the number
l: 
dec bl
mul bl
cmp bl,01h
jne l

mov ah,09h
lea dx,y
int 21h
cmp al,64h
jl l2

; DIV instruction divides 8-bit divisor by 16-bit divisor in AX
; by here in the code AH is uninitialized, and contains 24h, 24xx / 10 ?
; hence you need to initialize AH before the division
; *multiplier is explicit operand of DIVide

; control never reaches here you only allowed user to enter a two
; digit decimal value between 00-99 which is less than 64h

mov ch,al 
mov bh,0ah 
div bh    
mov dh,ah 
div bh    
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov dl,ah ; ? AH still contains number of function call
add dl,30h
mov ah,02h
int 21h
mov dl,dh
add dl,30h
mov ah,02h
int 21h
jmp e

l2: ; this code looks fine, control should have reached here, except
    ; the accumulator is not preserved before MS-DOS call

mov bh,0ah
div bh
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov dl,ah ; ? AH still contains number of function call 
add dl,30h
mov ah,02h
int 21h

Add this, made it work in my assembler...
When I assembled this I made it into a .COM

mov ah,01h
int 21h
sub al,30h
mov bl,0ah
mul bl
mov bl,al
mov ah,01h
int 21h
sub al,30h
add al,bl

push ax ; here
mov ah,09h
lea dx,  y
int 21h
pop ax ; here

mov bl,al

l:
dec bl
mul bl
cmp bl,01h
jne l

push ax ; here
mov ah,09h
lea dx,  y
int 21h
pop ax ; here
xor ah, ah ; here
cmp al,64h
jl l2

mov ch,al
mov bh,0ah
div bh     
mov dh,ah
div bh
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov dl,ah
add dl,30h
mov ah,02h
int 21h
mov dl,dh
add dl,30h
mov ah,02h
int 21h
jmp e

l2:

mov bh,0ah
div bh
mov dl,al
add dl,30h
push ax ; here
mov ah,02h
int 21h
pop ax ; here
mov dl,ah
add dl,30h
mov ah,02h
int 21h

Have a good day...

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.