Assembly : Coding Faktorial
December 20, 2019 No Comments Ari Usman,Artikel Komputer,Assembly Ari Usman

; this example gets the number from the user,
; and calculates factorial for it.
; supported input from 0 to 8 inclusive!

name “fact”

; this macro prints a char in AL and advances
; the current cursor position:
putc macro char
push ax
mov al, char
mov ah, 0eh
int 10h
pop ax
endm

org 100h

jmp start

result dw ?

start:

; get first number:

mov dx, offset msg1
mov ah, 9
int 21h
jmp n1
msg1 db 0Dh,0Ah, ‘enter the number: $’
n1:

call scan_num

; factorial of 0 = 1:
mov ax, 1
cmp cx, 0
je print_result

; move the number to bx:
; cx will be a counter:

mov bx, cx

mov ax, 1
mov bx, 1

calc_it:
mul bx
cmp dx, 0
jne overflow
inc bx
loop calc_it

mov result, ax

print_result:

; print result in ax:
mov dx, offset msg2
mov ah, 9
int 21h
jmp n2
msg2 db 0Dh,0Ah, ‘factorial: $’
n2:

mov ax, result
call print_num_uns
jmp exit

overflow:
mov dx, offset msg3
mov ah, 9
int 21h
jmp n3
msg3 db 0Dh,0Ah, ‘the result is too big!’, 0Dh,0Ah, ‘use values from 0 to 8.$’
n3:
jmp start

exit:

; wait for any key press:
mov ah, 0
int 16h

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; these functions are copied from emu8086.inc ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; gets the multi-digit SIGNED number from the keyboard,
; and stores the result in CX register:
SCAN_NUM PROC NEAR
PUSH DX
PUSH AX
PUSH SI

    MOV     CX, 0

    ; reset flag:
    MOV     CS:make_minus, 0

next_digit:

    ; get char from keyboard
    ; into AL:
    MOV     AH, 00h
    INT     16h
    ; and print it:
    MOV     AH, 0Eh
    INT     10h

    ; check for MINUS:
    CMP     AL, '-'
    JE      set_minus

    ; check for ENTER key:
    CMP     AL, 0Dh  ; carriage return?
    JNE     not_cr
    JMP     stop_input

not_cr:

    CMP     AL, 8                   ; 'BACKSPACE' pressed?
    JNE     backspace_checked
    MOV     DX, 0                   ; remove last digit by
    MOV     AX, CX                  ; division:
    DIV     CS:ten                  ; AX = DX:AX / 10 (DX-rem).
    MOV     CX, AX
    PUTC    ' '                     ; clear position.
    PUTC    8                       ; backspace again.
    JMP     next_digit

backspace_checked:

    ; allow only digits:
    CMP     AL, '0'
    JAE     ok_AE_0
    JMP     remove_not_digit

ok_AE_0:
CMP AL, ‘9’
JBE ok_digit
remove_not_digit:
PUTC 8 ; backspace.
PUTC ‘ ‘ ; clear last entered not digit.
PUTC 8 ; backspace again.
JMP next_digit ; wait for next input.
ok_digit:

    ; multiply CX by 10 (first time the result is zero)
    PUSH    AX
    MOV     AX, CX
    MUL     CS:ten                  ; DX:AX = AX*10
    MOV     CX, AX
    POP     AX

    ; check if the number is too big
    ; (result should be 16 bits)
    CMP     DX, 0
    JNE     too_big

    ; convert from ASCII code:
    SUB     AL, 30h

    ; add AL to CX:
    MOV     AH, 0
    MOV     DX, CX      ; backup, in case the result will be too big.
    ADD     CX, AX
    JC      too_big2    ; jump if the number is too big.

    JMP     next_digit

set_minus:
MOV CS:make_minus, 1
JMP next_digit

too_big2:
MOV CX, DX ; restore the backuped value before add.
MOV DX, 0 ; DX was zero before backup!
too_big:
MOV AX, CX
DIV CS:ten ; reverse last DX:AX = AX*10, make AX = DX:AX / 10
MOV CX, AX
PUTC 8 ; backspace.
PUTC ‘ ‘ ; clear last entered digit.
PUTC 8 ; backspace again.
JMP next_digit ; wait for Enter/Backspace.

stop_input:
; check flag:
CMP CS:make_minus, 0
JE not_minus
NEG CX
not_minus:

    POP     SI
    POP     AX
    POP     DX
    RET

make_minus DB ? ; used as a flag.
SCAN_NUM ENDP

; this procedure prints number in AX,
; used with PRINT_NUM_UNS to print signed numbers:
PRINT_NUM PROC NEAR
PUSH DX
PUSH AX

    CMP     AX, 0
    JNZ     not_zero

    PUTC    '0'
    JMP     printed

not_zero:
; the check SIGN of AX,
; make absolute if it’s negative:
CMP AX, 0
JNS positive
NEG AX

    PUTC    '-'

positive:
CALL PRINT_NUM_UNS
printed:
POP AX
POP DX
RET
PRINT_NUM ENDP

; this procedure prints out an unsigned
; number in AX (not just a single digit)
; allowed values are from 0 to 65535 (FFFF)
PRINT_NUM_UNS PROC NEAR
PUSH AX
PUSH BX
PUSH CX
PUSH DX

    ; flag to prevent printing zeros before number:
    MOV     CX, 1

    ; (result of "/ 10000" is always less or equal to 9).
    MOV     BX, 10000       ; 2710h - divider.

    ; AX is zero?
    CMP     AX, 0
    JZ      print_zero

begin_print:

    ; check divider (if zero go to end_print):
    CMP     BX,0
    JZ      end_print

    ; avoid printing zeros before number:
    CMP     CX, 0
    JE      calc
    ; if AX<BX then result of DIV will be zero:
    CMP     AX, BX
    JB      skip

calc:
MOV CX, 0 ; set flag.

    MOV     DX, 0
    DIV     BX      ; AX = DX:AX / BX   (DX=remainder).

    ; print last digit
    ; AH is always ZERO, so it's ignored
    ADD     AL, 30h    ; convert to ASCII code.
    PUTC    AL


    MOV     AX, DX  ; get remainder from last div.

skip:
; calculate BX=BX/10
PUSH AX
MOV DX, 0
MOV AX, BX
DIV CS:ten ; AX = DX:AX / 10 (DX=remainder).
MOV BX, AX
POP AX

    JMP     begin_print

print_zero:
PUTC ‘0’

end_print:

    POP     DX
    POP     CX
    POP     BX
    POP     AX
    RET

PRINT_NUM_UNS ENDP

ten DW 10 ; used as multiplier/divider by SCAN_NUM & PRINT_NUM_UNS.

Visited 4 times, 1 visit(s) today
Tags
About The Author
Ari Usman Seorang Praktisi IT di Bidang Digital Marketing dan Dosen di Bidang Teknik Informatika

Leave a reply

Your email address will not be published. Required fields are marked *