DOS Interrupts File Processing
 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 ```