“Assembly Language” 3rd Edition by Wang Shuang
Chapter 12: Internal Interrupts (Page 251)
Experiment 12: Writing the Handler for Interrupt 0
Write the handler for interrupt 0, so that when a division overflow occurs, the string “divide error!” is displayed in the center of the screen, and then return to DOS.
Requirements: Carefully track and debug; do not proceed with subsequent course learning until you understand the entire process.
===========================
The analysis is as follows:
How to write an interrupt handler and how to install it has been detailed by Professor Wang Shuang in the book, along with examples, so I will not elaborate further. However, one point needs attention:
Why does interrupt 0 not return using iret, but instead uses mov ax, 4c00H int 21H to terminate the program?
My understanding is that when a division overflow occurs, the original program should not continue executing, so interrupt 0 ends with a normal program termination code and returns to DOS (as required in the question). If iret were used, it would return to the original program and continue execution.
I am not sure if this understanding is correct, but I have also tested this point (see the debugging tracking explanation later), and related questions have been checked with Doubao (see the notes). The final conclusion is that due to the nature of interrupts and the special case of division overflow, the interrupt 0 handler generally does not return with iret, but instead terminates the program and returns to the operating system.
The example code is as follows:
; "Assembly Language" 3rd Edition by Wang Shuang; Chapter 12: Internal Interrupts (Page 251); Experiment 12: Writing the Handler for Interrupt 0; T12-1.asm: Interrupt 0 Handler and Its Installation Program
assume cs:code
code segment ; Copy the machine code of the interrupt 0 handler to 0:200H
start: mov ax, cs ; can also be written as mov ax, code
mov ds, ax
mov si, offset int0_begin
mov ax, 0
mov es, ax
mov di, 200H
mov cx, offset int0_end - offset int0_begin
cld
rep movsb
; Modify the interrupt vector table to set the entry address of interrupt 0 to 0:200H
mov ax, 0
mov es, ax
mov word ptr es:[0*4], 200H
mov word ptr es:[0*4+2], 0
; Installation complete, exit the installation program
mov ax, 4c00H
int 21H
; Custom interrupt 0 handler
int0_begin: jmp short int0_start ; jmp makes the entry address fixed
db "divide error!"
int0_start: mov ax, cs ; Note: cannot be written as mov ax, code
mov ds, ax
mov si, 202H ; Offset address 202H, because jmp occupies 2 bytes
mov ax, 0B800H
mov es, ax
mov di, 12*160+33*2 ; Display in the center of the screen
mov cx, 13
s: mov al, [si]
mov es:[di], al
inc di
mov byte ptr es:[di], 4 ; Red text display
inc di
inc si
loop s
mov ax, 4c00H ; Why not return with iret?
int 21H
int0_end: nop
code end
send start
The test program code is as follows:
; "Assembly Language" 3rd Edition by Wang Shuang; Chapter 12: Internal Interrupts (Page 251); Experiment 12: Writing the Handler for Interrupt 0; T12-2.asm: Test the Custom Interrupt 0 Handler
assume cs:code
data segment db "something...."
data ends
code segment start: mov ax, 1000
mov bl, 1
div bl ; Here overflow occurs, CPU calls interrupt 0
mov ax, data ; Test the difference between iret and int 21H
mov ds, ax
mov si, 0
mov ax, 0B800H
mov es, ax
mov di, 13*160+33*2
mov cx, 13
s: mov al, [si]
mov es:[di], al
inc di
mov byte ptr es:[di], 1 ; Blue text display
inc di
inc si
loop s
mov ax, 4c00H
int 21H
code ends
send start
The debugging tracking process is as follows:
1. Before installing the custom interrupt 0, run the test program, and you can see that the program hangs due to overflow (i.e., the default result of interrupt 0), as shown in the figure below.

2. Before executing the installation program, check the contents at 0:200H, as shown in the figure below. You can see that they are all 0, and the default entry address for interrupt 0 is: F000:1060. After running the installation program, check again, and the custom interrupt 0 handler has been copied to 0:200H, and the interrupt 0 vector also points to 0000:0200H.

3. After the custom interrupt 0 is installed, run the test program, which outputs “divide error!”, and then the program exits normally and returns to DOS, as shown in the figure below.

4. Regarding the question of why not return with iret, another test was conducted. We modified the custom interrupt 0 to use iret instead of mov ax, 4c00H int 21H, and then ran the test program. The expected output “something….” was not produced, and the program did not exit normally, but hung, as shown in the figure below.

5. By stepping through the test program, it can be found that after iret, the program returns to the div bl instruction, and then executes again, triggering interrupt 0, thus falling into an infinite loop. This is a bit strange; theoretically, after reading the div bl instruction, the IP should point to the next instruction mov ax, data, and then execute div bl, causing overflow, and the CPU calls interrupt 0. After iret returns, the IP should still point to mov ax, data, so the test program should output “something….” and not execute div bl again, leading to another overflow and thus falling into an infinite loop.

6. After re-debugging, when entering the interrupt program, first check the CS:IP saved in the stack, which indeed points to the offset address of div bl 076D:0005 and not to the offset address of the next instruction mov ax, data (as for why this is the case, I am still unclear, but the CPU should have automatically incremented the IP after fetching the instruction, as that is the hardware’s fixed logic).

We compare with the code from Experiment 11, as shown in the figure below. The offset address of the call instruction is 08, with a length of 3 bytes, so the next instruction’s offset address is 0B. We execute the call instruction and then check the saved IP in the stack, which is indeed 000B.

From this, it seems that when an overflow occurs, the IP does not point to the next instruction. It can only be speculated that it is related to the mechanism of interrupts themselves or the special nature of division overflow. Therefore, Professor Wang Shuang did not use iret to return, but chose to use mov ax, 4c00H int 21H to end the code.
==================
Notes: Explanation of related issues about interrupt 0 from Doubao (Doubao’s output may not be correct, for cross-reference and consideration only)
——————
First, let Doubao provide the example code for interrupt 0, as follows:
; Interrupt 0 handler: Division overflow handling; Function: Display overflow prompt, then return to DOS
assume cs:code
code segment
start: ; Set the interrupt vector table, pointing interrupt 0 to the custom handler
mov ax,0
mov es,ax ; ES points to segment 0 (where the interrupt vector table is located)
mov word ptr es:[0*4],offset do0 ; Store the offset address
mov word ptr es:[0*4+2],cs ; Store the segment address
; Test: Execute an instruction that will cause division overflow
mov ax,1000
mov bh,1
div bh ; This will cause division overflow, triggering interrupt 0
; Program ends normally (actually does not execute here)
mov ax,4c00h
int 21h
; Interrupt 0 handler do0:
jmp short do0_start ; String to display
db "Divide overflow!"
do0_start: ; Save context
push ax
push bx
push cx
push dx
push ds
push es
push si
push di
; Set display-related registers
mov ax,cs
mov ds,ax ; DS points to the current code segment (where the string is located)
mov si,offset do0 + 2 ; SI points to the start address of the string
mov ax,0b800h
mov es,ax ; ES points to the video memory segment
mov di,12*160 + 36*2 ; Display position: row 12, column 36
mov cx,16 ; String length
s: mov al,[si] ; Get character
mov es:[di],al ; Write to video memory (character)
mov byte ptr es:[di+1],02h ; Character attribute (black background, green text)
inc si
add di,2
loop s
; Restore context
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
; Interrupt return
iret ; Must use iret to return
code ends
send start
Note in the code comments: “The program ends normally (actually does not execute here)”, it seems to be the same situation we encountered.
———————–
Secondly, I asked Doubao, “Why does the program not continue executing after the interrupt 0 handler returns?” The explanation is as follows:
After the interrupt 0 (division overflow) handling is completed, the program typically does not continue executing, which is closely related tothe nature of interrupts and the essence of division overflow, with specific reasons as follows:
1. Division overflow is a fatal error, and the original instruction cannot continue executing
<span><span>div</span></span>/<span><span>idiv</span></span> instructions cause overflow (e.g., divisor is 0, quotient exceeds target register range), which is an unrecoverable fatal error. At this point:
- The original division instruction fails to execute, producing no valid result.
- Even if the interrupt handler processes the error prompt, the subsequent logic of the original instruction loses its meaning (because the division operation is incomplete, the subsequent code that depends on that result will be erroneous).
For example, if the program originally expected to calculate the quotient and remainder through<span><span>div</span></span> of<span><span>ax/bx</span></span>, and then use the result for subsequent calculations, once a division overflow occurs, both the quotient and remainder do not exist, and the subsequent code cannot execute correctly.
2. After the interrupt returns, the position pointed to by IP is unreasonable
When the CPU triggers an interrupt, it automatically pushes the current IP (instruction pointer) onto the stack (pointing to the next instruction after the overflow instruction), and when the interrupt handler returns using<span><span>iret</span></span>, it restores that IP, allowing the program to continue executing from “the next instruction after the overflow instruction”.
But the problem is: the division overflow instruction itself did not execute correctly, and the program logic usually depends on the result of that instruction. Even if it returns, the subsequent code will encounter logical errors due to the lack of valid calculation results (e.g., using uninitialized registers, erroneous data).
Therefore, in practical applications, the interrupt 0 handler typically does not allow the program to continue executing, but chooses toterminate the program or return to the operating system.
3. Design logic of the example code
In the previous example code for interrupt 0, the logic of the test part is:
; Triggering overflow instruction
mov ax,1000
mov bh,1
div bh ; Trigger interrupt 0
; The theoretical "subsequent code" (actually will not execute)
mov ax,4c00h
int 21h
Even if the interrupt handler returns using<span><span>iret</span></span>, the program will attempt to execute<span><span>div bh</span></span> next instruction (<span><span>mov ax,4c00h</span></span>), but this is just a special case in the example. In actual scenarios:
- The operating system’s interrupt 0 handler (like DOS) will directly terminate the program (similar to
<span><span>int 21h</span></span>function 4C), without returning to continue execution. - The custom handler, if allowed to return, may also crash due to subsequent code depending on the division result.
Conclusion
After handling interrupt 0, the program typically does not continue executing, the core reasons are:
- Division overflow is a fatal error, the original instruction has no valid result, and the subsequent code loses its basis for execution.
- Even if forced to return, the program will encounter exceptions due to data errors.
Therefore, the actual logic of handling interrupt 0 (especially at the operating system level) usually chooses to terminate the program rather than let it continue running.
————————-
Finally, I asked Doubao, “What are the special aspects of the characteristics of interrupts and division overflow that cause IP not to point to the next instruction of div bh?” The explanation is as follows:
In the division overflow interrupt, the IP (instruction pointer) actually points to<span><span>div bh</span></span> next instruction, which conforms to the general mechanism of CPU processing interrupts. But why does the program seem not to execute the next instruction? This is related to the design of the interrupt handler and the special nature of division overflow, rather than an error in the IP pointing.
1. The IP saving mechanism during CPU processing of interrupts is fixed
When<span><span>div bh</span></span> executes and an overflow occurs, the process of the CPU triggering interrupt 0 is:
- First, complete the “fetch” phase of the current instruction (the length of
<span><span>div bh</span></span>has been calculated, and the IP has automatically incremented to the address of the next instruction). - Then the CPU automatically pushes FLAGS (flag register), CS (code segment register), IP (which at this point points to
<span><span>div bh</span></span>next instruction) onto the stack. - Finally, read the entry address of the interrupt 0 handler from the interrupt vector table and execute the interrupt handling code.
Therefore, the IP was correctly pointing to<span><span>div bh</span></span> next instruction when the interrupt occurred, which is consistent with the handling mechanism of other interrupts (like<span><span>int 21h</span></span>), and there is nothing special about it.
2. The program “not continuing execution” is an active choice of the interrupt handler
The core of the problem is not the pointing of the IP, but how the interrupt handler handles the return logic:
- In normal interrupts (like
<span><span>int 21h</span></span>for keyboard input), after the interrupt handler completes its task, it uses<span><span>iret</span></span>to restore the IP, and the program continues executing from the next instruction of the original instruction (because the interrupt is “recoverable”). - But division overflow is a fatal error:
<span><span>div bh</span></span>execution fails, and the quotient and remainder are meaningless, and the subsequent code may depend on these invalid results (like using<span><span>ax</span></span>containing the quotient for calculations), continuing execution will lead to logical errors or crashes.
Therefore, the actual interrupt 0 handler (like the built-in handler of the operating system) typically will:
- Display an error message and directly call
<span><span>int 21h</span></span>function 4C (program termination), rather than executing<span><span>iret</span></span>to return. - Even if it returns (like in the example code with
<span><span>iret</span></span>), due to subsequent code possibly depending on the division result, it will also exhibit the phenomenon of “not executing normally” (like crashing, exiting abnormally).
3. Verification: IP indeed points to the next instruction
IP’s pointing can be verified through debugging (here it is said that it is inconsistent with our previous debug results):
Assuming the code is as follows:
; Address Instruction
0000:0100 mov ax,1000 ; IP=01000000:0103
0000:0103 mov bh,1 ; IP=01030000:0105
0000:0105 div bh ; IP=0105 (overflow occurs during execution)
0000:0107 mov ax,4c00h ; Next instruction, IP should point to 01070000:010A
int 21h
When the interrupt occurs, the saved IP in the stack is<span><span>0107</span></span> (the address of the next instruction after<span><span>div bh</span></span> instruction). If the interrupt handler executes<span><span>iret</span></span>, the CPU will restore IP=0107, attempting to execute<span><span>mov ax,4c00h</span></span>.
But at this point:
- If the subsequent code does not depend on the division result (like in the example directly terminating the program), it can execute normally to
<span><span>int 21h</span></span>. - If the subsequent code depends on
<span><span>div</span></span>result (like<span><span>add ax, dx</span></span>), it will error due to<span><span>ax</span></span>/<span><span>dx</span></span>values being invalid.
Conclusion
- IP in the division overflow interrupt correctly points to
<span><span>div</span></span>instruction’s next instruction, which is the fixed behavior of the CPU interrupt mechanism, with no special aspects. - The program “not continuing execution” is due to division overflow leading to invalid data, and the interrupt handler actively chooses to terminate the program, or subsequent code depends on invalid results and crashes, rather than an error in IP pointing.
The special nature of division overflow lies in the irrecoverability of the error, rather than an exception in the interrupt mechanism.