home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR4 / V12N16.ZIP / FINDIR.ZIP / FINDIR.DOC < prev    next >
Text File  |  1993-08-23  |  43KB  |  868 lines

  1. FINDIRQ.COM (VERSION 1.0)                    Copyright (c) Rick Knoblaugh
  2. -------------------------------------------------------------------------
  3.             First Published in PC Magazine September 25, 1993 (Utilities)
  4. -------------------------------------------------------------------------
  5. FINDIRQ BY RICK KNOBLAUGH
  6.  
  7.            FINDIRQ reports which interrupt request lines (IRQs) in a system
  8. are assigned to specific devices and which are free to use for installing a
  9. new peripheral device.
  10.  
  11.      When you purchase a scanner or other peripheral device, the installation
  12. instructions usually tell you to set the jumpers on the board for an
  13. ``available IRQ.''  Even if you know what an IRQ is, however, chances are you
  14. don't know which ones are in use and which are still free.  If you install
  15. your new device to use an already-assigned IRQ, it may well cause a system
  16. crash.
  17.  
  18.     What you need is FINDIRQ, a utility that tells you which devices in your
  19. system are using which IRQs and which IRQs are unassigned.
  20.  
  21.     An IRQ is simply a hardware interrupt request line that allows a
  22. peripheral to signal the computer's central processing unit that an event
  23. requiring the CPU's attention has occurred.  AT-style machines have 16 IRQ
  24. lines; older PCs have but 8.  A few IRQ assignments are more or less
  25. standard on AT-type machines:  The timer uses IRQ0 and the keyboard uses
  26. IRQ1, for example.  But if you've added optional peripherals, such as a
  27. scanner, mouse, or fax modem, you need a program like FINDIRQ to learn
  28. which IRQs the peripherals have appropriated.
  29.  
  30.     Users of Windows 3.1 and/or DOS 6.0 may wonder whether the new
  31. Microsoft Diagnostics (MSD) program makes FINDIRQ unnecessary.  MSD does
  32. a good job of showing IRQ assignments for standard devices, such as COM
  33. and LPT ports.  However, MSD will not report the IRQ assigned to any
  34. device that is not on its internal list.  MSD will  tell you the name of
  35. the program controlling a given IRQ only if you have not disabled STACKS
  36. in your CONFIG.SYS file (more on this later).
  37.  
  38.     Further, since it lacks FINDIRQ's TSR option, MSD overlooks IRQs
  39. that are enabled only while a device--such as a sound board--is active.
  40. FINDIRQ's TSR option also allows it to detect IRQ usage for devices that
  41. are only activated under Windows.  If you have a Microsoft InPort mouse,
  42. for example, and you use it only under Windows (and no DOS driver for it
  43. is installed), most DOS-based diagnostic programs will never detect that
  44. the device is present.  And while there are a number of commercial
  45. IRQ-detecting programs, each with its strengths and limitations, FINDIRQ
  46. can hold its own against any of them.
  47.  
  48.     To work from the source files, you will need the Microsoft MASM
  49. assembler (or compatible), and the LINK and EXE2BIN utilities.
  50.  
  51. USING FINDIRQ
  52.  
  53.       If you keep FIND IRQ.COM in a subdirectory on your path, simply
  54. entering  FINDIRQ  from a DOS prompt will bring up an opening display.
  55. The IRQ usage of all standard devices (those of which the BIOS has
  56. knowledge: COMn and LPTn) will be shown, along with that of any optional
  57. devices (like sound boards or SCSI adapters) that are currently enabled.
  58. IRQ assignments for devices controlled by TSR programs will also be
  59. displayed if the TSR is loaded in conventional memory.
  60.  
  61.     To report IRQ information for devices controlled by TSRs that are
  62. loaded in high memory, FINDIRQ must be invoked with LOADHIGH, thus 
  63. LH FINDIRQ. Note that when FINDIRQ is loaded high, it can still detect
  64. TSRs loaded into low memory, so if you use high memory on your system,
  65. it's always a good idea to load FINDIRQ high.
  66.  
  67.     In listing the IRQ usage, FINDIRQ assumes certain standard device
  68. IRQ assignments.  For example, if the BIOS reports that two COM ports
  69. are present, it assumes that COM1 uses IRQ4 and COM2 uses IRQ3; these
  70. are the standard AT assignments.  If your system supports steerable
  71. IRQs and you have routed them to nonstandard assignments (a rare
  72. situation), FINDIRQ will not properly report the IRQs used by those
  73. devices.
  74.  
  75.     For standard devices, FINDIRQ displays the device name.  For
  76. optional character devices, FINDIRQ displays the name of the device as
  77. specified by the device driver.  For optional block devices,
  78. ``Block Device'' is displayed. If a device is being handled by a TSR,
  79.  FINDIRQ displays the name of the TSR program.
  80.  
  81. FINDIRQ SYNTAX OPTIONS
  82.  
  83.      To document even unusual and elusive interrupts, FINDIRQ provides
  84. a variety of optional parameters.  In summary form, the syntax for the
  85. command is
  86.  
  87.             [LH] FINDIRQ [/t] [/i] [/u] [/d] [/h]
  88.  
  89.      As indicated initially, some interrupts are enabled only when the
  90. devices that use them are active.  To detect such devices--sound boards,
  91. for instance--FINDIRQ must be memory-resident at the time the devices
  92. are activated.  FINDIRQ can be installed as a TSR, therefore, by entering
  93. FINDIRQ /t .
  94.  
  95.      When the TSR component of FINDIRQ is loaded, your system is
  96. constantly being monitored in the background for IRQ activity.  When a
  97. previously unassigned IRQ is enabled, its IRQ information is stored in a
  98. tiny, 2-byte data file, FINDIRQ.DAT, which FINDIRQ creates in its
  99. subdirectory.  FINDIRQ uses the information in this file to supplement
  100. what it knows about IRQ usage.  This scheme can't tell you whether a
  101. temporarily activated IRQ is currently in use, but it can tell you that
  102. the IRQ has been used at some point and is probably not available for
  103. other devices.
  104.  
  105.     If you change your system configuration--by removing or rejumpering
  106. a board, for example--you must reinitialize the FINDIRQ.DAT file.  This
  107. is done by executing the /i option  FINDIRQ /i. Regardless of whether
  108. the TSR component is resident, you display IRQ usage in the same way--by
  109. typing FINDIRQ with no switches.  If the resident portion is currently
  110. loaded, a notation will appear on the left side of the display.  Although
  111. the resident portion of FINDIRQ is very small (less than 3K), you can
  112. unload it if you wish by typing FINDIRQ /u .
  113.  
  114.      In certain cases, programs can erroneously enable IRQs; that is,
  115. the IRQs can appear to be in use when in fact they are not.  To guard
  116. against false reporting, FINDIRQ normally verifies that enabled IRQs
  117. have valid handlers before reporting that they are unavailable.  This is
  118. done by checking that the handler is not simply a ROM default interrupt
  119. handler, whose job is to field spuriously generated interrupts.
  120.  
  121.     When an IRQ is enabled under Windows and no DOS device driver for
  122. the device is installed, however, the handler cannot be verified.  There
  123. are also other instances in which interrupt handlers cannot be verified,
  124. such as when IRQ enabling is intermittent or when a memory manager has
  125. remapped ROM. In these circumstances, FINDIRQ will incorrectly report that
  126. the IRQ is available.  To suppress FINDIRQ's verification requirement and
  127. so avoid this problem, you can execute FINDIRQ with its ``don't verify''
  128. switch: FINDIRQ /d . Finally, to remind yourself of the range of FINDIRQ
  129. options, simply execute the program with its help switch: FINDIRQ /h.
  130. The help screen lists and describes all program options.
  131.  
  132. IRQ REVIEW
  133.  
  134.      Before delving into FIND IRQ's inner workings, a brief review of
  135. interrupt processing may be helpful.  Interrupts provide a time-saving
  136. alternative to periodically polling all the peripherals attached to the
  137. system to determine whether they require the CPU's attention.  Under an
  138. interrupt system, peripheral devices receive recognition only when they
  139. ask for it.  Thus, the system processor does not waste processing time
  140. repeatedly checking for characters being typed at the keyboard or data
  141. bytes being received by an attached modem.
  142.  
  143.     Devices such as the keyboard controller and the communications UART
  144. chip utilize the interrupt lines labeled IRQ0 through IRQ15.  These lines
  145. go to the 8259 Programmable Interrupt Controllers (PICs), which arbitrate
  146. the interrupt requests and then activate the processor's single interrupt
  147. request line.  A detailed discussion of this process can be found in
  148. The IBM PC from the Inside Out, by Sargent and Shoemaker (Addison-Wesley,
  149. 1984, 1986).
  150.  
  151.     Figure 2 (See below) depicts this arrangement and shows the standard
  152. IRQ assignments used by an AT-class machine.  Each 8259 PIC handles eight
  153. interrupt lines; to accommodate additional interrupt lines, multiple 8259s
  154. are tied together.  One serves as the master and has a cascade line that
  155. connects it to slave 8259s.  On the AT, IRQ2 is used for this purpose.
  156. Note, however, that you can still attach devices that utilize IRQ2.
  157. The adapter-card edge connector used for IRQ2 in PC-class machines is
  158. wired to IRQ9 in AT machines. It is then routed via a software interrupt
  159. to the interrupt for IRQ2. This round-about arrangement was implemented
  160. to provide compatibility with original PC adapter boards.
  161.  
  162.     When each 8259 is initialized, the interrupt base value is written
  163. to the PIC.  This value determines which interrupt number is generated.
  164. The master PIC starts at 8, so IRQs 0 through 7 generate interrupts 8
  165. through 15.  The slave PIC starts at 70h, so IRQs 8 thru 15 generate
  166. interrupts 70h through 77h.  The PIC registers are accessible in I/O
  167. space; the master PIC is based at 20h and the slave starts at A0h.
  168.  
  169.     FINDIRQ checks the PIC's Interrupt Mask Register to detect active
  170. interrupts.  In this 8-bit register, each bit position corresponds to
  171. one of the interrupt lines.  The BIOS and other device handlers use
  172. this register to enable or disable the generation of interrupts.
  173. Interrupts are disabled or ``masked'' when a 1 is written to the
  174. corresponding bit position, while a 0 means that they are enabled.
  175.  
  176. HOW FINDIRQ WORKS
  177.  
  178.     FINDIRQ starts by checking the system model ID byte at 0F000:FFFE
  179. to ascertain whether the system is an AT-class machine (16 IRQ lines)
  180. or an older, PC-type machine (8 IRQ lines).  The program next attempts
  181. to read FINDIRQ.DAT, which exists if FINDIRQ has previously been executed
  182. as a TSR.  The file FINDIRQ.DAT contains 2 bytes (16 bits) that show the
  183. IRQs that have at some point been enabled.
  184.  
  185.     After checking the data file, the PIC mask registers are read to
  186. determine which IRQs are currently enabled.  FINDIRQ maintains its own
  187. copies of the PIC mask registers and sets the bits appropriately as it
  188. learns about the devices present on the system.
  189.  
  190.     IRQs assigned to COM and LPT devices may not be enabled if the
  191. devices are not being used.  The INT 11h equipment check call is used
  192. to determine the number of COM and LPT ports available, and the IRQs
  193. corresponding to these are recorded in the FINDIRQ PIC mask variables.
  194. If a mouse is present and its driver is loaded, determining its IRQ is
  195. easy:  Software interrupt 33h, function 24h returns information about
  196. the type of mouse and its IRQ assignment. For PS/2 mice, the IRQ is not
  197. returned, but the IRQ assignment is always IRQ12.
  198.  
  199.     If an IRQ is enabled and has a standard system assignment, FINDIRQ
  200. can easily display the description for the device.  For example, if IRQ6
  201. is enabled, the utility knows that the device description is ``Floppy.''
  202. For optional devices, FIND IRQ displays the name of the device as it is
  203. specified by the device driver (for character devices) or the name of
  204. the controlling program (in the case of a TSR). In order to do this,
  205. FINDIRQ must locate these items in memory.
  206.  
  207. FINDING THE DRIVER OR TSR
  208.  
  209.      The first step in finding a device driver or TSR program is to look
  210. at the interrupt vector corresponding to the IRQ. For device drivers,
  211. if stacks have been disabled (that is, if STACKS  0,0 has been entered
  212. in the CONFIG.SYS file), the vector will provide the address directly.
  213. Since most systems have stacks enabled (the default is STACKS 9,128 on
  214. AT-class machines), however, the search must usually continue.
  215.  
  216.     When stacks are enabled, DOS intercepts hardware interrupts, and
  217. when interrupts occur, DOS sets up a stack from its pool of stacks
  218. before dispatching them to the original interrupt handler. This ensures
  219. that stack space will not be exhausted during  hardware interrupt
  220. servicing.  Figure 3 (below) shows the techniques  used to identify the
  221. code segment of the DOS STACKS interrupt  handlers.  Also shown in
  222. Figure 3 is the way FINDIRQ inspects the  code in those handlers as it
  223. looks for the original interrupt  service routine (ISR) address.
  224.  
  225.     For later versions of DOS, FINDIRQ takes advantage of the  memory
  226. control block's subsegment control blocks (these only exist in DOS 4.0
  227. and later).  FINDIRQ uses the undocumented ``list of lists,'' DOS
  228. function 52h, to  establish a pointer to the memory control block (MCB)
  229. chain. Using  the subsegment control blocks within the MCBs, the STACKS
  230. entry can be located, yielding the segment of the STACKS area. (For more
  231. information, see the June 12, 1990, Utilities column.)
  232.  
  233. FINDING THE NAME
  234.  
  235.      Once the ISR address is determined, FINDIRQ searches the DOS device
  236. driver chain for an entry whose code segment matches that of the
  237. interrupt handler.  Again, the ``list of lists'' DOS function is
  238. utilized, this time to retrieve the pointer to the DOS driver chain.
  239. Andrew Schulman's text, Undocumented DOS (Addison Wesley, 1990), contains
  240. an excellent discussion of the requisite techniques.  If a match is found
  241. in the driver chain and if the driver is a character device driver,
  242. FINDIRQ copies the name of the device contained in the device driver
  243. header into its description area.  For block devices, which are referenced
  244. by drive letters instead of device names, ``Block Device'' is used as
  245. the description.  If the ISR address does not belong to a device driver,
  246. it presumably belongs to a TSR.  Thus the next step is to search the MCB
  247. chain for entries that point to executable code.
  248.  
  249.     The procedures used for searching the driver and MCB chains are
  250. listed in Figure 4 (below).  All programs loaded by DOS begin with a
  251. program segment prefix (PSP), the information that includes a pointer
  252. to the program's copy of the environment.  The job at hand is to locate
  253. the PSP for the TSR.
  254.  
  255.     The 16-byte MCB immediately precedes the block of memory it
  256. describes.  Within each MCB is one field that contains the PSP segment
  257. of the program that owns the memory block and another field that tells
  258. how large the block is. If the block of memory described by the MCB is
  259. also the owner, then the block contains a program; it's not an
  260. environment block or another allocated chunk of memory.  If the ISR
  261. address falls in the block of memory (as defined by its size), then the
  262. TSR is found. Once the TSR is located, the PSP's pointer to the
  263. environment is used to retrieve the program name.  With DOS 3.0 and
  264. later, the program name is stored in the last portion of the environment
  265. block.
  266.  
  267.     One complication that can arise here is that the environment may
  268. have been freed by the TSR.  In such a case, the address space may
  269. actually contain the environment for another program.  To ensure that
  270. it has the right environment, FIND IRQ compares the owner field of the
  271. MCB for the environment with the PSP segment.  If these are different,
  272. the environment has been freed.  Even in such a case, however, the
  273. program name can sometimes still be retrieved; DOS versions 4.0 and
  274. later place the program name in the MCB itself.  If all else fails,
  275. FINDIRQ uses ``Other'' for the description of the program that handles
  276. the interrupt.  IRQ usage that can only be detected with FINDIRQ's TSR
  277. component will usually list ``Other'' as its description.
  278.  
  279. TSR APPROACH
  280.  
  281.      When FINDIRQ is invoked with the TSR option, it becomes resident in
  282. the system and monitors the enabling of IRQs in the 8259 PIC interrupt
  283. mask registers.  The TSR component of FINDIRQ chains into the user timer
  284. interrupt (INT 1Ch), and the PIC mask registers are read approximately
  285. once a second.  When a new IRQ is enabled, FINDIRQ sets a flag so that
  286. it can update its .DAT file when it is safe to do so.  Interrupt 28h,
  287. the DOS idle handler, provides the means of detecting the period in which
  288. it is safe to use DOS functions from within a TSR (see ``The Inner Life
  289. of a TSR,'' PC Magazine, August 1990).  As mentioned earlier, this data
  290. file is read by FINDIRQ when it is invoked to display IRQ status
  291. information.
  292.  
  293. SUMMARY
  294.  
  295.      Adding new devices to your system can be a challenge.  The difficulties
  296. are sufficient to have prompted Microsoft to spearhead development of the
  297. Plug and Play Industry Standard Architecture Specification which may
  298. someday make manual configuration obsolete. In fact, IBM's MCA architecture
  299. already supports ``plug and play,'' though it is not in wide use.  But
  300. until automatic configuration becomes a standard, the next time you need
  301. to add a peripheral that uses an IRQ, fire up FINDIRQ and make your IRQ
  302. selection with ease.
  303. -----------------------------------------------------------------------------
  304. RICK KNOBLAUGH IS A FREQUENT CONTRIBUTOR TO PC MAGAZINE.
  305. -----------------------------------------------------------------------------
  306.                          QUICK REFERENCE GUIDE
  307.  
  308. FINDIRQ
  309.  
  310. Rick Knoblaugh                            September 28, 1993 (Utilities)
  311.  
  312.  
  313. Purpose:
  314.  
  315.         Reports which interrupt request lines (IRQs) in a system are
  316. assigned to specific devices and which are free to use for installing
  317. a new peripheral device.
  318.  
  319. Format:
  320.                      [LH] FINDIRQ [/t] [/i] [/u] [/d] [/h]
  321.  
  322. Remarks:
  323.  
  324.         Executed from a DOS prompt and without any of its optional
  325. parameters, FINDIRQ identifies installed standard (AT) IRQ devices and
  326. optional character devices by device name.  Optional block devices are
  327. designated by ``Block Device.''  TSR-controlled devices are identified
  328. by their program names.
  329.  
  330.     The command FINDIRQ will detect devices controlled by TSR programs
  331. that are loaded in conventional memory.  Devices controlled by TSRs
  332. loaded in high memory require entering the command as LH FINDIRQ.
  333. Since this latter command form also detects conventionally sited TSRs,
  334. LH FINDIRQ should be used on systems that use high memory.
  335.  
  336.     Peripherals such as sound boards, whose IRQ numbers are enabled only
  337. when the devices are active, can be detected only by activating them
  338. while FINDIRQ is memory-resident.  The command FINDIRQ /t installs the
  339. utility as a TSR; FINDIRQ /u uninstalls it.  While memory-resident,
  340. FINDIRQ records IRQs in the file  FINDIRQ.DAT, which is read before
  341. displaying its on-screen report.  Since any configuration changes render
  342. the previous contents of FINDIRQ.DAT obsolete, the data file can be
  343. reinitialized by entering FINDIRQ /i.
  344.  
  345.     FINDIRQ verifies its interrupt information before presenting it.
  346. In some circumstances, however, such as when an IRQ is enabled under
  347. Windows but no DOS interrupt handler is located, no verification is
  348. possible, so the interrupt will appear to be free when it is not.
  349. To check for such a possibility, the verification requirement can be
  350. turned off with the command FINDIRQ /d.
  351.  
  352.     Finally, a help screen can be displayed by entering FINDIRQ /h.
  353. A Microsoft MASM assembler (or compatible), LINK, and EXE2BIN are required
  354. if the program is to be rebuilt.
  355. ---------------------------------------------------------------------------
  356.                         Standard IRQ Assignments
  357.  
  358.                                  _________________       _________________
  359. Timer                     IRQ0   |               |       |               |
  360. Keyboard                  IRQ1   |               |       |               |
  361.                           IRQ2   |   Master      | INTR  |               |
  362. Serial port2             -IRQ3---|    8259       |-------|     CPU       |
  363. Serial port1            | IRQ4   | programmable  |       |               |
  364. Parallel port 2         | IRQ5   |  Interrupt    |       |               |
  365. Floppy disk controller  | IRQ6   |  controller   |       |               |
  366. Parallel                | IRQ7   |_______________|       |_______________|
  367.                         |
  368.                         |_________________________________________
  369.                                                                  |
  370.                                  _________________               |
  371. Real-time clock           IRQ8   |               |               |
  372.                           IRQ9   |               |               |
  373.                           IRQ10  |    Slave      |---------------
  374.                           IRQ11  |    8259       |
  375. PS/2 mouse                IRQ12  | programmable  |
  376. Math coprocessor          IRQ13  |  Interrupt    |
  377. Hard disk controller      IRQ14  |  controller   |
  378.                           IRQ15  |_______________|
  379.  
  380. -----------------------------------------------------------------------------
  381. Figure 2: Shown here are the master and slave 8259 interrupt controllers and the standard IRQ assignments utilized in AT-class platforms.
  382. -----------------------------------------------------------------------------
  383.                            STACKS Routines
  384.  
  385. ;-------------------------------------------------------------|
  386. ;get_stacks_seg - Determine the segment where the DOS STACKS  |
  387. ;                 code resides.  This will be used later      |
  388. ;                 for tracing back through the STACKS         |
  389. ;                 interrupt handlers to find the original     |
  390. ;                 ISRs.                                       |
  391. ;                                                             |
  392. ;             Enter:                                          |
  393. ;                      ds = local data segment                |
  394. ;                atclassf = TRUE if AT class machine          |
  395. ;                 dos_ver = DOS minor ver/major ver           |
  396. ;                                                             |
  397. ;              Exit:                                          |
  398. ;                                                             |
  399. ;         dos_handler_seg = segment for DOS STACKS.           |
  400. ;                                                             |
  401. ;  ds preserved.                                              |
  402. ;-------------------------------------------------------------|
  403. get_stacks_seg  proc    near
  404.                 push    ds
  405.                 xor     bx, bx
  406.                 mov     ds, bx                  ;vectors
  407.                 assume  ds:nothing
  408. ;
  409. ;Use the segment found in the floppy handler as our default guess of
  410. ;where the STACKS code is.
  411. ;
  412. ;
  413.                 mov     bx, ((pcflop + BEG_INTS) shl 2) ;vector for IRQ6
  414.                 mov     ax, [bx].d_segment
  415.  
  416.                 cmp     byte ptr cs:dos_ver, 4  ;DOS 4 or better?
  417.                 jb      get_stacks800
  418. ;
  419. ;If DOS 4 or better, memory control block, subsegment control blocks
  420. ;can be used to find STACKS. 
  421. ;
  422.                 mov     ah, DOS_LIST_LISTS
  423.                 int     21h
  424.                 mov     dx, es:[bx - 2]         ;segment of first MCB
  425.                 mov     ds, dx
  426.                 mov     cx, dx                  ;save it
  427.                 xor     bx, bx
  428.                 cmp     [bx].mcb_sig, 'M'       ;valid MCB?
  429.                 jne     get_stacks800           ;MCBs corrupted
  430.  
  431.                 mov     di, [bx].mcb_size       ;get first size
  432.                 inc     di                      ;add para for the MCB
  433.                 add     dx, di                  ;after DOS data seg 
  434.  
  435.                 inc     cx                      ;1st subsegment block
  436. get_stacks300:
  437.                 cmp     cx, dx                  ;at end of info?
  438.                 jae     get_stacks800           ;if so, search done
  439.                 mov     ds, cx                  ;load the segment
  440.                 cmp     [bx].mcb_sig, 'S'       ;STACKS?
  441.                 je      get_stacks500
  442.                 mov     cx, [bx].mcb_owner   
  443.                 add     cx, [bx].mcb_size       ;advance to next block
  444.                 jmp     short get_stacks300
  445.  
  446. get_stacks500:
  447.                 mov     ax, [bx].mcb_owner
  448. get_stacks800:
  449.                 mov     cs:dos_handler_seg,  ax    ;save it
  450. get_stacks900:
  451.                 pop     ds
  452.                 assume  ds:code
  453.                 ret
  454. get_stacks_seg  endp
  455.  
  456. ;-------------------------------------------------------------|
  457. ;ck_for_dos - Given a ptr to an interrupt vector, determine if|
  458. ;             the handler is one of those inserted by DOS     |
  459. ;             to first check stack before dispatching to      |
  460. ;             original ISR.  If it is, attempt to get the     |
  461. ;             address of the original ISR.                    |
  462. ;                                                             |
  463. ;             Enter:                                          |
  464. ;                      ds = local data segment                |
  465. ;                   es:bx = ptr to vector                     |
  466. ;         dos_handler_seg = segment of the DOS handlers       |
  467. ;                                                             |
  468. ;              Exit:                                          |
  469. ;                                                             |
  470. ;                Carry set if no DOS handler present or       |
  471. ;                          can't find original ISR.           |
  472. ;                                                             |
  473. ;             else  es:bx = ptr to original ISR               |
  474. ;                                                             |
  475. ;  All other registers preserved.                             |
  476. ;--------------------------------------------------------------|
  477. ck_for_dos      proc    near
  478.                 push    ax
  479.                 push    cx
  480.                 push    di
  481.  
  482.                 mov     ax, dos_handler_seg
  483.                 cmp     es:[bx].d_segment, ax   ;same seg STACKS?
  484.                 jne     ck_for_900
  485.                 les     di, es:[bx]             ;es:di = &handler
  486.                 mov     al, es:[di]             ;get byte of code
  487.  
  488.  
  489. ;
  490. ;Several different flavors of these stack handlers are used in the
  491. ;various iterations of DOS. Look for the following cases:
  492. ;
  493. ;case 1:             jmp  label
  494. ;                    dd   &old_isr
  495. ;
  496. ;            label:  call handler   ;put offset of &ISR on stack
  497. ;                    dw  offset var ;and go get it and call it
  498. ;
  499. ;
  500. ;case 2:             call handler   ;put offset of &ISR on stack
  501. ;                    dd   &old_isr
  502. ;
  503. ;
  504. ;case 3:             jmp  label
  505. ;                    dd   &old_isr
  506. ;
  507. ;             label:  ...
  508. ;
  509. ;                    pushf
  510. ;                    cs:
  511. ;                    call far [xxxx] ;also this it may not follow the
  512. ;                                    ;jmp
  513.  
  514. ;case 4:             push ax
  515. ;                    ... 
  516. ;                    ...
  517. ;
  518. ;                    pushf
  519. ;                    cs:
  520. ;                    call far [xxxx]   ;xxxx is the offset of the
  521. ;                                      ;variable that holds the &ISR
  522.  
  523.                 cmp     al, OP_JMP      ;is it a jmp
  524.                 jne     ck_for_200
  525.                 inc     di
  526.                 sub     ah, ah
  527.                 mov     al, es:[di]     ;get next type of code
  528.                 inc     di              ;past 2nd byte of rel jmp
  529.                 add     di, ax          ;get to location of jmp
  530.  
  531.                 mov     al, es:[di]     ;get byte of code there
  532. ck_for_200:
  533.                 cmp     al, OP_CALL     ;is it a call
  534.                 jne     ck_for_300
  535.                 mov     di, es:[di + 3] ;offset to dd that holds &ISR
  536.                 les     bx, es:[di]     ;&ISR
  537.                 jmp     short ck_for_999 ;exit with carry clear
  538.  
  539. ck_for_300:
  540.                 mov     cx, BYTES_TO_SEARCH
  541. ck_for_400:
  542.                 mov     al, OP_PUSHF
  543.                 repne   scasb           ;pushf instruction?
  544.                 jcxz    ck_for_900
  545.                 mov     al, OP_CS       ;CS override?
  546.                 scasb
  547.                 jnz     ck_for_400
  548.                 mov     ax, OP_CALL_MEM16
  549.                 scasw                   ;call?
  550.                 jnz     ck_for_400
  551.                 mov     di, es:[di]     ;offset of var that holds &ISR
  552.                 les     bx, es:[di]     ;get &ISR
  553.  
  554. ck_for_800:
  555.                 jmp     short ck_for_999 ;exit with carry clear
  556. ck_for_900:
  557.                 stc
  558. ck_for_999:
  559.                 pop     di
  560.                 pop     cx
  561.                 pop     ax
  562.                 ret
  563. ck_for_dos      endp
  564.  
  565. -----------------------------------------------------------------------
  566. Figure 3: These routines identify the segment where the DOS STACKS
  567. interrupt handlers reside and retrieve the original interrupt service
  568. routine address.
  569. -------------------------------------------------------------------------
  570.                              Search Procedures
  571.  
  572. ;-------------------------------------------------------------|
  573. ;find_drivers - For all IRQs that are active, look to see if  |
  574. ;               we have a description for the device.  If we  |
  575. ;               don't, the device using this IRQ must be      |
  576. ;               something we don't know about, such as a      |
  577. ;               sound board, SCSI adapter, etc.  In this case,|
  578. ;               go get the driver name or program name and    |
  579. ;               put it in the description for the IRQ.        |
  580. ;                                                             |
  581. ;               In the event that a driver or TSR (with       |
  582. ;               PSP) is not found, assign "Other" for the     |
  583. ;               device description.                           |
  584. ;                                                             |
  585. ;                                                             |
  586. ;          Enter:                                             |
  587. ;                  ds=local data segment                      |
  588. ;                atclassf = TRUE if AT class machine          |
  589. ;                                                             |
  590. ;                pic_mask = PIC mask values.                  |
  591. ;                                                             |
  592. ;          device_desc = offset to array of description       |
  593. ;                        offsets.                             |
  594. ;                                                             |
  595. ;           Exit:                                             |
  596. ;                                                             |
  597. ;  ds, bp preserved, es trashed.                              |
  598. ;--------------------------------------------------------------|
  599. find_drivers    proc    near
  600.                 push    bp
  601.                 mov     ah, DOS_LIST_LISTS
  602.                 int     21h             ;get es:bx ptr to lists
  603.                 
  604.                 cmp     dos_ver, 0003h  ;DOS 3.0?
  605.                 jne     find_drv_050
  606.                 lea     bx, es:[bx].nul_dev_head30 ;ptr to NUL device 
  607.                 jmp     short find_Drv_080
  608. find_drv_050:
  609.                 lea     bx, es:[bx].nul_dev_head   ;ptr to NUL device
  610. find_drv_080:
  611.                 mov     ax, es
  612.                 mov     drv_ptr.d_segment, ax   ;save ptr to start
  613.                 mov     drv_ptr.d_offset, bx    ;of driver chain
  614.  
  615.                 xor     al, al                  ;IRQ counter
  616.                 mov     bp, pic_mask
  617.                 mov     cx, 16                  ;16 IRQs on AT
  618.                 cmp     atclassf, TRUE          ;AT machine?
  619.                 je      find_drv_100
  620.                 shr     cx, 1                   ;only 8 IRQs on PC
  621. find_drv_100:
  622.                 shr     bp, 1                   ;check IRQ
  623.                 jc      find_drv_600            ;skip if not active
  624. find_drv_110:
  625.                 call    get_desc_entry          ;description for IRQ
  626.                 xor     bx, bx
  627.                 cmp     [di], bx                ;is there one?
  628.                 je      find_drv_125
  629. ;
  630. ;If there is already a description for the device, don't need to look
  631. ;for driver.  However, there is one exception.  If we are on an AT, 
  632. ;we always assign an initial description of "cascade" to IRQ2.  If 
  633. ;that is the only description we have (e.g. we haven't got a mouse on
  634. ;IRQ2, etc.), go ahead and look for a driver using IRQ2.
  635. ;
  636.  
  637.                 cmp     atclassf, TRUE          ;AT machine?
  638.                 jne     find_drv_600            ;if not, no cascade
  639.                 cmp     al, 2                   ;doing IRQ2?
  640.                 jne     find_drv_600            ;if not, no test
  641.                 cmp     [di + 2], bx            ;check 2nd desc (bx=0)
  642.                 jne     find_drv_600
  643.                 add     di, 2                   ;next desc entry
  644.  
  645. find_drv_125:
  646.                 mov     es, bx                  ;point to vector
  647.  
  648.                 mov     bl, al                  ;irq number
  649.                 cmp     al, 8
  650.                 jb      find_drv_150
  651.                 add     bl, (BEG_SLAVE_INTS  - 8)
  652.                 jmp     short find_drv_175
  653.  
  654. find_drv_150:
  655.                 add     bl, BEG_INTS
  656. find_drv_175:
  657.                 shl     bx, 1
  658.                 shl     bx, 1                   ;offset into vectors
  659. ;See if pointing to DOS stack handlers.  If so, carry is not set and
  660. ;es:bx points to address of original ISR.
  661. ;
  662.                 call    ck_for_dos
  663.                 jnc     find_drv_200            ;es:bx point to ISR
  664.  
  665.                 les     bx, es:[bx]             ;point to ISR
  666.  
  667. find_drv_200:
  668.                 call    load_drv_desc
  669. find_drv_600:
  670.                 inc     al                      ;advance IRQ counter
  671.                 loop    find_drv_100
  672. find_drv_900:
  673.                 pop     bp
  674.                 ret
  675. find_drivers    endp
  676.  
  677.  
  678.  
  679.  
  680. ;-------------------------------------------------------------|
  681. ;load_drv_desc - Retrieve and load driver description.        |
  682. ;                                                             |
  683. ;          Enter:                                             |
  684. ;                  ds=local data segment                      |
  685. ;                  di=offset of entry for description         |
  686. ;               es:bx=ptr to ISR for which description is     |
  687. ;                     needed.                                 |
  688. ;        device_desc = offset to array of description         |
  689. ;                      offsets.                               |
  690. ;       drv_desc_ptr = offset of next work description area.  |
  691. ;                                                             |
  692. ;           Exit:                                             |
  693. ;                 [di]=offset of description.                 |
  694. ;         drv_desc_ptr advanced to next work description area.|
  695. ;                                                             |
  696. ;  all registers saved.                                       |
  697. ;--------------------------------------------------------------|
  698. load_drv_desc   proc    near
  699.                 push    ax              ;save picmask/IRQ counter
  700.                 push    cx              ;save loop count
  701.                 push    di              ;save offset of desc entry
  702.  
  703.                 push    bx                      ;save offset
  704.                 mov     dx, es                  ;address in dx:bx
  705.                 call    is_it_driver            ;see if driver or TSR
  706.                 pop     dx                      ;get back offset (bx)
  707.                 jnc     load_drv_300            ;jmp if not a driver
  708.                 mov     dx, offset block_desc   ;"Block Device"
  709.                 cmp     al, 2                   ;IRQ2? if so, may have
  710.                 jne     load_drv_290            ;"Cascade" if AT - use
  711.                 mov     dx, offset block_abbrv  ;abbrev desc to fit
  712.  
  713. load_drv_290:
  714.                 test    es:[bx].dev_attrib, 8000h ;char driver?
  715.                 jz      load_drv_550     ;if not, use "Block Device"
  716.                 call    mov_char_name    ;if so, move char device name
  717.                 jmp     short load_drv_450
  718. load_drv_300:
  719. ;
  720. ;Wasn't a driver.  Must be a TSR.  Using the pointer to environment
  721. ;from the TSR's PSP, find the name of executable.
  722. ;
  723. ;
  724.                 mov     bx, dx                  ;offset of ISR
  725.                 call    determine_psp           ;return nc if PSP found
  726. ;
  727. ;If a PSP is found, carry will NOT be set.  Also, if PSP is found, and
  728. ;ax is nonzero, ax holds the segment of the environment.  If ax is
  729. ;zero then the environment was freed.  If such cases, es:bx will be
  730. ;returned as a pointer to the filename description in the MCB
  731. ; (if DOS is 4.x or better - otherwise bx=0)
  732. ;
  733.                 jnc     load_drv_350
  734. load_drv_330:
  735.                 mov     dx, offset other_desc   ;can't get filename
  736.                 jmp     short load_drv_550      ;so, go assign "Other"
  737. load_drv_350:
  738.                 or      ax, ax                  ;environment found?
  739.                 jnz     load_drv_370            ;if so, get filename
  740.                 or      bx, bx                  ;name in MCB?
  741.                 jz      load_drv_330            ;if not, print "Other"
  742.                 jmp     short load_drv_400      ;go get name
  743. load_drv_370:
  744.                 mov     es, ax
  745.                 call    retrieve_path           ;get ptr to filename
  746.                 jc      load_drv_330            ;if data not right
  747. load_drv_400:
  748.                 mov     di, drv_desc_ptr
  749.                 mov     dx, di                  ;save desc offset
  750.                 call    mov_file_name
  751. load_drv_450:
  752.                 mov     al, STR_TERM
  753.                 stosb                           ;terminate it
  754.                 add     drv_desc_ptr, size drv_descrip
  755. load_drv_550:
  756.                 pop     di
  757.                 pop     cx
  758.                 pop     ax
  759.                 mov     [di], dx                ;store offset of name
  760.                 ret
  761. load_drv_desc   endp
  762.  
  763.  
  764.  
  765. ;-------------------------------------------------------------|
  766. ;determine_psp - An ISR was found and it is not part of a     |
  767. ;                device driver.  Search the MCB chain for     |
  768. ;                PSP associated with code that would contain  |
  769. ;                the ISR.  If found, check the environment    |
  770. ;                entry for the PSP.  If environment has not   |
  771. ;                been freed, return ax=environment segment. If|
  772. ;                not found and DOS is 4.x or better, return   |
  773. ;                es:bx as ptr to filename in MCB.             |
  774. ;                                                             |
  775. ;                                                             |
  776. ;          Enter:                                             |
  777. ;                  es:bx = ptr to ISR                         |
  778. ;                                                             |
  779. ;           Exit:                                             |
  780. ;                     cy set if PSP NOT found else nc         |
  781. ;                     also if found ax=environment seg (zero  |
  782. ;                                      if it was freed)       |
  783. ;                                                             |
  784. ;                          if found and ax=0, es:bx=ptr       |
  785. ;                                       to filename in MCB    |
  786. ;                                       (zero if < DOS 4)     |
  787. ;                                                             |
  788. ;   ds preserved.                                             |
  789. ;-------------------------------------------------------------|
  790. determine_psp   proc    near
  791.                 push    ds
  792.                 mov     dx, es                  ;segment of ISR
  793.                 mov     cl, 4
  794.                 shr     bx, cl                  ;convert to paragraphs
  795.                 add     dx, bx                  ;para location of code
  796.  
  797.                 mov     ah, DOS_LIST_LISTS
  798.                 int     21h
  799.                 mov     di, es:[bx - 2]         ;segment of first MCB
  800.                 mov     es, di
  801.                 xor     bx, bx                  ;offset zero in MCB
  802.                 mov     si, es                  ;save MCB segment
  803. determine_p200:
  804.                 inc     si                      ;ready for next one
  805.  
  806.                 cmp     es:[bx].mcb_sig, 'Z'    ;at end of chain?
  807.                 je      determine_p900          ;if so, not found
  808.                 cmp     es:[bx].mcb_sig, 'M'    ;valid entry?
  809.                 jne     determine_p900          ;if not, MCBs wrong
  810.  
  811. determine_p300:
  812.                 mov     cx, es:[bx].mcb_owner   ;get MCB owner seg
  813.                 mov     di, cx                  ;save it
  814.                 mov     ax, es:[bx].mcb_size    ;and its size
  815.                 cmp     cx, si                  ;is this a PSP?
  816.                 jne     determine_p700          ;if not, go next PSP
  817.  
  818.                 cmp     dx, cx                  ;compare segment
  819.                 jb      determine_p700          ;beneath the segment
  820.                 add     cx, ax                  ;get end of the area
  821.                 cmp     dx, cx
  822.                 ja      determine_p700          ;above the segment
  823.  
  824.                 assume  ds:nothing
  825.                 mov     ds, di                  ;ds=seg of owner
  826.                 mov     ax, [bx].psp_environ_seg
  827.  
  828.                 or      ax, ax                  ;freed environment?
  829.                 jz      determine_p400          ;if so, try plan b
  830.  
  831.                 dec     ax                      ;1 paragraph back
  832.                 mov     ds, ax                  ;is seg of MCB for it
  833.                 inc     ax                      ;back to "environment"
  834.                 cmp     [bx].mcb_owner, di      ;belongs to PSP?
  835.                 je      determine_p999          ;yes, exit nc, ax=seg
  836.  
  837. determine_p400:
  838.                 xor     ax, ax                  ;indicate none
  839.                 cmp     byte ptr cs:dos_ver, 4  ;DOS 4 or better?
  840. ;
  841. ;if yes, we can return filename
  842. ;if not, bx=0 indicating must print "Other"
  843. ;
  844.                 cmc                             ;carry clear if < DOS 4
  845.                 jnb     determine_p999          ;nc and bx=0 if < DOS 4
  846.                                                 
  847. determine_p500:
  848.                 clc
  849.                 lea     bx, es:[bx].mcb_fname 
  850.                 jmp     short determine_p999    ;nc, es:bx = &filename
  851.  
  852. determine_p700:
  853.                 add     si, ax                  ;get to next MCB
  854.                 mov     es, si
  855.                 jmp     short determine_p200
  856.  
  857. determine_p900:
  858.                 stc
  859. determine_p999:
  860.                 assume  ds:code
  861.                 pop     ds
  862.                 ret
  863. determine_psp   endp
  864. ---------------------------------------------------------------------------
  865. Figure 4:  The procedures listed above are used to search the driver and
  866. MCB chains.
  867. ---------------------------------------------------------------------------
  868. «MDUL»«MDNM»