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
| Instruction | Description |
|---|---|
ADD dest, src | dest โ dest + src |
SUB dest, src | dest โ dest โ src |
MUL src | AX โ AX ร src (unsigned 16-bit; high word in DX) |
DIV src | AX โ DX:AX รท src; DX โ remainder (unsigned) |
CMP a, b | Sets flags based on a โ b without storing result |
JE / JNE | Jump if equal / not equal (checks ZF) |
INT 21h, AH=01h | Read one character from keyboard into AL |
INT 21h, AH=02h | Print character in DL to screen |
INT 21h, AH=09h | Print $-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
- Data segment setup โ
MOV AX, @DATA / MOV DS, AXpoints DS at the program’s data segment so that named variables likenum1are addressable. - 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. - Operator dispatch โ a chain of
CMP / JEpairs checks the operator byte against each supported character and jumps to the corresponding handler label. - MUL โ the unsigned
MUL BXinstruction multiplies AX by BX and stores the 32-bit result across DX:AX. For single-digit inputs the result always fits in AX. - DIV guard โ
CMP BX, 0 / JE DIV_ERRORintercepts divide-by-zero before the CPU can trigger interrupt 0 (divide error fault). - 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
| Mistake | Symptom | Fix |
|---|---|---|
Forgetting XOR DX, DX before DIV | Wrong quotient or divide fault | Always zero DX before unsigned 16-bit division |
| Not converting ASCII to binary | Arithmetic on wrong values (e.g. 55 instead of 7) | Subtract '0' immediately after reading |
Using MOV AH, 0 before storing to num1 | High byte garbage in word variable | Zero AH so the full 16-bit word is correct |
Missing $ string terminator | INT 21h / AH=09h prints garbage beyond the string | Every string must end with $ |
See Also
- 8086 Assembly: Fibonacci Sequence
- 8086 Assembly: GCD Using Euclidean Algorithm
- 8086 Assembly: Factorial Using Recursion
- Introduction to Memory Segmentation in 8086
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.