A Time Display Program 

As an example of using interrupt routines, we write a program that displays the current time. When the computer is powered up, the current time can be entered by the user or supplied by a real-time clock circuit that is battery powered. This time value is kept in memory and updated by a timer circuit using interrupt 8. A program can call the DOS INT 21h, function 2Ch, to access the time.

Our time display program has the following steps:
Obtain the current time
Convert the hours, minutes, and seconds into ASCII digits
Display the ASCII digits

The procedure GET_TIME is used to get the current time and the procedure CONVERT is used to convert a number into ASCII. It first divides the number in AL by 10. This puts the ten's digit value in AL and the unit's digit value in AH (note that the input value is less than 60). Then, it converts both digits to ASCII. These two procedures are shown below:

Example: Procedures GET_TIME and CONVERT.
PUBLIC GET_TIME
.MODEL SMALL
.CODE
GET_TIME PROC
        MOV AH, 2CH	; CH=hour, CL=min, DH=sec
        INT 21H
; convert hours into ASCII and store
        MOV AL, CH
        CALL CONVERT
        MOV [BX], AX
; convert minutes into ASCII and store
        MOV AL, CL
        CALL CONVERT
        MOV [BX+3], AX
; convert seconds into ASCII and store
        MOV AL, DH
        CALL CONVERT
        MOV [BX+6], AX
        RET
GET_TIME ENDP
CONVERT PROC
        XOR AH, AH
        MOV DL, 10
        DIV DL		; AH has remainder, AL has quotient
        OR AX, 3030H	; convert to ASCII
        RET
CONVERT ENDP
END

To display the current time, a time buffer, TIME_BUF, is initialized with 00:00:00. The procedure GET_TIME is then called to store the current time in the time buffer. Then, the time_buffer is displayed using INT 21h, function 9. This program is shown below:

Example: Display current time.
EXTRN GET_TIME: NEAR
.MODEL SMALL
.STACK 100H
.DATA
TIME_BUF DB '00:00:00$';
.CODE
MAIN PROC
        MOV AX, @DATA		; initialize DS
        MOV DS, AX
; get and display time
        LEA BX, TIME_BUF	; BX points to TIME_BUF
        CALL GET_TIME		; put current time in TIME_BUF 
        LEA DX, TIME_BUF	; display time
        MOV AH, 9
        INT 21H

        MOV AH, 4CH		; return to dos
        INT 21H
MAIN ENDP
END MAIN

User Interrupt Procedures

To make the time display program more interesting, let us write a second version that displays the time and updates it every second.

One way to continuously update the time is to execute a loop that keeps obtaining the time via INT 21h, function 2Ch and displaying it. The problem here is to find a way to stop the program.

Instead, this can be done by writing a routine for interrupt 1Ch. This interrupt is generated by the INT 8 routine which is activated by a timer circuit about 18.2 times a second. When our interrupt routine is called, it will get the time and display it.

Set Interrupt Vector

To set up an interrupt routine we need to do the following steps:
Save the current interrupt vector
Place the vector of the user procedure in the interrupt vector table
Restore the previous vector before termination the program

We use the INT 21h, function 35h, to get the old vector and function 25h to set up the new interrupt vector.

Set interrupt Vector: INT 21h, function 25h
; store interrupt vector into vector table
Input:	AH = 25h
	AL = interrupt  number
	DS:DX = interrupt vector
Output: none 

Get interrupt Vector: INT 21h, function 35h
; obtain interrupt vector from vector table
Input:	AH = 35h
	AL = interrupt  number
Output: ES:BX = interrupt vector

The following procedure, SETUP_INT, saves an old interrupt vector and sets up a new vector. It gets the interrupt number in AL, a buffer to save the old vector at DS:DI, and a buffer containing the new interrupt vector at DS:SI. By reversing the two buffers, SETUP_INT can also be used to restore the old vector.

Example: Procedure SETUP_INT for setting interrupt vector.
PUBLIC SETUP_INT
.MODEL SMALL
.CODE
SETUP_INT PROC
;saves old interrupt vector and sets a new one
;input: AL = interrupt number
;       DI = address of buffer for old vector
;       SI = address of buffer containing new vector 
; save old interrupt vector
        MOV AH, 35H     ;get vector
        INT 21H
        MOV [DI], BX    ;save offset
        MOV [DI+2], ES  ;save segment
;setup new vector
        MOV DX, [SI]
        PUSH DS
        MOV DS, [SI+2]
        MOV AH, 25H     ;set vector
        INT 21H
        POP DS          ;restore DS
        RET
SETUP_INT ENDP
END

Cursor Control

Each display of the current time by INT 21h, function 9, will advance the cursor. If a new time is then displayed, it appears at a different screen position. So, to view the time updated at the same screen position we must restore the cursor to its original position before we display the time. This is achieved by first determining the current cursor position; then, after each print string operation, we move the cursor back.

We use the INT 10h, functions 3 and 2, to save the original cursor position and to move the cursor to its original position after each print string operation.

Move Cursor: INT 10h, function 2
Input:	AH = 2
	BH = page number
	DH = row number
	DL = column number
Output: none 

Get Cursor Position: INT 10h, function 3
Input:	AH = 2
	BH = page number
Output: DH = row number
	DL = column number
	CH = starting scan line for cursor
	CL = ending scan line for cursor

Interrupt Procedure

The interrupt procedure, TINE_INT, is written to perform the following steps:
Set DS
Get new time
Display time
Restore cursor position
Restore DS

The main procedure is written to have the following steps:
Save the current cursor position
Set up the interrupt vector for the interrupt procedure, TIME_INT
Wait for a key input
Restore the old interrupt vector and terminate

To do step 2, we use OFFSET and SEG to obtain the offset and segment of procedure SETUP_INT; the vector is then stored in the buffer NEW_VEC. The procedure SETUP_INT, is called to set up the vector for interrupt type 1Ch, timer tick. The interrupt 16h, function 0 is used for step 3, key input. Procedure SETUP_INT is again used in step 4; this time SI points to the old vector and DI points to the vector for TIME_INT.

After setting up the cursor and interrupt vectors, the main procedure just waits for a keystroke. In the meantime, the interrupt procedure, TIME_INT, keeps updating the time whenever the timer circuit ticks. After a key is hit, the old interrupt vector is restored and the program terminates. The complete program is given below.

Example: Display updated time program.
EXTRN GET_TIME:NEAR, SETUP_INT: NEAR
.MODEL SMALL
.STACK 100H
.DATA
TIME_BUF        DB '00:00:00$'
CURSOR_POS      DW ?
NEW_VEC         DW ?,?
OLD_VEC         DW ?,?
.CODE
MAIN PROC
        MOV AX, @DATA
        MOV DS, AX
;save cursor position
        MOV AH, 3
        MOV BH, 0
        INT 10H
        MOV CURSOR_POS, DX
; setup interrupt procedure
        MOV NEW_VEC, OFFSET TIME_INT
        MOV NEW_VEC+2, SEG TIME_INT
        LEA DI, OLD_VEC
        LEA SI, NEW_VEC
        MOV AL, 1CH
        CALL SETUP_INT
;read keyboard
        MOV AH, 0
        INT 16H
;restore old interrupt vector
        LEA DI, NEW_VEC
        LEA SI, OLD_VEC
        MOV AL, 1CH
        CALL SETUP_INT
;exit to DOS
        MOV AH, 4CH
        INT 21H
MAIN ENDP
;
TIME_INT PROC
        PUSH DS
        MOV AX, @DATA
        MOV DS, AX
; get new time
        LEA BX, TIME_BUF
        CALL GET_TIME
; display time
        LEA DX, TIME_BUF
        MOV AH, 9
        INT 21H
;restore cursor position
        MOV AH, 2
        MOV BH, 0
        MOV DX, CURSOR_POS
        INT 10H
        POP DS
        IRET
TIME_INT ENDP
END MAIN
END