8086 Assembly Program to Implement a Simple Calculator

Building a calculator in 8086 assembly language is an excellent way to consolidate knowledge of arithmetic instructions, conditional jumps, procedure calls, and string I/O in a single program. In this post you will study a fully working calculator that accepts two 16-bit operands and an operator (+, -, *, /), performs the chosen operation, and displays the result โ€” all in real-mode 8086 assembly using MASM/TASM-compatible syntax.

Prerequisites

  • Basic familiarity with 8086 registers (AX, BX, CX, DX) and segments (DS, CS).
  • MASM, TASM, or DOSBox with an assembler installed.
  • Understanding of INT 21h DOS interrupts for character I/O.

Key Instructions Used

InstructionDescription
ADD dest, srcdest โ† dest + src
SUB dest, srcdest โ† dest โˆ’ src
MUL srcAX โ† AX ร— src (unsigned 16-bit; high word in DX)
DIV srcAX โ† DX:AX รท src; DX โ† remainder (unsigned)
CMP a, bSets flags based on a โˆ’ b without storing result
JE / JNEJump if equal / not equal (checks ZF)
INT 21h, AH=01hRead one character from keyboard into AL
INT 21h, AH=02hPrint character in DL to screen
INT 21h, AH=09hPrint $-terminated string pointed to by DX

Complete Program

.MODEL SMALL
.STACK 100h

.DATA
    msg_num1  DB 'Enter first number (0-9):  $'
    msg_op    DB 'Enter operator (+,-,*,/):  $'
    msg_num2  DB 'Enter second number (0-9): $'
    msg_result DB 'Result = $'
    msg_err   DB 'Division by zero error!$'
    msg_newln DB 0Dh, 0Ah, '$'     ; CR + LF

    num1      DW 0
    num2      DW 0
    op        DB 0

.CODE
MAIN PROC
    ; ------ Initialise data segment ------
    MOV  AX, @DATA
    MOV  DS, AX

    ; ------ Read first number ------
    LEA  DX, msg_num1
    MOV  AH, 09h
    INT  21h                    ; print prompt

    MOV  AH, 01h
    INT  21h                    ; read char into AL
    SUB  AL, '0'                ; convert ASCII digit to binary
    MOV  AH, 0
    MOV  num1, AX               ; store as word

    LEA  DX, msg_newln
    MOV  AH, 09h
    INT  21h

    ; ------ Read operator ------
    LEA  DX, msg_op
    MOV  AH, 09h
    INT  21h

    MOV  AH, 01h
    INT  21h                    ; operator character in AL
    MOV  op, AL

    LEA  DX, msg_newln
    MOV  AH, 09h
    INT  21h

    ; ------ Read second number ------
    LEA  DX, msg_num2
    MOV  AH, 09h
    INT  21h

    MOV  AH, 01h
    INT  21h
    SUB  AL, '0'
    MOV  AH, 0
    MOV  num2, AX

    LEA  DX, msg_newln
    MOV  AH, 09h
    INT  21h

    ; ------ Load operands ------
    MOV  AX, num1
    MOV  BX, num2

    ; ------ Dispatch on operator ------
    MOV  CL, op

    CMP  CL, '+'
    JE   DO_ADD
    CMP  CL, '-'
    JE   DO_SUB
    CMP  CL, '*'
    JE   DO_MUL
    CMP  CL, '/'
    JE   DO_DIV
    JMP  DONE                   ; unknown operator โ€” exit

DO_ADD:
    ADD  AX, BX                 ; AX = num1 + num2
    JMP  PRINT_RESULT

DO_SUB:
    SUB  AX, BX                 ; AX = num1 - num2
    JMP  PRINT_RESULT

DO_MUL:
    MUL  BX                     ; DX:AX = AX * BX (unsigned)
                                 ; single-digit result fits in AX
    JMP  PRINT_RESULT

DO_DIV:
    CMP  BX, 0
    JE   DIV_ERROR              ; guard against divide-by-zero
    XOR  DX, DX                 ; clear high word of dividend
    DIV  BX                     ; AX = quotient, DX = remainder
    JMP  PRINT_RESULT

DIV_ERROR:
    LEA  DX, msg_err
    MOV  AH, 09h
    INT  21h
    JMP  DONE

PRINT_RESULT:
    ; Print "Result = " label
    LEA  DX, msg_result
    MOV  AH, 09h
    INT  21h

    ; Convert AX (0-81 for single-digit inputs) to decimal digits and print
    CALL PRINT_DECIMAL

DONE:
    LEA  DX, msg_newln
    MOV  AH, 09h
    INT  21h

    MOV  AH, 4Ch                ; terminate program
    INT  21h
MAIN ENDP

; -------------------------------------------------------
; PRINT_DECIMAL: prints the unsigned decimal value in AX
; Handles values 0-99 (sufficient for single-digit operands)
; -------------------------------------------------------
PRINT_DECIMAL PROC
    MOV  BX, 10
    XOR  CX, CX                 ; digit counter

PD_PUSH:
    XOR  DX, DX
    DIV  BX                     ; AX = AX/10, DX = AX%10
    PUSH DX                     ; push remainder (least-significant digit first)
    INC  CX
    CMP  AX, 0
    JNE  PD_PUSH

PD_POP:
    POP  DX
    ADD  DL, '0'                ; convert digit to ASCII
    MOV  AH, 02h
    INT  21h                    ; print character
    LOOP PD_POP

    RET
PRINT_DECIMAL ENDP

END MAIN

How the Code Works

  1. Data segment setup โ€” MOV AX, @DATA / MOV DS, AX points DS at the program’s data segment so that named variables like num1 are addressable.
  2. Reading a digit โ€” INT 21h / AH=01h places a single ASCII character in AL. Subtracting the ASCII code of '0' (48) converts a digit character to its binary value.
  3. Operator dispatch โ€” a chain of CMP / JE pairs checks the operator byte against each supported character and jumps to the corresponding handler label.
  4. MUL โ€” the unsigned MUL BX instruction multiplies AX by BX and stores the 32-bit result across DX:AX. For single-digit inputs the result always fits in AX.
  5. DIV guard โ€” CMP BX, 0 / JE DIV_ERROR intercepts divide-by-zero before the CPU can trigger interrupt 0 (divide error fault).
  6. PRINT_DECIMAL โ€” repeatedly divides AX by 10, pushing remainders (digits) onto the stack, then pops and prints them in reverse order to display most-significant digit first.

Sample Console Session

Enter first number (0-9):  7
Enter operator (+,-,*,/):  *
Enter second number (0-9): 8
Result = 56

Enter first number (0-9):  9
Enter operator (+,-,*,/):  /
Enter second number (0-9): 0
Division by zero error!

Common Mistakes

MistakeSymptomFix
Forgetting XOR DX, DX before DIVWrong quotient or divide faultAlways zero DX before unsigned 16-bit division
Not converting ASCII to binaryArithmetic on wrong values (e.g. 55 instead of 7)Subtract '0' immediately after reading
Using MOV AH, 0 before storing to num1High byte garbage in word variableZero AH so the full 16-bit word is correct
Missing $ string terminatorINT 21h / AH=09h prints garbage beyond the stringEvery string must end with $

See Also

Conclusion

This calculator program ties together the core pillars of 8086 assembly in one cohesive example: segment setup, INT 21h I/O, ASCII conversion, the four arithmetic instructions, conditional dispatch with CMP/JE, divide-by-zero guarding, and a stack-based decimal printing routine. Master these building blocks and you will have the toolkit to solve virtually any real-mode 8086 programming challenge.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.