Abstract: This article describes how to write, install, and use interrupt service programs in C language. As hardware port read/write operations are involved in writing a hardware interrupt service program, you can directly deal with the hardware, and there are a lot of data (such as hardware port addresses) to be used in the program design process, this makes it much easier for programmers and computer hardware devices to directly program hardware in assembler languages. This article only introduces the preparation of Soft Interrupt programs.
Keywords: Soft Interrupt, interrupt vector, interrupt vector table, TSR memory resident, DOS Re-entry, interrupt request, segment address, offset, register, bios, DOS, setvect (), getvect (), keep (), disable (), enable (), geninterrupt (), int86 (), interrupt
For General C language enthusiasts, we should be very familiar with how to use interrupt routines in C. For example, we can use int86 () the function calls the 13h interrupt to directly operate the physical sector of the disk. You can also call the int86 () function to call the 33h interrupt to display the mouse and cursor on the screen. In fact, 13h and 33h are just some functions, and the parameters of these functions are passed through the CPU register. The interrupt number is only indirectly directed to the starting memory unit of the function body. It is indirect, that is, the START segment address and offset of the function are calculated by the interrupt number (the following explains how to operate it ). As a result, programmers do not have to spend too much time writing hardware programs. They only need to set parameters in their own programs, you can call the interrupt service program provided by BIOS or DoS, which greatly reduces the difficulty of Program Development and shortens the program development cycle. Since the interrupt is a function, it can be called by the user and written by the user.
The first 1024 bytes (offset: 256 h to 003ffh) in the computer memory store interrupt vectors, each of which occupies 4 bytes, the first two bytes store the entry address offset of the interrupt service program, and the last two bytes store the entry segment address of the interrupt program. during use, you only need to transfer them to the Register IP address and CS respectively, the service interruption program can be transferred to implement the call interruption. When an interrupt occurs, the CPU times the interrupt number by 4 and obtains the interrupt vector address in the interrupt vector table. In this way, the IP address and Cs value are obtained and then transferred to the endpoint address of the interrupt service program, call interrupted. This is the basic process of calling the interrupt service program through the interrupt number. When the computer is started, the BIOS fills in the interrupt vector table with the basic interrupt. When dos gets control of the system, it also needs to fill in some interrupt vectors in the table and modify some BIOS interrupt vectors. Some interrupt vectors are reserved by the system for users, such as 60 h to 67h interruptions. Users can write their interrupt service programs into these interrupt vectors. In addition, you can modify and improve the existing interrupt vectors of the system.
In the C language, a new function type interrupt is provided to define the interrupt service program. For example, we can write the following interrupt service program:
/* Example 1: service interruption */
Void interrupt int60 ()
{
Puts ("this is an example ");
}
The interrupted function is to display a string. Why not use the printf () function? This involves DOS Re-entry, which will be introduced later.
After a simple interrupt service program is written, how can I enter its function entry address in the interrupt vector table so that it can be transferred to the interrupt service program for execution when an interruption occurs? The setvect () and getvect () functions are used here. Setvect () has two parameters: the interrupt number and the function entry address. The function is to install the specified function to the specified interrupt vector. The getvect () function has a parameter: the interrupt number. The return value is the interrupt entry address. Before the installation is interrupted, it is best to use the disable () function to disable the interruption, so as to prevent program running disorder caused by new interruptions during the installation process. After the installation is complete, use Enable () function opening is interrupted to make the program run normally. Now we can enrich the above examples:
/* Example 2: Write, install, and use the interrupted service program */
# Include <dos. h>
# Include <stdio. h>
# Ifdef _ cplusplus
# DEFINE _ argu...
# Else
# DEFINE _ argu
# Endif
Void interrupt int60 (_ argu)/* interrupt service function */
{
Puts ("this is an example ");
}
Void install (void interrupt (* FADD) (_ argu), int num)/* installation interrupted */
{
Disable ();/* disable interrupt */
Setvect (Num, FADD);/* set the interrupt */
Enable ();/* Open interrupt */
}
Void main ()
{
Install (int60, 0x60);/* install the int60 function to 0x60 interrupt */
Geninterrupt (0x60);/* manual 0x60 interruption */
}
Readers with some experience can easily get the execution result of the program: "This is an example!" is displayed on the screen !".
This section describes how to compile and install an interrupted service program. Next, let's talk about writing and using a memory resident program (TSR. In C, you can use the keep () function to resident the program in the memory. This function has two parameters: Status and size. Size is the length of the resident memory, which can be obtained using size = _ SS + _ sp/16-_ PSP. Of course, this is also an estimation method, not an exact value. After the function is executed, the exit status information is saved in status. For example, for the above example, rewrite "geninterrupt (0x60);" to "keep (0, _ SS + _ sp/16-_ PSP ); "and then execute the program, this program will be resident, and then in any other software or program design, as long as the 60 h interrupt is used, "This is an example!" is displayed on the screen!" . To restore the definition of 60 H interruption, you can only restart the computer.
As in the above example, it is still not perfect. It does not consider the status of the DOS system environment, whether the program has resident memory, or whether to exit the memory resident. The second problem is easily solved: The execution program reads a function interrupt entry address (for example, the 63h interrupt) from the beginning to determine whether it is null ), if it is null, the address is set to non-empty and then resident memory. If it is not empty, it indicates that the address has resident and exited the program. This step is very important; otherwise, the system will crash due to the excessive memory space occupied by the repeated resident. For the other two questions, I will not explain them here. Interested readers can refer to some related books.
In addition, we can also use the hotkey in DOS to call the memory resident program. For example, you can activate the program by pressing CTRL + F11 at any time after the memory of the expected Chinese character system is resident. The dictionary interface appears. The Microcomputer keyboard has a microprocessor chip used to scan and detect the press and release status of each key. Most keys have a scan code to tell the current CPU status. However, some special keys such as printscreen and CTRL + break do not generate scan codes, but directly interrupt them. Because of this, we can point the interrupt number generated by Ctrl + break to the entry address of our own program. After pressing CTRL + break, the system will call our own program for execution, which is actually modifying the interrupt vector of Ctrl + break. For other key activation programs, you can use the scanning code captured by the 9h keyboard interrupt. For example, after executing the following program, return to the DOS system and press Ctrl + break at any time, the background color of the screen turns red.
/* Example 3: interrupt service program writing, installation, and usage, memory resident */
# Include <dos. h>
# Include <conio. h>
# Ifdef _ cplusplus
# DEFINE _ argu...
# Else
# DEFINE _ argu
# Endif
Void interrupt newint (_ argu);/* function declaration */
Void install (void interrupt (* FADD) (_ argu), int num );
Int main ()
{
Install (newint, 0x1b);/* Ctrl + break interrupt number: 1bh */
Keep (0, _ SS + (_ sp/16)-_ PSP);/* resident program */
Return 0;
}
Void interrupt newint (_ argu)
{
Textbackground (4);/* set the screen background color to red */
Clrscr ();/* clear screen */
}
Void install (void interrupt (* FADD) (_ argu), int num)
{
Disable ();
Setvect (Num, FADD);/* set the interrupt */
Enable ();
}
Because the interruption of the 13h number is a disk interruption service program provided by the bios, for applications under DOS, their disk storage and disk reading functions are implemented by calling this interruption. Many dos viruses like to modify the 13h interrupt to destroy the system. For example, modify the 13h interrupt service program and change it:
/* Example 4: pseudocode of the virus program */
Void interrupt new13 (_ argu)
{
If (the virus attack condition is mature)
{Modify the entry parameter to the entry address of the virus program;
Execute virus code;
}
The original 13 H call was interrupted;
}
The virus is activated when any software (such as Edit. com) has operations on the disk and the virus attack conditions are ripe. Of course, this will reduce the available memory space and be easily discovered by users. Some "smart" viruses will modify other interrupt vectors so that the memory size reported by the system is in line with the actual situation. Another virus, when it is found that the user traces it through some programs (such as debug. com), it will quietly slide, and its basic principle is still related to the change interruption. The 0-side, 0-column, and 1-sector (side 0 cylinder 0 sector 1) of the hard disk stores important boot information. Once damaged, the computer cannot identify the hard disk. We can write a program to prevent any software (including viruses) from performing "write" operations on this sector. To some extent, we have implemented the "Write protection" function, its basic principle is to modify the interrupt vector of the 13h number and stay in the memory, monitoring every detail of the disk operations of the software (including viruses. Reader's note: this program does not consider the exit of memory resident. If you want to resume the interruption of the 13h number, restart the computer.
/* Example 5: Primary boot sector protection. Use Turbo C 2.0 for compilation. mbsp. C */
# Include <dos. h>
# Include <stdio. h>
# Include <stdlib. h>
# Define stsize 8192
# Define psp_env_psp 0x2c
# Define para (x) (fp_off (x) + 15)> 4)
Typedef struct {
Unsigned bp, Di, Si, DS, es, dx, CX, BX, ax, IP, Cs, flags;
} Interrupt_parameter;
Void install (void interrupt (* faddress) (), int num );
Void interrupt new13 (interrupt_parameter P );
Int main ()
{
Union regs;
Struct sregs;
Unsigned MEM;
Unsigned far * pointer;
Char far * stack;
Printf ("/n <Master Boot Sector protector> Version 1.0/n ");
If (stack = malloc (stsize) = NULL)
{
Printf ("not enough memory! /N ");
Exit (1 );
}
If (getvect (0x62 )! = NULL)
{
Printf ("already installed! /N ");
Exit (1 );
}
Install (getvect (0x13), 0x62 );
Install (new13, 0x13 );
Pointer = mk_fp (_ PSP, psp_env_psp );
Freemem (* pointer );
Segread (& sregs );
Mem = sregs. DS + para (stack)-_ PSP;
Setblock (_ PSP, Mem );
Keep (0, Mem );
Return 0;
}
Void install (void interrupt (* faddress) (), int num)
{
Disable ();
Setvect (Num, faddress );
Enable ();
}
Void interrupt new13 (interrupt_parameter P)
{
P. Ax = _ ax;
P. Cx = _ CX;
P. dx = _ DX;
If (_ Ah = 0x03 & _ CH = 0 & _ Cl = 0x01 & _ DH = 0 & _ DL = 0 x 80) return;
Enable ();
Geninterrupt (0x62 );
Disable ();
_ AX = P. Ax;
_ Cx = P. CX;
_ Dx = P. dx;
Return;
}
Note: before using this program, please: ① Use anti-virus software to perform a full scan of computer boot sectors, memory and all files to ensure that there is no virus in the computer; ② readers with computer assembly language basics can write a new boot program by themselves, first resident the program in the memory, and then call the original boot program, this allows you to enable the protection function before the virus has control of the system.
Finally, we will briefly describe the DOS system re-entry problem. DOS is a single-user, single-task operating system. If the program is interrupted during execution, it may be caused by the failure of the original running environment, which is catastrophic. When an interrupt occurs, the CPU immediately terminates the current program to execute the interrupt service program. If a DOS interrupt call (such as a DOS 21h interrupt) occurs in the interrupt service program, in this way, the global variables of the environment will be overwritten (for example, the PSP segment prefix will be changed to the PSP of the interrupted program being executed), so that the original environment will be damaged and the original program will not be correctly executed. When the call is interrupted and the response is returned, the user gets unexpected results. Therefore, you should avoid calling the DOS system functions when writing interrupt service programs. malloc (), printf (), sprintf () and other functions should not appear in the interrupt service program in C language.