home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / CL386P.ZIP / CL386PFX.ASM < prev    next >
Assembly Source File  |  1991-07-29  |  25KB  |  655 lines

  1.               PAGE     80, 120
  2.               TITLE    CL386PFX - CL386 prefix replacement
  3.     SUBTTL    Copyright (c) 1991  Stephen Best
  4. ;
  5. ;
  6. ;  CL386PFX
  7. ;
  8. ;  Version 1.1
  9. ;
  10. ;
  11. ;  CL386PFX is a replacement for the runtime prefix code used with MS CL386.
  12. ;  It was developed primarily for use with re-entrant multi-threaded 32 bit
  13. ;  applications for OS/2 2.X, using the MS 386 C compiler (currently named
  14. ;  CL386). The use of this prefix enables full use of the facilities of the
  15. ;  OS/2 2.X API (with no MS specific extensions) to enable such applications
  16. ;  to be readily ported to future OS/2 platforms, such as RISC machines. No
  17. ;  changes need be made to headers or C code used with this prefix as the
  18. ;  modifications are invoked when the module is linked.
  19. ;
  20. ;  This version 1.1 has been modified to work with the _syscall declarative
  21. ;  used for malloc and free runtime functions, as introduced with the MS OS/2
  22. ;  2.0 SDK Pre-release 3.
  23. ;
  24. ;  This modification to the supplied prefix offers the following advantages:
  25. ;
  26. ;     1. Smaller EXE file size.
  27. ;
  28. ;     2. A re-entrant heap manager (used by malloc/free calls) which allows for
  29. ;        replacement of _beginthread with DosCreateThread. The heap manager
  30. ;        in this prefix allocates a 1MB heap which is sub-allocated by OS/2
  31. ;        calls and dynamically committed as required. Also the DosSuspendThread
  32. ;        and DosResumeThread can now be used without risk of a deadlock
  33. ;        situation. Use of _beginthread and _endthread with this prefix is not
  34. ;        supported.
  35. ;
  36. ;     3. A new stack replaces that defined in the DEF file. This new stack is
  37. ;        allocated 64Kb and uses the guard page feature of OS/2 2.X to
  38. ;        dynamically commit pages to the maximum above. As the stack is
  39. ;        allocated in a separate memory object, an exception will occur on an
  40. ;        overflow condition (generally an indication of looping in recursive
  41. ;        routines). It is suggested that all programs using this prefix be
  42. ;        linked with STACKSIZE 1024 in the DEF file. The HEAPSIZE parameter
  43. ;        can be omitted.
  44. ;
  45. ;     4. General protection exceptions for stack overflows and bad heap frees
  46. ;        with an indication of the cause and program address.
  47. ;
  48. ;
  49. ;  Restrictions:
  50. ;
  51. ;     1. To make use of such functions as DosCreateThread, all runtime functions
  52. ;        called by more than one thread must be re-entrant as this prefix does
  53. ;        not provide serialization to a shared resource (other than that
  54. ;        provided by the system for malloc/free calls). To this aim, a pair of
  55. ;        LIB response files have been provided to enable migration of selected
  56. ;        object files from the standard LIBC.LIB runtime library, to a new
  57. ;        re-entrant library, BITWARE.LIB which initially only contains this
  58. ;        prefix code.
  59. ;
  60. ;        This new library should be specified instead of LIBC.LIB when linking,
  61. ;        for example:
  62. ;
  63. ;          link386 /NOD test, test,, bitware os2386, test.def
  64. ;
  65. ;        The MS 're-entrant' library LIBCMT.LIB must NOT be used with this
  66. ;        replacement prefix, nor should the /MT option be specified when
  67. ;        compiling. An alternate approach is to specify the initial BITWARE.LIB
  68. ;        before LIBC.LIB when linking to pick up the appropriate object files
  69. ;        but this carries the risk of including objects that have not be tested
  70. ;        to be truly re-entrant.
  71. ;
  72. ;        Note that the list of objects provided in the LIB response files have
  73. ;        not, as yet, been fully tested for the re-entrancy requirements above
  74. ;        and it is expected that additions/subtractions will occur over the
  75. ;        life of this product.
  76. ;
  77. ;     2. As yet, no support for a number of useful runtime objects (such as the
  78. ;        printf/scanf family) has been provided, nor can these routines be used
  79. ;        with this prefix. Expect this shortcoming to change in a future
  80. ;        packaging of these CL386 runtime modifications. Volunteers wanted!
  81. ;
  82. ;     3. Care with memory allocations is required. Memory can be allocated
  83. ;        to automatic variables, by malloc and lastly by DosAllocMem. All three
  84. ;        methods should be used as appropriate. As there is only a single guard
  85. ;        page set up for stack growth, the total size of automatic variables
  86. ;        for a function call should not exceed 4096 bytes. This is not likely
  87. ;        to be a problem but can be checked by examining the maximum negative
  88. ;        offset in the stack frame from the CL386 local symbol listing, and
  89. ;        making sure that it is less than 0x1000. Malloc (and free) should be
  90. ;        used for temporary allocations of intermediate size. As there is no
  91. ;        heap cleanup logic provided by the system (that I know of), unused but
  92. ;        committed pages can remain after a free. Lastly, DosAllocMem should be
  93. ;        used for all permanent (or large temporary) allocations. These
  94. ;        allocations will be rounded up to the next 4Kb page size.
  95. ;
  96. ;     4. The command line arguments are parsed and available in the argc and
  97. ;        argv parameters as per normal. A maximum of 100 arguments will be
  98. ;        parsed. No support has been provided for the environment as this is
  99. ;        satisfied by OS/2 2.X API calls.
  100. ;
  101. ;
  102. ;  Exception codes:
  103. ;
  104. ;  This prefix will generate an exception during operation for various
  105. ;  conditions. Exceptions from the prefix will have zeros in the EIP register
  106. ;  and 0xdead in the EDX register. Any others you get are either unintentional
  107. ;  or the cause of problems external to this code. The following is an
  108. ;  explanation of the meaning of the code (in the EAX register):
  109. ;
  110. ;
  111. ;     1 - Bad API return code from initial heap allocation request
  112. ;     2 - Bad API return code from initial heap sub-allocation request
  113. ;
  114. ;     3 - Bad API return code from heap request (malloc)
  115. ;     4 - Bad API return code from heap request (free)
  116. ;
  117. ;  In both the above, EBX contains the caller's address. Code 4 is the most
  118. ;  likely one to be seen, and represents either a bad address given to a free
  119. ;  or a second free for the same address or, worse still, a corruption of the
  120. ;  heap management chain due to an out-of-bounds condition. In any event, the
  121. ;  location of the malloc or free can be worked out with a combination of the
  122. ;  COD and MAP listings.
  123. ;
  124. ;     5 - Bad API return code from initial stack allocation request
  125. ;     6 - Bad API return code from initial stack guard page setup
  126. ;
  127. ;     7 - Insufficient remaining stack for stack probe
  128. ;
  129. ;
  130. ;  Files:
  131. ;
  132. ;     CL386PFX.DOC - cover letter
  133. ;
  134. ;     CL386PFX.ASM - source for the new CL386 prefix
  135. ;     CL386PFX.MAK - NMAKE file for above
  136. ;
  137. ;     BITWARE.LIB  - object library containing CL386PFX.OBJ only
  138. ;     BITWARE.H    - header file for migrated objects
  139. ;     EXPORT.RSP   - LIB response file for exporting objects from LIBC.LIB
  140. ;     IMPORT.RSP   - LIB response file for importing objects to BITWARE.LIB
  141. ;
  142. ;  The BITWARE.LIB object library can be built from the re-entrant portions of
  143. ;  the MS LIBC.LIB library with the following commands:
  144. ;
  145. ;     LIB @EXPORT.RSP
  146. ;     LIB @IMPORT.RSP
  147. ;
  148. ;  Note that you may have to change to path name for the LIBC library on the
  149. ;  first line of EXPORT.RSP. Check the output BITWARE.LST file to see which
  150. ;  runtime functions are currently usable. ALSO note that at no time should
  151. ;  the completed BITWARE.LIB be distributed containing anything other than the
  152. ;  the replacement prefix. Objects imported from LIBC.LIB remain the
  153. ;  property of Microsoft and are subject to normal distribution regulations.
  154. ;
  155. ;  
  156. ;  Note that when using CV386 to debug modules including this prefix, you may
  157. ;  see a number of segment violation messages. These are spurious and result
  158. ;  from the initial page being validated for new program stack, as part of the
  159. ;  guard page mechanism. Just hit the GO key until these are cleared.
  160. ;
  161. ;
  162. ;  These modifications are distributed free of charge (and with no implied
  163. ;  liability or guarantee of functionality) for use as the recipient chooses,
  164. ;  excluding resale for profit. Please feel free to distribute this set of
  165. ;  files intact, together with any modifications you or others may make.
  166. ;
  167. ;  If you have any suggestions/criticisms/comments please direct them to me at
  168. ;  the below address or the Fidonet address given.
  169. ;
  170. ;
  171. ;     Stephen Best
  172. ;     July 21, 1991
  173. ;
  174. ;
  175. ;  FidoNet:    3:620/243.4
  176. ;  CompuServe: 100033,340
  177. ;
  178. ;  Bitware, Software & Services
  179. ;  P.O. Box 3097
  180. ;  Manuka  A.C.T.  2603
  181. ;  Australia
  182. ;
  183. ;
  184.               PAGE
  185.               .386P
  186.  
  187. ;  Kernel routines
  188.  
  189.               EXTRN    DOS32ALLOCMEM: PROC
  190.               EXTRN    DOS32FREEMEM: PROC
  191.               EXTRN    DOS32SUBSET: PROC
  192.               EXTRN    DOS32SUBUNSET: PROC
  193.               EXTRN    DOS32SUBALLOC: PROC
  194.               EXTRN    DOS32SUBFREE: PROC
  195.               EXTRN    DOS32SETMEM: PROC
  196.               EXTRN    DOS32EXIT: PROC
  197.  
  198. ;  Variables
  199.  
  200. PAGE_SIZE     EQU      4096
  201. STACK_SIZE    EQU      PAGE_SIZE * 16
  202. HEAP_SIZE     EQU      PAGE_SIZE * 256
  203. MAX_ARGS      EQU      100
  204.  
  205. ;  Constants
  206.  
  207. PAG_READ      EQU      00000001h              ; read access
  208. PAG_WRITE     EQU      00000002h              ; write access
  209. PAG_GUARD     EQU      00000008h              ; guard page attributes
  210. PAG_COMMIT    EQU      00000010h              ; commit storage
  211.  
  212. DOSSUB_INIT            EQU  00000001h
  213. DOSSUB_SPARSE_OBJ      EQU  00000004h
  214. DOSSUB_SERIALIZE       EQU  00000008h
  215.  
  216. EXIT_PROCESS  EQU      00000001h              ; exit process
  217.               PAGE
  218.               ASSUME   cs:FLAT, ds:FLAT, es:FLAT, ss:FLAT
  219.  
  220. DGROUP        GROUP    _DATA, CONST, _BSS, STACK
  221.  
  222.  
  223. _DATA         SEGMENT  DWORD USE32 PUBLIC 'DATA'
  224.  
  225.               PUBLIC   __acrtused
  226. __acrtused    EQU      0                      ; link hook
  227.  
  228. __stacksave   DD       0                      ; original stack pointer
  229. __stackbase   DD       0                      ; new stack base
  230. __heapbase    DD       0                      ; heap memory base
  231.  
  232. ___argc       DD       0                      ; argument count
  233. ___argv       DD       0                      ; argument vector table address
  234.  
  235. _DATA         ENDS
  236.  
  237.  
  238. CONST         SEGMENT  DWORD USE32 PUBLIC 'CONST'
  239. CONST         ENDS
  240.  
  241.  
  242. _BSS          SEGMENT  DWORD USE32 PUBLIC 'BSS'
  243. _BSS          ENDS
  244.  
  245.  
  246. STACK         SEGMENT  DWORD USE32 STACK 'STACK'
  247. STACK         ENDS
  248.               PAGE
  249. _TEXT         SEGMENT  DWORD USE32 PUBLIC 'CODE'
  250.  
  251.               EXTRN    _main: PROC
  252.  
  253. ;  Entry point for module
  254.  
  255.               PUBLIC   __entry
  256. __entry       PROC
  257.  
  258.  
  259.               mov      ebp, esp               ; copy caller's ebp
  260.  
  261. ;  Save command line address
  262.  
  263.               mov      esi, [ebp + 16]        ; get command line address
  264.  
  265. ;  Set scan/copy direction
  266.  
  267.               cld                             ; set scan/copy forward
  268.  
  269. ;  Allocate stack and heap
  270.  
  271.               call     __stackinit            ; initialize stack
  272.               call     __heapinit             ; initialize heap
  273.  
  274. ;  Build command line argument array and count
  275.  
  276.               push     esi                    ; set command line address
  277.               call     __setargv              ; parse command arguments
  278.  
  279. ;  Prefix code all done, now do some work
  280.  
  281.               push     ___argv                ; argument vector array address
  282.               push     ___argc                ; argument count
  283.               call     _main                  ; call program
  284.               mov      esp, ebp               ; restore stack pointer
  285.               xchg     eax, esi               ; save return code
  286.  
  287. ;  Free heap and stack
  288.  
  289.               call     __heapfree             ; free heap
  290.               call     __stackfree            ; free stack
  291.  
  292. ;  Set return code and exit
  293.  
  294.               push     esi                    ; set return code
  295.               push     EXIT_PROCESS           ; set exit condition
  296.               call     DOS32EXIT              ; exit
  297.  
  298.  
  299. __entry       ENDP
  300.               PAGE
  301. __stackinit   PROC
  302.  
  303. ;  New stack creation
  304.  
  305.               pop      edi                    ; return address
  306.               mov      ebp, esp               ; copy stack pointer
  307.  
  308.               mov      __stacksave, esp       ; save stack pointer
  309.  
  310.               push     PAG_READ OR PAG_WRITE  ; attributes
  311.               push     STACK_SIZE             ; total stack size
  312.               push     OFFSET FLAT:__stackbase ; field for stack address
  313.               call     DOS32ALLOCMEM          ; allocate it
  314.               mov      esp, ebp               ; restore stack pointer
  315.  
  316.               or       eax, eax               ; check return code
  317.               jz       SHORT @f               ; ok, skip
  318.  
  319.               push     5                      ; error code 5
  320.               push     0                      ; no program address
  321.               call     __error                ; call error logic
  322.  
  323. @@:           push     PAG_READ OR PAG_WRITE OR PAG_GUARD OR PAG_COMMIT
  324.               push     PAGE_SIZE              ; page size
  325.               mov      eax, __stackbase       ; stack base
  326.               add      eax, STACK_SIZE - PAGE_SIZE
  327.               push     eax                    ; last page address
  328.               call     DOS32SETMEM            ; set attributes
  329.               mov      esp, ebp               ; restore stack pointer
  330.  
  331.               or       eax, eax               ; check return code
  332.               jz       SHORT @f               ; ok, skip
  333.  
  334.               push     6                      ; error code 6
  335.               push     0                      ; no program address
  336.               call     __error                ; call error logic
  337.  
  338. @@:           mov      esp, __stackbase       ; get new stack base
  339.               add      esp, STACK_SIZE        ; stack end
  340.               mov      ebp, esp               ; copy stack end address
  341.  
  342.               jmp      edi                    ; return
  343.  
  344.  
  345. __stackinit   ENDP
  346.  
  347.  
  348.  
  349. __stackfree   PROC
  350.  
  351. ;  New stack deallocation
  352.  
  353.               pop      edi                    ; return address
  354.  
  355.               mov      esp, __stacksave       ; original stack address
  356.               mov      ebp, esp               ; copy stack end address
  357.  
  358.               push     __stackbase            ; address of stack
  359.               call     DOS32FREEMEM           ; free heap
  360.               mov      esp, ebp               ; restore stack pointer
  361.  
  362.               jmp      edi                    ; return
  363.  
  364.  
  365. __stackfree   ENDP
  366.               PAGE
  367. __heapinit    PROC
  368.  
  369. ;  Allocate heap
  370.  
  371.               enter    0, 0                   ; set stack frame
  372.  
  373.               push     PAG_READ OR PAG_WRITE  ; read/write allocation
  374.               push     HEAP_SIZE              ; total heap size
  375.               push     OFFSET FLAT:__heapbase ; field for heap address
  376.               call     DOS32ALLOCMEM          ; allocate it
  377.               mov      esp, ebp               ; restore stack pointer
  378.  
  379.               or       eax, eax               ; check return code
  380.               jz       SHORT @f               ; ok, skip
  381.  
  382.               push     1                      ; error code 1
  383.               push     0                      ; no program address
  384.               call     __error                ; call error logic
  385.  
  386. @@:           push     HEAP_SIZE              ; suballocation size
  387.               push     DOSSUB_INIT OR DOSSUB_SPARSE_OBJ OR DOSSUB_SERIALIZE
  388.               push     __heapbase             ; address of heap
  389.               call     DOS32SUBSET            ; set for initial suballocation
  390.               mov      esp, ebp               ; restore stack pointer
  391.  
  392.               or       eax, eax               ; check return code
  393.               jz       SHORT @f               ; ok, skip
  394.  
  395.               push     2                      ; error code 2
  396.               push     0                      ; no program address
  397.               call     __error                ; call error logic
  398.  
  399. @@:           leave                           ; restore ebp
  400.  
  401.               ret                             ; return
  402.  
  403.  
  404. __heapinit    ENDP
  405.  
  406.  
  407.  
  408. __heapfree    PROC
  409.  
  410. ;  Free heap
  411.  
  412.               enter    0, 0                   ; set stack frame
  413.  
  414.               push     __heapbase             ; address of heap
  415.               call     DOS32SUBUNSET          ; free sub allocation
  416.               mov      esp, ebp               ; restore stack pointer
  417.  
  418.               push     __heapbase             ; address of heap
  419.               call     DOS32FREEMEM           ; free heap
  420.               mov      esp, ebp               ; restore stack pointer
  421.  
  422.               leave                           ; restore ebp
  423.  
  424.               ret                             ; return
  425.  
  426.  
  427. __heapfree    ENDP
  428.               PAGE
  429. __setargv     PROC
  430.  
  431. ;  Parse command line arguments
  432.  
  433.               enter    0, 0                   ; set stack frame
  434.  
  435.               mov      esi, [ebp + 8]         ; command line start address
  436.               mov      edi, esi               ; copy it
  437.               xor      eax, eax               ; scan terminator (null)
  438.               cmp      al, [esi]              ; do we have command line?
  439.               je       SHORT __setargv_3      ; no, skip
  440.  
  441.               mov      ecx, -1                ; scan length
  442.               repne    scasb                  ; scan for terminator
  443.               repne    scasb                  ; scan for terminator
  444.  
  445.               sub      edi, esi               ; command line length
  446.               lea      eax, MAX_ARGS * 4 [edi] ; add vector table length
  447.               push     eax                    ; set as combined length
  448.               call     _malloc                ; let's test our malloc routine
  449.  
  450.               mov      ___argv, eax           ; save vector array address
  451.               lea      edi, MAX_ARGS * 4 [eax] ; copy area start
  452.               xchg     eax, ebx               ; copy array address
  453.               mov      ecx, 2                 ; null counter
  454.  
  455. __setargv_1:
  456.               lodsb                           ; get next character
  457. __setargv_2:
  458.               or       al, al                 ; null?
  459.               jne      SHORT @f               ; no, skip
  460.               loop     __setargv_1            ; decrement null counter
  461.  
  462.               jmp      SHORT __setargv_3      ; end of command line
  463.  
  464. @@:           cmp      al, ' '                ; blank?
  465.               je       __setargv_1            ; yes, skip it
  466.               cmp      al, 09h                ; tab?
  467.               je       __setargv_1            ; yes, skip it
  468.  
  469.               mov      [ebx], edi             ; set parameter address
  470.               add      ebx, 4                 ; update slot address
  471.  
  472.               xor      edx, edx               ; zero quote indicator
  473.  
  474. __setargv_6:
  475.               cmp      al, '"'                ; quote?
  476.               jne      SHORT __setargv_7      ; no, skip
  477.  
  478.               not      edx                    ; toggle quote sequence flag
  479.  
  480.               jmp      SHORT __setargv_8      ; continue
  481.  
  482. __setargv_7:
  483.               stosb                           ; set last character
  484. __setargv_8:
  485.               lodsb                           ; get next character
  486.               or       al, al                 ; null?
  487.               je       SHORT __setargv_5      ; yes, end of parameter
  488.  
  489.               or       edx, edx               ; in quote sequence
  490.               jnz      __setargv_6            ; yes, skip blank/tab check
  491.  
  492.               cmp      al, ' '                ; blank?
  493.               je       SHORT __setargv_5      ; yes, end of parameter
  494.               cmp      al, 09h                ; tab?
  495.               jne      __setargv_6            ; no, continue
  496.  
  497. __setargv_5:
  498.               mov      BYTE PTR [edi], 0      ; set null
  499.               inc      edi                    ; update address
  500.  
  501.               inc      ___argc                ; update counter
  502.               cmp      ___argc, MAX_ARGS      ; reached maximum?
  503.               jne      __setargv_2            ; no, continue
  504.  
  505. __setargv_3:
  506.               leave                           ; restore ebp
  507.  
  508.               ret      4                      ; return
  509.  
  510.  
  511. __setargv     ENDP
  512.               PAGE
  513.               PUBLIC   _malloc
  514. _malloc       PROC
  515.  
  516. ;  Runtime malloc function
  517.  
  518.               enter    4, 0                   ; set stack frame
  519.  
  520.               push     edi                    ; save edi
  521.  
  522.               mov      eax, [ebp + 8]         ; request size
  523.               or       eax, eax               ; check if zero
  524.               jz       SHORT _malloc_1        ; is, use it as return code
  525.  
  526.               xchg     eax, edi               ; copy request size
  527.               add      edi, 4                 ; allow for header
  528.               push     edi                    ; set length
  529.               lea      eax, [ebp - 4]         ; local variable
  530.               push     eax                    ; set output address
  531.               push     __heapbase             ; set heap base
  532.               call     DOS32SUBALLOC          ; call allocation routine
  533.               lea      esp, [ebp - 8]         ; restore stack pointer
  534.  
  535.               or       eax, eax               ; check return code
  536.               jz       SHORT @f               ; ok, skip
  537.  
  538.               push     3                      ; error code 3
  539.               push     [ebp + 4]              ; caller's address
  540.               call     __error                ; call error logic
  541.  
  542. @@:           mov      eax, [ebp - 4]         ; block address
  543.               mov      [eax], edi             ; set block length as header
  544.  
  545.               add      eax, 4                 ; skip header
  546.  
  547. _malloc_1:
  548.               pop      edi                    ; restore edi
  549.  
  550.               leave                           ; restore ebp
  551.  
  552.               ret    4    ; return
  553.  
  554.  
  555. _malloc       ENDP
  556.  
  557.  
  558.  
  559.               PUBLIC   _free
  560. _free         PROC
  561.  
  562. ;  Runtime free function
  563.  
  564.               enter    0, 0                   ; set stack frame
  565.  
  566.               mov      eax, [ebp + 8]         ; input pointer
  567.               sub      eax, 4                 ; backspace to header
  568.  
  569.               push     [eax]                  ; set length
  570.               push     eax                    ; set address
  571.               push     __heapbase             ; set heap base
  572.               call     DOS32SUBFREE           ; free block
  573.               mov      esp, ebp               ; restore stack pointer
  574.  
  575.               or       eax, eax               ; check return code
  576.               jz       SHORT @f               ; ok, skip
  577.  
  578.               push     4                      ; error code 4
  579.               push     [ebp + 4]              ; caller's address
  580.               call     __error                ; call error logic
  581.  
  582. @@:           leave                           ; restore ebp
  583.  
  584.               ret    4    ; return
  585.  
  586.  
  587. _free         ENDP
  588.               PAGE
  589.               PUBLIC   _stackavail
  590. _stackavail   PROC
  591.  
  592. ;  Runtime stackavail function, returns EXACT amount of stack remaining
  593.  
  594.               pop      ecx                    ; return address
  595.  
  596.               mov      eax, __stackbase       ; base address of stack
  597.               sub      eax, esp               ; remaining length (negated)
  598.               jb       SHORT @f               ; some, skip
  599.  
  600.               xor      eax, eax               ; set zero
  601.  
  602. @@:           neg      eax                    ; make amount positive
  603.  
  604.               jmp      ecx                    ; return
  605.  
  606.  
  607. _stackavail   ENDP
  608.  
  609.  
  610.  
  611.               PUBLIC   __chkstk
  612. __chkstk      PROC
  613.  
  614. ;  Stack probe logic required for runtimes such as qsort
  615.  
  616.               pop      ecx                    ; return address
  617.  
  618.               cmp      eax, PAGE_SIZE         ; check stack adjustment
  619.               ja       SHORT @f               ; exceed page size, error
  620.               mov      edx, esp               ; copy current stack pointer
  621.               sub      edx, eax               ; adjust by requested amount
  622.               jb       SHORT @f               ; gone negative, error
  623.               cmp      edx, __stackbase       ; compare with stack base address
  624.               jb       SHORT @f               ; below, error
  625.  
  626.               mov      esp, edx               ; set new stack address
  627.  
  628.               jmp      ecx                    ; return
  629.  
  630. @@:           push     7                      ; error code 7
  631.               push     ecx                    ; caller's address
  632.               call     __error                ; call error logic
  633.  
  634.  
  635. __chkstk      ENDP
  636.               PAGE
  637. __error       PROC
  638.  
  639. ;  Error exception
  640.  
  641.               mov      eax, [esp + 8]         ; error code
  642.               mov      ebx, [esp + 4]         ; program address (or null)
  643.               mov      edx, 0000deadh         ; set eyecatcher
  644.  
  645.               xor      ecx, ecx               ; zero address
  646.               jmp      ecx                    ; zap program counter
  647.  
  648.  
  649. __error       ENDP
  650.               PAGE
  651. _TEXT         ENDS
  652.  
  653.  
  654.               END      __entry
  655.