home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_02_10 / 2n10062a < prev    next >
Text File  |  1991-07-17  |  17KB  |  617 lines

  1. COMMENT ~
  2. ---------------------------------------------------------
  3.                         LISTING 1
  4.  File:  breakout.asm
  5.  
  6.  This module contains routines necessary to enable
  7.  Ctrl-Break to "break out" of infinite loops.  A user
  8.  supplied clean up routine may be specified if necessary.
  9.  
  10.  MASM 5.1/TASM 2.0 - C callable installation &
  11.  de-installation functions.
  12.  
  13.  Language & memory model independant.  Change the .model
  14.  directive as needed.
  15.  
  16.  Author:  David Burki
  17. ---------------------------------------------------------
  18. END OF COMMENT ~
  19.  
  20. title BREAKOUT.ASM
  21. page 57,132
  22.  
  23. %  .model m_model, lang
  24.  
  25. ; ----  MACROS and EQUATES  ----
  26.  
  27. ;macro to simulate an interrupt so the interrupt returns
  28. ;to the instruction immediately following the call
  29. sim_int macro  num
  30.         pushf
  31.         call    cs:orig_&num
  32. endm
  33.  
  34. ;macro to push selected registers
  35. apush macro   a,b,c,d,e,f,g,h
  36.    irp x,<a,b,c,d,e,f,g,h>
  37.       ifnb <x>
  38.          push    x
  39.       endif
  40.    endm
  41. endm
  42.  
  43. ;macro to pop selected registers
  44. apop macro   a,b,c,d,e,f,g,h
  45.    irp x,<h,g,f,e,d,c,b,a>
  46.       ifnb <x>
  47.          pop     x
  48.       endif
  49.    endm
  50. endm
  51.  
  52. ;macro to install a replacement vector
  53. ;  --  assumes that ds = code seg of replacing function
  54. ;  --  all var names used to save original vector must be
  55. ;        2 characters long plus a training "h" (i.e. for
  56. ;        int 8h "orig_08h")
  57. install_vector macro vector_num,function_name
  58.         mov     ax,35&vector_num
  59.         int     21h
  60.         mov     word ptr orig_&vector_num,bx
  61.         mov     word ptr orig_&vector_num+2,es
  62.         lea     dx,function_name
  63.         mov     ax,25&vector_num
  64.         int     21h
  65. endm
  66.  
  67. ;macro to un-install a replaced vector
  68. ;  --  all var names used to save original vector must be
  69. ;        2 characters long plus a training "h" (i.e. for
  70. ;        int 8h "orig_08h")
  71. restore_vector macro replaced_vector
  72.         lds     dx,cs:orig_&replaced_vector
  73.         mov     ax,25&replaced_vector
  74.         int     21h
  75. endm
  76.  
  77. ; segment and offset of the BIOS break flag
  78. BIOS_SEG        equ     40h
  79. BREAK_FLAG_OFF  equ     71h
  80.  
  81. ; ----  CODE  ----
  82. .code
  83.  
  84. ; declare externally visible functions
  85. public  insure, cancel
  86.  
  87. ; NOTE:  all variables used here are part of the code
  88. ;        segment so the interrupt routines can have easy
  89. ;        access to them
  90.  
  91. ; --- storage for the address of the InDOS flag
  92. indos_addr              dd      0
  93.  
  94. ; --- storage for the address of the critical error flag
  95. critter_addr            dd      0
  96.  
  97. ; --- storage for the version of dos
  98. dos_major_version       db      0
  99. dos_minor_version       db      0
  100.  
  101. ; --- flag indicating disk i/o in progress
  102. busy_flag               db      0
  103.  
  104. ; --- far pointer to clean up routine
  105. exit_routine            dd      0
  106.  
  107. ; --- storage for original addresses of intercepted
  108. ;     interrupt vectors.
  109. orig_08h                dd      0
  110. orig_1bh                dd      0
  111. orig_13h                dd      0
  112. orig_25h                dd      0
  113. orig_26h                dd      0
  114.  
  115. ; --- storage for the segment addr of the psp of process
  116. ;     that asked for insurance  -  if zero, insurance isn't
  117. ;     in effect
  118. insured_psp     dw      0
  119.  
  120. ; --- flag if still processing in int 8h
  121. in_already      db      0
  122.  
  123. ; --- interrupted program's SS & SP
  124. save_ss         dw      0
  125. save_sp         dw      0
  126.  
  127. ; --- original C stack segment
  128. installer_ss    dw      0
  129. installer_sp    dw      0
  130.  
  131. ; --- local stack while in int_8h_handler
  132. my_stack        dw      256 dup(0)
  133. stack_top       label word
  134.  
  135.  
  136. assume ds:@curseg, es:nothing
  137.  
  138. ;---------------------------------------------------------
  139. ;  GET_DOS_VERSION()
  140. ; Obtain the version & revision of DOS.  Store the version
  141. ; in the variable "dos_major_version" and the revision in
  142. ; the variable "dos_minor_version".  Both variables are code
  143. ; segment variables.
  144. ;
  145. ; Returns:
  146. ;       AL - dos minor version number
  147. ;       AH - dos major version number
  148. ;---------------------------------------------------------
  149. public get_dos_version
  150. get_dos_version proc
  151.         mov     ah,30h
  152.         int     21h
  153.         mov     cs:dos_major_version,al
  154.         mov     cs:dos_minor_version,ah
  155.         ret
  156. get_dos_version endp
  157.  
  158. ;---------------------------------------------------------
  159. ;  GET_CRITICAL_ERROR_ADDR()
  160. ; This function obtains & saves the address of the
  161. ; critical error flag.  For DOS versions 3+, the critical
  162. ; error flag is the byte just before the InDOS flag. For
  163. ; DOS 2.x, it's the byte just after.
  164. ;
  165. ; NOTE:  get_dos_version() must be called before this
  166. ;        function
  167. ;
  168. ; NOTE:  For COMPAQ DOS version 3.x, the critical error
  169. ;        flag is located 01aah bytes BEFORE the InDOS flag.
  170. ;
  171. ; Trashes: AX
  172. ;---------------------------------------------------------
  173. public get_critical_error_addr
  174. get_critical_error_addr proc    USES ES BX
  175. ; --- get the address of the InDOS flag
  176.         mov     ah,34h
  177.         int     21h
  178.  
  179. ; --- dos 3.x or better use byte before InDOS
  180.         cmp     byte ptr cs:dos_major_version,3
  181.         jge     byte_before  ;good_version
  182.  
  183. ; --- dos 2.x, use byte after InDOS flag
  184.         inc     bx
  185.         jmp     store_critter_addr
  186.  
  187. byte_before:
  188.         dec     bx
  189.  
  190. store_critter_addr:
  191. ; --- save the address & return success
  192.         mov     word ptr cs:critter_addr,bx
  193.         mov     word ptr cs:critter_addr+2,es
  194.  
  195.         ret
  196. get_critical_error_addr endp
  197.  
  198. ;---------------------------------------------------------
  199. ;  GET_INDOS_ADDR()
  200. ; Uses the undocumented (but well known) function 34h of
  201. ; INT 21h to obtain a far pointer to the "InDOS" flag.
  202. ; Stores the address of the flag in the code segment
  203. ; variable "indos_addr".
  204. ;
  205. ; Trashes AX
  206. ;---------------------------------------------------------
  207. public get_indos_addr
  208. get_indos_addr proc     USES ES BX
  209. ; --- get the address
  210.         mov     ah,34h
  211.         int     21h
  212.  
  213. ; --- save address in cs referenced variable
  214.         mov     word ptr cs:indos_addr,bx
  215.         mov     word ptr cs:indos_addr+2,es
  216.         ret
  217. get_indos_addr endp
  218.  
  219. ;---------------------------------------------------------
  220. ;  CHECK_CRITTER_FLAG()
  221. ; Examine the undocumented "critical error" flag to
  222. ; determine if a critical error is in progress.
  223. ;
  224. ; Returns:
  225. ;     carry clear - A critical error is NOT in progress
  226. ;     carry set   - A critical error is in progress
  227. ;---------------------------------------------------------
  228. public check_critter_flag
  229. check_critter_flag proc USES AX DS SI
  230. ; --- ds:si <- address of critter flag
  231.         lds     si,dword ptr cs:critter_addr
  232.  
  233. ; --- check critter flag = zero
  234.         lodsb
  235.         or      al,al
  236.         jz      no_critter
  237.  
  238. ; --- critter flag not zero, return carry set
  239.         stc
  240.         jmp     critter_exit
  241.  
  242. ; --- critter flag zero, return carry clear
  243. no_critter:
  244.         clc
  245.  
  246. critter_exit:
  247.         ret
  248. check_critter_flag endp
  249.  
  250. ;---------------------------------------------------------
  251. ;  CHECK_INDOS_FLAG()
  252. ; Examine the undocumented "InDOS" flag to determine if
  253. ; non-rentrant DOS functions are currently executing.
  254. ;
  255. ; Returns:
  256. ;     carry clear - InDOS flag is zero.
  257. ;     carry set   - InDOS flag is non-zero.
  258. ;---------------------------------------------------------
  259. public check_indos_flag
  260. check_indos_flag proc   USES AX DS SI
  261. ; --- ds:si <-- addr of InDOS flag, then load al with
  262. ;     the byte at that address
  263.         lds     si,dword ptr cs:indos_addr
  264.         lodsb
  265.  
  266. ; --- indos flag equal zero, DOS is stable
  267.         or      al,al
  268.         jz      dos_stable
  269.  
  270. ; --- no, set carry & return
  271.         stc
  272.         jmp     indos_exit
  273.  
  274. ; --- yes, clear carry and return
  275. dos_stable:
  276.         clc
  277.  
  278. indos_exit:
  279.         ret
  280. check_indos_flag endp
  281.  
  282.  
  283. ;---------------------------------------------------------
  284. ;  CHECK_BREAK_BIT()
  285. ;   This function examines the BIOS Break Flag (bit 7 of
  286. ;   the byte at 40:71).
  287. ;
  288. ; Returns:
  289. ;     carry clear - Break Flag is clear.
  290. ;     carry set   - Break Flag is set.
  291. ;---------------------------------------------------------
  292. check_break_bit proc    USES DS ES BX
  293. ; --- establish addressing to the flag
  294.         mov     bx,BIOS_SEG
  295.         mov     es,bx
  296.         mov     bx,BREAK_FLAG_OFF
  297.  
  298. ; --- is bit 7 set
  299.         test    es:byte ptr [bx],10000000b
  300.         jnz     break_bit_set
  301.  
  302. ; --- no, return with carry clear
  303.         clc
  304.         jmp     check_break_exit
  305.  
  306. ; --- yes, clear it & return carry set
  307. break_bit_set:
  308.         and     es:byte ptr [bx],01111111b
  309.         stc
  310.  
  311. check_break_exit:
  312.         ret
  313. check_break_bit endp
  314.  
  315. ;---------------------------------------------------------
  316. ; INSTALL_INTERCEPTS()
  317. ;   Retrieve & save the current vectors, replacing the
  318. ;   vector table entries with the addresses of the
  319. ;   intercept routines.
  320.  
  321. ; Entry:
  322. ;   DS = CS
  323. ; Returns:
  324. ;   No value
  325. ; Trashes:
  326. ;   AX, BX, DX, ES
  327. ;---------------------------------------------------------
  328. install_intercepts proc
  329. assume ds:@curseg
  330. ; --- capture int 8h vector
  331.         install_vector 08h,int_8h_handler
  332.  
  333. ; --- capture int 13h vector
  334.         install_vector 13h,thirteen_handler
  335.  
  336. ; --- capture int 25h vector
  337.         install_vector 25h,twenty_five_handler
  338.  
  339. ; --- capture int 26h vector
  340.         install_vector 26h,twenty_six_handler
  341.  
  342. ; --- capture int 1bh vector
  343.         install_vector 1bh,break_handler
  344.  
  345.         ret
  346. install_intercepts endp
  347.  
  348.  
  349. ;---------------------------------------------------------
  350. ; INSURE( cleanup_function )
  351. ;   void (*PFV)();  /* far pointer to cleanup function */
  352. ;
  353. ;   Installs the breakout tool.
  354. ;
  355. ; Returns:
  356. ;       AX = zero    -  installed successfully
  357. ;       AX not zero  -  did not install
  358. ;---------------------------------------------------------
  359. insure proc          USES DS ES SI DI, CLEAN_UP:FAR PTR
  360. ; --- force ds = cs
  361.         push    cs
  362.         pop     ds
  363. assume ds:@curseg
  364.  
  365. ; --- currently in palce?
  366.         cmp     word ptr insured_psp,0
  367.  
  368. ; --- yes, return to caller with no action
  369.         jnz     not_installed
  370.  
  371. ; --- get dos version
  372.         call    get_dos_version
  373.  
  374. ; --- if not dos 3.x don't install
  375.         cmp     ah,3
  376.         jl      not_installed
  377.  
  378. ; --- retrieve & save the psp of the current process
  379. ; --- function 62h (get PSP) is DOS 3+ only
  380.         mov     ah,62h
  381.         int     21h
  382.         mov     insured_psp,bx
  383.  
  384. ; --- get the addr of clean up function & save it
  385.         les     bx,CLEAN_UP
  386.         mov     word ptr exit_routine+2,es
  387.         mov     word ptr exit_routine,bx
  388.  
  389. ; --- get the addr of "InDOS" flag
  390.         call    get_indos_addr
  391.  
  392. ; --- get the gritical error flag address
  393.         call    get_critical_error_addr
  394.  
  395. ; --- install the interrupt intercepts
  396.         call    install_intercepts
  397.  
  398. ; --- insure BIOS break flag is clear
  399.         mov     bx,BIOS_SEG
  400.         mov     es,bx
  401.         mov     bx,BREAK_FLAG_OFF
  402.         and     es:byte ptr [bx],01111111b
  403.  
  404. ; --- save installer's SS and SP
  405.         mov     installer_ss,ss
  406.         mov     installer_sp,sp
  407.  
  408. ; --- set good return code & clear "in already" flag
  409. ;     for int 8h handler
  410.         xor     ax,ax
  411.         mov     byte ptr in_already,al
  412.         jmp     insure_exit
  413.  
  414. not_installed:
  415.         mov     ax,1
  416.  
  417. insure_exit:
  418.         ret
  419. insure endp
  420.  
  421.  
  422. assume ds:nothing
  423.  
  424. ;---------------------------------------------------------
  425. ; CANCEL()
  426. ;   Restore the original vectors and clear the
  427. ;   "insured_psp" address.
  428. ; Returns:
  429. ;   No value
  430. ; Trashes:
  431. ;   AX
  432. ;---------------------------------------------------------
  433. cancel proc             USES DS DX
  434. ; --- if not installed, get out with no action
  435.         mov     dx,cs:insured_psp
  436.         or      dx,dx
  437.         jz      no_cancellation
  438.  
  439. ; --- get the original int 8h  & restore it
  440.         restore_vector 08h
  441.  
  442. ; --- get the original int 1bh & restore it
  443.         restore_vector 1bh
  444.  
  445. ; --- get the original int 13h & restore it
  446.         restore_vector 13h
  447.  
  448. ; --- get the original int 25h & restore it
  449.         restore_vector 25h
  450.  
  451. ; --- get the original int 26h & restore it
  452.         restore_vector 26h
  453.  
  454. ; --- clear the insured_psp address
  455.         mov     cs:word ptr insured_psp,0
  456.  
  457. no_cancellation:
  458.         ret
  459. cancel endp
  460.  
  461.  
  462. assume cs:@curseg
  463. ;---------------------------------------------------------
  464. ;  INT_8H_HANDLER()
  465. ;   Intercept for timer tick interrupt.
  466. ;   Checks the BIOS break flag (bit 7 at 40:71) on each
  467. ;   invocation.
  468. ;   Flagged to prevent rentry.
  469. ;---------------------------------------------------------
  470. int_8h_handler proc far
  471. ; --- let int 8 do it's stuff
  472.         sim_int 08h
  473.         cli
  474.  
  475. ; --- if int 8 post processing is still going on, don't
  476. ;     re-enter - return to the interrupted code
  477.         cmp     cs:byte ptr in_already,1
  478.         jnz     do_break_ck
  479.         iret
  480.  
  481. do_break_ck:
  482. ; --- flag that we're processing an int 8h & swap stacks
  483.         mov     cs:byte ptr in_already,1
  484.         mov     cs:save_ss,ss
  485.         mov     cs:save_sp,sp
  486.         push    cs
  487.         pop     ss
  488.         mov     sp,offset cs:stack_top
  489.         sti
  490.         apush   ax,bx,cx,dx,ds,es,si,di
  491.  
  492. ; --- if the BIOS break flag is NOT set, iret to caller
  493.         call    check_break_bit
  494.         jnc     no_break
  495.  
  496. ; --- if disk i/o in progress, iret back to caller
  497.         cmp     cs:busy_flag,0
  498.         jnz     no_break
  499.  
  500. ; --- if not in dos or critical error
  501.         call    check_indos_flag
  502.         jc      no_break
  503.         call    check_critter_flag
  504.         jc      no_break
  505.  
  506. ; --- if the psp of the caller isn't the same as
  507. ;     the psp when installed, skip it
  508.         mov     ah,62h
  509.         int     21h
  510.         cmp     bx,cs:insured_psp
  511.         jnz     no_break
  512.  
  513. ; --- BIOS break flag was set, everything is stable and the
  514. ;     active PSP is the same as the one that installed the
  515. ;     insurance.  clear the BIOS break bit and uninstall
  516. ;     the intercepted vectors
  517.         call    cancel
  518.  
  519. ; --- if a user supplied exit routine is installed, do it
  520.         mov     bx,word ptr exit_routine+2
  521.         or      bx,bx
  522.         jnz     do_user_supplied_exit
  523.  
  524. ; --- otherwise, DOS terminate function - error level = 1
  525.         mov     ax,4c01h
  526.         int     21h
  527.  
  528. do_user_supplied_exit:
  529. ; --- get back BX & ES plus clean off CS, IP & FLAGS pushed
  530. ;     on original interrupt, then jump to user exit routine
  531.         mov     ss,cs:installer_ss
  532.         mov     sp,cs:installer_sp
  533.         jmp     cs:exit_routine
  534.  
  535. no_break:
  536. ; --- clean up the stack
  537.         apop    ax,bx,cx,dx,ds,es,si,di
  538.         cli
  539.         mov     ss,cs:save_ss
  540.         mov     sp,cs:save_sp
  541.         sti
  542.         mov     cs:byte ptr in_already,0
  543.  
  544. skip_it:
  545. ; --- iret to the int 8 caller
  546.         iret
  547.  
  548. int_8h_handler endp
  549.  
  550. ;---------------------------------------------------------
  551. ;  BREAK_HANDLER()
  552. ;   Simple replacement for int 1bh - no more than an iret
  553. ;   to keep the "^C" off the display screen.
  554. ;---------------------------------------------------------
  555. break_handler proc far
  556.         iret
  557. break_handler endp
  558.  
  559. ;---------------------------------------------------------
  560. ;  THIRTEEN_HANDLER()
  561. ;   intercept for int 13h
  562. ;   Increments the busy flag on entry, calls the original
  563. ;   interrupt service routine, and decrements the busy flag
  564. ;   when the disk routine is complete. The busy flag must
  565. ;   be zero to indicate no disk i/o in progress.
  566. ;---------------------------------------------------------
  567. thirteen_handler proc far
  568.         pushf
  569.         inc     cs:busy_flag
  570.         popf
  571.         sim_int 13h
  572.         pushf
  573.         dec     cs:busy_flag
  574.         popf
  575.         iret
  576. thirteen_handler endp
  577.  
  578. ;---------------------------------------------------------
  579. ;  TWENTY_FIVE_HANDLER()
  580. ;   intercept for int 25h
  581. ;   Increments the busy flag on entry, calls the original
  582. ;   interrupt service routine, and decrements the busy flag
  583. ;   when the disk routine is complete. The busy flag must
  584. ;   be zero to indicate no disk i/o in progress.
  585. ;---------------------------------------------------------
  586. twenty_five_handler proc far
  587.         pushf
  588.         inc     cs:busy_flag
  589.         popf
  590.         sim_int 25h
  591.         pushf
  592.         dec     cs:busy_flag
  593.         popf
  594.         iret
  595. twenty_five_handler endp
  596.  
  597. ;---------------------------------------------------------
  598. ;  TWENTY_SIX_HANDLER()
  599. ;   intercept for int 26h
  600. ;   Increments the busy flag on entry, calls the original
  601. ;   interrupt service routine, and decrements the busy flag
  602. ;   when the disk routine is complete. The busy flag must
  603. ;   be zero to indicate no disk i/o in progress.
  604. ;---------------------------------------------------------
  605. twenty_six_handler proc far
  606.         pushf
  607.         inc     cs:busy_flag
  608.         popf
  609.         sim_int 26h
  610.         pushf
  611.         dec     cs:busy_flag
  612.         popf
  613.         iret
  614. twenty_six_handler endp
  615.  
  616. end
  617.