Here is an example of using a DOS service to get information about the location of interrupt handlers. This program can help you locate the handlers for all 256 possible interrupts. A table of 16 lines and 16 columns that contain the interrupt numbers followed by the letters B, D or N is output to the screen. These letters indicate the area where the segment address of the interrupt points. The letter B stands for BIOS and the letter D stands for DOS. N means that the interrupt has a dummy handler - a handler which has an IRET instruction as its first instruction. Let's have a look then at how this program works.
; ; Program Ch18Pr8 ( ch18pr8.asm ) ; .model small .stack .data tbint db 16 dup ('nu-H ') , '$' HexSym db '0','1','2','3','4','5','6','7' db '8','9','A','B','C','D','E','F' NumInt db 0 NumIntL db 0
The data area contains a pattern for the output string, an array of constants and two variables. The pattern for the output string is made up of 16 fields each of which is 5 bytes long (tbint). The next array HexSym contains 16 text constants which are ASCII representations of 16 Hexadecimal numbers. The variable NumInt contains the number of the first interrupt to be output in the line. The variable NumIntL is the number of the interrupt in the current column of this line. The initial value for both these variables is 0.
.code begin: mov ax,@data mov ds,ax mov cx,16 ; Line counter
The program begins as usual by loading segment register DS. Following that, the value 16 is placed into the register CX. This specifies the number of lines to be printed, and register CX will be used as a counter for the corresponding cycle. Beginning with the Label Rows:, the first instruction saves the cycle counter CX because CX will be used in a nested cycle that starts with the label Intrs:. By putting the inital value in the registers DI and CX, the next cycle is prepared.
Rows: ; Cycle starts here push cx ; Save this counter mov di,0 ; Counter within line mov cx,16 ; Columns counter mov al,NumInt mov NumIntL,al
DI is used as an index of the current element in the output string and CX as a cycle counter. NumIntL now contains the first interrupt number and we are ready to begin the next cycle. The first thing being done here is the setup for INT 21h Function 35 (Get Interrupt Vector) DOS service. To use this Service, put the number of the interrupt you want to locate into register AL. The result will be given in registers ES and BX - ES will contain the segment address of the corresponding interrupt handler and BX will contain its offset address.
Intrs: ; mov al,NumintL ; Load interrupt number mov ah,35h ; Get interrupt vector (ES:BX) int 21h ; DOS service call mov dx,es ; Segment address of interrupt handler cmp dx,0A000h ; Compare it to the BIOS start address ja InBios ; If DX is greater - handler is in BIOS mov tbint[di+3],'D' ; Set indicator 'DOS' jmp DoneIn ; To the end of block InBios: mov tbint[di+3],'B' ; Set indicator 'BIOS' DoneIn: ; This is the end of block
Next we load the segment address found from ES into DX and compare it with the value A000h, which is the beginning of most BIOS areas. For segment locations above this address, we fill the tbint[di+3] string with the letter 'B'. Locations below this get assigned the letter 'D'. Each field is output in one column. The expression tbint[di+3] defines the place-holder for this letter. Here, DI contains the offset current field from the beginning of the string - 0 for the first element, 5 for the second, 10 for third and, eventually, 75 for the last one. Constant offset 3 defines the position of the letter within the current field.
Next set of instructions checks whether the first instruction of our found handler is an IRET instruction. The operation code for this instruction is 0CFh. If this code is found at location es:[bx] (the beginning of the interrupt handler), the corresponding interrupt is marked by the letter 'N' (Not set).
cmp byte ptr es:[bx],0CFh ; First instruction IRET? jne PresHan ; If not - handler presented mov tbint[di+3],'N' ; Set indicator 'Not set'
Register AL contains an unsigned integer number of the interrupt and needs to be converted into symbolic representation suitable for output. Because this number is less than 256, it cannot consist of more than two hexadecimal digits. Divide the 8-bit number by 16 to get these digits. The quotient is the first digit of the result and the remainder - the second digit. Note that after the DIV command for 8-bit integers, the quotient from the DIV command is stored in register AL and the remainder in register AH.
PresHan: ; mov ah,0 ; AL keeps the interrupt number mov dl,16 ; Prepare to converting AL to symbols div dl ; AX / 16 mov bx,offset HexSym xlat ; translate into AL by table lookup in DS:BX mov byte ptr tbint[di],al ; symbol into output line mov al,ah xlat mov byte ptr tbint[di+1],al ; symbol into output line
Transforming the 8-bit integers contained in registers AL and AH into ASCII codes is possible using the XLAT instruction. The value in AL is used as an index for looking into the corresponding HexSym table. Once AL has been converted, the result is stored in the proper byte position of tbint[di]. Register AH is next for the XLAT instruction and loading of the proper byte position + 1 (tbint[di+1]).
DI register is increased by 5 (the width of one column in a table to be printed) and NumIntL by 16 (the difference between the number off interrupts in adjoining columns). The entire cycle is executed again until such time as the CX register (our cycle counter) is equal to 0.
add di,5 ; Next position in the line add NumIntL,16 ; Increase interrupt number loop Intrs ; Next step - next interrupt
Now that one line has been created by the execution of the nested cycle Intrs, the text string is ready to be displayed onto the screen using the function 09h of interrupt 21h(Display ASCIIZ String). Using this Function requires the address of the string in registers DS (segment) and DX (offset).
pop cx ; Restore cycle counter mov ah,09 ; Function 09 - output string mov dx,offset tbint ; Address of string in DS:DX int 21h ; Output one string inc NumInt ; loop Rows ; Next step - next string
Once all 16 Rows are displayed, we can finish the program using the standard Interrupt 21h Function 4Ch (Terminate with Output Code). The Return Code will be zero to indicated Successful Completion. DOS likes to interpret Error Level's of Zero as Success.
mov ax,4C00h int 21h end begin
A sample of the output created by this program is shown below. You may get a slightly different picture, depending on what resident programs and installable device drivers you are using.
00-D 10-B 20-D 30-B 40-B 50-B 60-D 70-D 80-D 90-D A0-D B0-D C0-D D0-D E0-D F0-D 01-N 11-B 21-D 31-B 41-B 51-B 61-D 71-B 81-D 91-D A1-D B1-D C1-D D1-D E1-D F1-D 02-D 12-B 22-D 32-N 42-B 52-B 62-D 72-B 82-D 92-D A2-D B2-D C2-D D2-D E2-D F2-D 03-N 13-B 23-D 33-D 43-B 53-B 63-D 73-B 83-D 93-D A3-D B3-D C3-D D3-D E3-D F3-B 04-N 14-B 24-D 34-N 44-B 54-B 64-D 74-D 84-D 94-D A4-D B4-D C4-D D4-D E4-D F4-D 05-B 15-D 25-D 35-N 45-B 55-B 65-D 75-B 85-D 95-D A5-D B5-D C5-D D5-D E5-D F5-D 06-B 16-D 26-D 36-N 46-B 56-B 66-D 76-D 86-D 96-D A6-D B6-D C6-D D6-D E6-D F6-D 07-B 17-D 27-D 37-N 47-B 57-B 67-D 77-D 87-D 97-D A7-D B7-D C7-D D7-D E7-D F7-D 08-B 18-B 28-N 38-N 48-B 58-B 68-B 78-D 88-D 98-D A8-D B8-D C8-D D8-D E8-D F8-D 09-D 19-D 29-D 39-N 49-B 59-B 69-B 79-D 89-D 99-D A9-D B9-D C9-D D9-D E9-D F9-D 0A-B 1A-B 2A-D 3A-N 4A-B 5A-B 6A-B 7A-D 8A-D 9A-D AA-D BA-D CA-D DA-D EA-D FA-D 0B-B 1B-D 2B-N 3B-N 4B-B 5B-B 6B-B 7B-D 8B-D 9B-D AB-D BB-D CB-D DB-D EB-D FB-D 0C-B 1C-B 2C-N 3C-N 4C-B 5C-D 6C-B 7C-D 8C-D 9C-D AC-D BC-D CC-D DC-D EC-B FC-D 0D-B 1D-B 2D-N 3D-N 4D-B 5D-B 6D-B 7D-D 8D-D 9D-D AD-D BD-D CD-D DD-D ED-D FD-D 0E-D 1E-D 2E-D 3E-N 4E-B 5E-B 6E-B 7E-D 8E-D 9E-D AE-D BE-D CE-D DE-D EE-D FE-D 0F-N 1F-B 2F-D 3F-N 4F-D 5F-B 6F-B 7F-D 8F-B 9F-D AF-D BF-D CF-D DF-D EF-D FF-D
Using Win98 Dos Sessions, the above values were reported. As you may have guessed, the Operating System also influences the results.
Monday, February 12, 2007