I am using tasm and dosbox to solve a problem. I have to catch the timer interrrupt and output all squares of natural numbers until they have 16-bit length. In the example my professor provides the 08h interrupt is replaced with a new one, which is not a good thing to do, since altering 08h can lead to malfunction. As far as I know it is much better to toggle 1ch interrupt, which is triggered by 08h every 18,2 ms and does not do anything necessary to the computer. Moreover reading my professor's code has left me with a great number of whats and whys. I wrote the following code, but it does not seem do work as intended, it does output squares, however, it stops execution at a random moment of time and I do not understand what I am missing. Any help would be appreciated, thanks for reading
.MODEL SMALL
.STACK 1024H
.DATA
NUM DB 1
SQUARE DW ?
.CODE
ASSUME CS:@CODE, DS:@DATA, ES:NOTHING
START:
OLD_INT_SEG DW ?
OLD_INT_OFF DW ?
MOV AX, @DATA
MOV DS, AX
;saving away the old interrupt;
MOV AX, 0
MOV ES, AX
MOV AX, ES:[1CH * 4]
MOV WORD PTR OLD_INT_OFF, AX
MOV AX, ES:[1CH * 4 + 2]
MOV WORD PTR OLD_INT_SEG, AX
;patching into the new one;
CLI
MOV WORD PTR ES:[1CH * 4], OFFSET TIMER_ISR
MOV WORD PTR ES:[1CH * 4 + 2], CS
STI
;calculating squares;
TO_THE_SECOND_POWER:
XOR AX, AX
MOV AL, NUM
MUL NUM
;outputing them;
CALL PRINT_NUM
INC NUM
LOOP TO_THE_SECOND_POWER
;the new isr's body;
TIMER_ISR PROC NEAR
PUSH DS
PUSH AX
MOV AX, @DATA
MOV DS, AX
JС OVERFLOW_REACHED
POP AX
POP DS
;calling the original 1ch isr;
JMP CS:OLD_INT_OFF
TIMER_ISR ENDP
PRINT_NUM PROC
PUSH BX
PUSH DX
PUSH SI
PUSH CX
MOV CX, 0
MOV BX, 10
LOOP_HERE:
MOV DX, 0
DIV BX
PUSH AX
ADD DL, "0"
POP AX
PUSH DX
INC CX
CMP AX, 0
JNZ LOOP_HERE
MOV AH, 2
LOOP_HERE_2:
POP DX
MOV AH, 02H
INT 21H
LOOP LOOP_HERE_2
MOV DL, 10
MOV AH, 02H
INT 21H
MOV DL, 13
MOV AH, 02H
INT 21H
POP CX
POP SI
POP DX
POP BX
RET
PRINT_NUM ENDP
;if overflow has occurred;
OVERFLOW_REACHED:
;restoring the old isr;
MOV AX, 0
MOV ES, AX
CLI
MOV AX, WORD PTR OLD_INT_OFF
MOV ES:[1CH * 4], AX
MOV AX, WORD PTR OLD_INT_SEG
MOV ES:[1CH * 4 + 2], AX
STI
;and terminating the program;
MOV AH, 4CH
MOV AL, 0H
INT 21H
END START
CODE ENDS
Your code segment begins at the START:
label with two DW ?
directives. They will be executed as if they were code. The fact that your code does not crash right there is a miracle.
To be perfectly (paranoidly so) correct, saving away the old interrupt should be done inside the CLI
/STI
block, because in theory someone might change it after you have saved it and before you patched it.
Your JС OVERFLOW_REACHED
jumps if the C flag was set, but does not follow any instructions that would modify the C flag. (MOV instructions do not alter the C flag.) Therefore, it will probably jump if the C flag was set by whatever code was pre-empted by the interrupt. That's a recipe for disaster.
Loop LOOP TO_THE_SECOND_POWER
uses the CX
register as a loop counter. You do not initialize CX
anywhere.
After LOOP TO_THE_SECOND_POWER
you are not doing anything meaningful to ensure that your program will gracefully handle the case where it computes all numbers that were to be computed.
Your interrupt handler may invoke OVERFLOW_REACHED:
, which will invoke INT 21H
. That's a recipe for disaster. Do not invoke any operating system functions from within an interrupt handler.
In short, your interrupt handler should set some variable to inform your main loop that it should stop looping.