home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / assemblr / library / sampler0 / ovlmgr.asm < prev    next >
Assembly Source File  |  1989-11-27  |  31KB  |  1,180 lines

  1. ;    SCCS Id: @(#)ovlmgr.asm     3.0    89/11/16
  2. ;  Copyright (c) Pierre Martineau and Stephen Spackman, 1989.
  3. ;  This product may be freely redistributed.  See NetHack license for details.
  4.  
  5.         PAGE    60,132
  6.         TITLE    'Overlay manager for use with Microsoft overlay linker'
  7.         SUBTTL    'Brought to you by Pierre Martineau and Stephen Spackman'
  8.  
  9. ; acknowledgements:   - No thanks to Microsoft
  10. ;              - alltrsidsctysti!!!
  11. ;              - izchak and friends for impetus
  12. ;              - us for brilliance
  13. ;              - coffee for speed
  14. ;              - others as necessary
  15.  
  16. ; assumptions:          - all registers are preserved including flags
  17. ;              - the stack is preserved
  18. ;              - re-entrancy is not required
  19.  
  20. DOSALLOC    equ    48h            ; memory allocation
  21. DOSFREE     equ    49h            ; free allocated memory
  22. DOSREALLOC    equ    4ah            ; modify memory block
  23. DOSREAD     equ    3fh            ; read bytes from handle
  24. DOSSEEK     equ    42h            ; logical handle seek
  25. DOSOPEN     equ    3dh            ; open handle
  26. DOSCLOSE    equ    3eh            ; close handle
  27. DOSGETVEC    equ    35h            ; get interrupt vector
  28. DOSSETVEC    equ    25h            ; set interrupt vector
  29. DOSEXEC     equ    4bh            ; exec child process
  30. DOS        equ    21h            ; Dos interrupt #
  31. PRINT        equ    09h            ; print string
  32. TERMINATE    equ    4ch            ; terminate process
  33. CR        equ    0dh
  34. LF        equ    0ah
  35. BELL        equ    07h
  36. FAERIE        equ    0h            ; Used for dummy segment allocation
  37. PARSIZ        equ    10h            ; this is the size of a paragraph - this better not change!
  38.  
  39. ; The following extrns are supplied by the linker
  40.  
  41. extrn        $$OVLBASE:byte            ; segment of OVERLAY_AREA
  42. extrn        $$MPGSNOVL:byte         ; ^ to module table
  43. extrn        $$MPGSNBASE:word        ; ^ to module segment fixups
  44. extrn        $$INTNO:byte            ; interrupt number to be used
  45. extrn        $$COVL:word            ; number of physical overlays
  46. extrn        $$CGSN:word            ; number of modules
  47. extrn        $$MAIN:far            ; ^ to function main()
  48.  
  49. public        $$OVLINIT            ; Our entry point
  50.                         ; called by the c startup code
  51.  
  52. ovlflgrec    record    running:1=0,locked:1=0,loaded:1=0 ; overlay flags
  53.  
  54. ; This is a dirty hack. What we need is a virtual segment that will be built
  55. ; by the (our) loader in multiple copies, one per overlay. Unfortunately, this
  56. ; doesn't seem to be a sensible idea in the minds of the folks at Microsoft.
  57. ; Declaring this segment AT will ensure that it never appears in the exefile,
  58. ; and ASSUME is dumb enough to be fooled.
  59. ;
  60. ; The reason we want to do this is also not-to-be-tried-at-home: it turns out
  61. ; that we can code a faster interrupt handler if we map overlay numbers to
  62. ; segment values. Normally I would consider this unacceptable programming
  63. ; practise because it is 86-mode specific, but the *need* for this entire
  64. ; programme is 86-mode specific, anyway.
  65.  
  66. pspseg        segment para at FAERIE        ; dummy segment for psp
  67.         org    2ch            ; ^ to segment of environmemt in psp
  68. pspenv        LABEL    WORD
  69. pspseg        ends
  70.  
  71. ovltbl        segment para at FAERIE        ; Dummy segment definition for overlay table
  72.  
  73. ; NOTE: This segment definition MUST be exactly 16 bytes long
  74.  
  75. ovlflg        ovlflgrec    <0,0,0>     ; overlay flags
  76. ovltblpad1    db    ?            ; go ahead, delete me!
  77. ovlmemblk    dw    ?            ; ^ to allocated memory block
  78. ovlseg        dw    0            ; ovl segment physical add.
  79. ovlfiloff    dw    ?            ; ovl file offset in pages (512 bytes)
  80. ovlsiz        dw    ?            ; ovl size in paragraphs
  81. ovllrudat    dd    0            ; misc lru data (pseudo time stamp)
  82. ovltblpad2    dw    ?            ; go ahead, delete me!
  83.  
  84. if1
  85. if        $ gt PARSIZ
  86.         .err
  87.         %out This segment MUST be no more than 16 bytes, REALLY!!!
  88. endif
  89. endif
  90.  
  91. ovlsegsiz    equ    PARSIZ            ; this had better be true!!! (16 bytes)
  92.  
  93. ovltbl        ends
  94.  
  95. EXEHDR        struc                ; structure of an EXE header
  96. exesign     dw    5a4dh            ; signature
  97. exelstpgesiz    dw    ?            ; last page size (512 byte pages)
  98. exesiz        dw    ?            ; total pages (including partial last page)
  99. relocitems    dw    ?            ; number of relocation entries
  100. hdrparas    dw    ?            ; number of paragraphs in the header
  101. minalloc    dw    ?            ; minimum paragraph allocation
  102. maxalloc    dw    ?            ; maximum patagraph allocation
  103. exess        dw    ?            ; initial stack segment
  104. exesp        dw    ?            ; initial stack pointer
  105. exechksum    dw    ?            ; checksum
  106. exeip        dw    ?            ; initial instruction pointer
  107. execs        dw    ?            ; initial code segment
  108. reloctbloff    dw    ?            ; offset from beginning of header to relocation table
  109. exeovlnum    dw    ?            ; overlay number
  110. EXEHDR        ends
  111.  
  112. MASK_used    equ    1            ; memory block flag
  113.  
  114. memctlblk    struc                ; memory block structure
  115. memblkflg    db    0            ; flags
  116. memblkpad1    db    0            ; go ahead, delete me!
  117. memblknxt    dw    0            ; ^ to next block
  118. memblkprv    dw    0            ; ^ to previous block
  119. memblkovl    dw    0            ; ^ to overlay occupying this block
  120. memblksiz    dw    0            ; size in paragraphs
  121. memblkpad    db    PARSIZ - ($ - memblkflg) mod parsiz dup (?) ; pad to 16 bytes
  122. memctlblk    ends
  123.  
  124. memctlblksiz    equ    memblkpad + SIZE memblkpad ; should equal 1 paragraph (16 bytes)
  125.  
  126. ;-------------------------------------------------------------------------------
  127.  
  128. code        segment public
  129.  
  130. ovlexefilhdl    dw    -1            ; always-open file handle of our .EXE
  131. ovltim        dd    0            ; pseudo-lru time variable
  132. curovl        dw    offset framestk     ; ^ into stack frame
  133. ovlcnt        dw    0            ; # overlays
  134. modcnt        dw    0            ; # of modules
  135. ovltblbse    dw    -1            ; segment of first overlay descriptor
  136. ovlrootcode    dw    0            ; logical segment of OVERLAY_AREA
  137. ovldata     dw    0            ; logical segment of OVERLAY_END
  138. memblk1st    dw    0            ; first memory block
  139. pspadd        dw    0            ; our psp address + 10h (for relocations)
  140. oldvec        dd    -1            ; saved interrupt vector
  141. oldint21    dd    -1            ; saved int 21 vector
  142. memstat     db    0ffh            ; must we re-allocate some memory
  143. bxreg        dw    0            ; temp save area
  144. esreg        dw    0            ; temp save area
  145. farcall     dd    0            ; internal trampoline.
  146. hdr        EXEHDR    <>            ; EXE header work area
  147. hdrsize     equ    $ - hdr
  148.  
  149. framestk    dw    100h dup (0)        ; internal stack
  150.  
  151. moduletbl    dw    256*2 dup (0)        ; module lookup table
  152.  
  153. noroom        db    CR,LF,'Not enough memory to run this program.  Time to go to the store.',CR,LF,BELL,'$'
  154. nocore        db    CR,LF,'Your dog eats all your remaining memory!  You die.',CR,LF,BELL,'$'
  155. nofile        db    CR,LF,'The Nymph stole your .EXE file!  You die.',CR,LF,BELL,'$'
  156. exitmsg     db    CR,LF,'$'
  157.  
  158. ;-------------------------------------------------------------------------------
  159.  
  160. $$OVLINIT    proc    far            ; Init entry point
  161.  
  162.         assume    cs:code,ds:pspseg,es:nothing
  163.  
  164.         push    ax
  165.         push    bx
  166.         push    cx
  167.         push    dx
  168.         push    si
  169.         push    di
  170.         push    bp
  171.         push    ds
  172.         push    es            ; save the world
  173.         mov    ax,ds            ; get our psp
  174.         add    ax,10h
  175.         mov    pspadd,ax        ; save it
  176.         mov    ds,pspenv        ; get environment segment
  177.         mov    si,-1
  178. envloop:                    ; search for end of environment
  179.         inc    si
  180.         cmp    word ptr [si],0
  181.         jnz    envloop
  182.         add    si,4            ; point to EXE filename
  183.         mov    al,0            ; access code
  184.         mov    ah,DOSOPEN
  185.         mov    dx,si
  186.         int    DOS            ; open EXE
  187.         jnc    dontdie
  188.         mov    al,5
  189.         mov    dx,offset nofile
  190.         jmp    putserr         ; cry to the world!
  191. dontdie:
  192.         mov    ovlexefilhdl,ax     ; save handle
  193.         mov    ax,SEG $$OVLBASE    ; OVERLAY_AREA segment
  194.         mov    ovlrootcode,ax
  195.  
  196. ; Now allocate memory
  197.         mov    bx,0900h        ; allocate memory for malloc()
  198.         mov    ah,DOSALLOC
  199.         int    DOS
  200.         jnc    getmore
  201.         jmp    buyram
  202. getmore:
  203.         mov    es,ax            ; find largest free memory
  204.         mov    ah,DOSALLOC
  205.         mov    bx,0ffffh        ; Everything
  206.         int    DOS
  207.         mov    ah,DOSALLOC        ; allocate our own memory
  208.         int    DOS
  209.         jnc    gotitall
  210.         jmp    buyram
  211. gotitall:
  212.         mov    memstat,0        ; indicate that we have memory
  213.         mov    ovltblbse,ax        ; overlay descriptor table begins at start of memory block
  214.         mov    ax,SEG $$COVL        ; segment of DGROUP
  215.         mov    ds,ax
  216.         mov    cx,$$CGSN        ; number of modules
  217.         mov    modcnt,cx        ; save for later use
  218.         mov    cx,$$COVL        ; number of physical overlays
  219.         mov    ovlcnt,cx        ; save for later use
  220.         sub    bx,cx            ; enough mem for ovl tbl?
  221.         jnc    memloop
  222.         jmp    buyram
  223. memloop:
  224.         push    bx
  225.         mov    ah,DOSFREE        ; free first block for malloc()
  226.         int    DOS
  227.         jnc    cockadoodledoo
  228.         jmp    buyram
  229. cockadoodledoo:
  230.  
  231.         assume    es:ovltbl
  232.  
  233.         xor    bp,bp
  234.         xor    di,di
  235.         xor    si,si
  236. filsegtbllpp:                    ; initialise ovl table
  237.         call    gethdr            ; get an EXE header
  238.         mov    ax,ovltblbse
  239.         add    ax,hdr.exeovlnum
  240.         mov    es,ax            ; ^ to ovl table entry
  241.         xor    ax,ax
  242.         mov    word ptr ovllrudat,ax    ; initialise ovl lru
  243.         mov    word ptr ovllrudat+2,ax
  244.         mov    ovlseg,ax        ; initialise ovl segment
  245.         mov    ovlflg,al        ; initialise ovl flags
  246.         mov    ax,hdr.exesiz
  247.         shl    ax,1
  248.         shl    ax,1
  249.         shl    ax,1
  250.         shl    ax,1
  251.         shl    ax,1            ; * 32
  252.         mov    dx,hdr.exelstpgesiz
  253.         or    dx,dx
  254.         jz    emptypage
  255.         shr    dx,1
  256.         shr    dx,1
  257.         shr    dx,1
  258.         shr    dx,1            ; / 16
  259.         inc    dx
  260.         sub    ax,20h
  261.         add    ax,dx
  262. emptypage:
  263.         mov    ovlsiz,ax        ; overlay size in paragraphs
  264.         sub    ax,hdr.hdrparas     ; actual size of code and relocation table
  265.         cmp    hdr.exeovlnum,0     ; skip if ovl 0 (root code)
  266.         jz    notlargest
  267.         cmp    ax,di            ; find largest ovl
  268.         jc    notlargest
  269.         mov    di,ax
  270.         mov    si,ovlsiz
  271. notlargest:
  272.         mov    ovlfiloff,bp        ; initialise ovl file offset
  273.         add    bp,hdr.exesiz        ; ^ to next overlay
  274.         mov    dx,bp
  275.         mov    cl,dh
  276.         mov    dh,dl
  277.         xor    ch,ch
  278.         xor    dl,dl
  279.         shl    dx,1
  280.         rcl    cx,1            ; cx:dx = bp * 512
  281.         mov    al,0
  282.         mov    ah,DOSSEEK        ; seek to next ovl
  283.         int    DOS
  284.         mov    ax,ovlcnt
  285.         dec    ax
  286.         cmp    ax,hdr.exeovlnum    ; all overlays done?
  287.         jz    makmemblk
  288.         jmp    filsegtbllpp        ; Nope, go for more.
  289. makmemblk:
  290.         push    si            ; contains largest ovl size in paragraphs
  291.  
  292.         assume    es:nothing        ; prepare first two memory blocks
  293.                         ; OVERLAY_AREA and allocated memory block
  294.         mov    ax,ovlrootcode        ; OVERLAY_AREA segment
  295.         mov    es,ax
  296.         mov    si,ovltblbse
  297.         add    si,ovlcnt        ; end of ovl table
  298.         mov    es:memblkflg,0        ; clear mem flags
  299.         mov    es:memblknxt,si     ; point to next
  300.         mov    es:memblkprv,0        ; set previous to nothing
  301.         mov    es:memblksiz,di     ; di contains OVERLAY_AREA size in paragraphs
  302.         add    di,ax
  303.         mov    ovldata,di        ; end of OVERLAY_END
  304.         mov    es,si            ; end of ovl tbl (first memory block in allocated memory)
  305.         mov    es:memblkflg,0        ; clear mem flags
  306.         mov    es:memblknxt,0        ; set next to nothing
  307.         mov    es:memblkprv,ax     ; point to previous
  308.         pop    si
  309.         pop    bx
  310.         mov    es:memblksiz,bx     ; allocated memory block size less ovl table
  311.         mov    memblk1st,ax        ; save pointer to first mem block
  312.         mov    word ptr ovltim,0    ; initialise global lru time stamp
  313.         mov    word ptr ovltim+2,0
  314.         mov    ax,offset framestk
  315.         mov    curovl,ax        ; initialise stack frame pointer
  316.         mov    di,ax
  317.         mov    word ptr cs:[di],-1    ; initialise stack frame
  318.         add    di,6
  319.         mov    ax,ovltblbse
  320.         mov    cs:[di],ax
  321.         mov    curovl,di
  322.         mov    es,ax
  323.         mov    es:ovlflg,MASK running OR MASK locked OR MASK loaded ; set flags on ovl 0
  324.         inc    si            ; largest ovl size + 1 paragraph
  325.         cmp    bx,si            ; enough memory to alloc largest?
  326.         jnc    chgintvec
  327. buyram:
  328.         mov    al,5
  329.         mov    dx,OFFSET noroom    ; free up some TSRs or something
  330.         jmp    putserr
  331. chgintvec:
  332.         mov    ax,SEG $$INTNO
  333.         mov    ds,ax
  334.         mov    ah,DOSGETVEC
  335.         mov    al,$$INTNO        ; get int number to use
  336.         int    DOS            ; get original vector
  337.         mov    word ptr oldvec,bx    ; save original vector
  338.         mov    word ptr oldvec+2,es
  339.  
  340.         mov    ah,DOSGETVEC
  341.         mov    al,21h
  342.         int    DOS            ; get original vector
  343.         mov    word ptr oldint21,bx    ; save original vector
  344.         mov    word ptr oldint21+2,es
  345.  
  346.         mov    ax,SEG $$INTNO
  347.         mov    ds,ax
  348.         mov    ah,DOSSETVEC
  349.         mov    al,$$INTNO
  350.         mov    bx,cs
  351.         mov    ds,bx
  352.         mov    dx,OFFSET ovlmgr    ; point to ovlmgr
  353.         int    DOS            ; set vector
  354.  
  355.         mov    ah,DOSSETVEC
  356.         mov    al,21h
  357.         mov    bx,cs
  358.         mov    ds,bx
  359.         mov    dx,OFFSET int21     ; point to int21
  360.         int    DOS            ; set vector
  361.  
  362.         mov    cx,modcnt        ; module count
  363.         mov    ax,SEG $$MPGSNBASE
  364.         mov    es,ax
  365.         mov    ax,cs
  366.         mov    ds,ax
  367.  
  368.         assume    ds:code
  369.  
  370.         mov    bx,offset $$MPGSNBASE    ; ^ to linker provided overlay segment fixups
  371.         mov    si,offset $$MPGSNOVL    ; ^ to linker provided module table
  372.         mov    di,offset moduletbl    ; ^ to our module table
  373. modloop:
  374.         mov    al,es:[si]        ; real physical ovl number
  375.         xor    ah,ah
  376.         add    ax,ovltblbse        ; ovlctlseg address
  377.         mov    [di],ax         ; save in module table
  378.         mov    ax,es:[bx]        ; get seg fixup
  379.         sub    ax,ovlrootcode        ; adjust for relative reference
  380.         mov    [di+2],ax        ; save in module table
  381.         add    di,4
  382.         add    bx,2
  383.         inc    si
  384.         loop    modloop
  385.  
  386.         pop    es
  387.         pop    ds
  388.         pop    bp
  389.         pop    di
  390.         pop    si
  391.         pop    dx
  392.         pop    cx
  393.         pop    bx
  394.         pop    ax            ; restore the world
  395.         jmp    $$MAIN            ; And away we go!
  396.  
  397. $$OVLINIT    endp
  398.  
  399. ;-------------------------------------------------------------------------------
  400.  
  401. ovlmgr        proc    far            ; This the it!
  402.  
  403.         assume cs:code,ds:nothing,es:nothing
  404.  
  405.         mov    bxreg,bx        ; preserve bx
  406.         mov    esreg,es        ; and es
  407.         pop    bx            ; retrieve caller ip
  408.         pop    es            ;     "      "    cs
  409.         push    ax
  410.         push    si
  411.         mov    ax,es:[bx+1]        ; offset in ovl to call
  412.         mov    word ptr farcall,ax    ; into trampoline
  413.         xor    ah,ah
  414.         mov    al,es:[bx]        ; module # to call
  415.         add    bx,3            ; fix return address
  416.         mov    si,curovl        ; get stack frame pointer
  417.         mov    cs:[si+2],es        ; save return seg
  418.         mov    cs:[si+4],bx        ; and return offset
  419.  
  420.         mov    bx,ax
  421.         shl    bx,1
  422.         shl    bx,1            ; * 4 (2 words/entry in module tbl)
  423.         add    bx,offset moduletbl
  424.         mov    es,cs:[bx]        ; ovl tbl entry
  425.         mov    ax,cs:[bx+2]        ; segment fixup
  426.         mov    cs:[si+6],es        ; ovl entry into stack frame
  427.         add    curovl,6        ; update stack
  428.  
  429.         assume    es:ovltbl
  430.  
  431.         mov    si,WORD PTR ovltim    ; lru time stamp
  432.         inc    si            ; time passes!
  433.         mov    WORD PTR ovltim,si    ; update global clock
  434.         mov    WORD PTR ovllrudat,si    ; as well as ovl clock
  435.         jz    ininc            ; dword increment
  436. cryupcdon:    test    ovlflg,mask loaded    ; ovl loaded?
  437.         jz    inload            ; load it then.
  438. ovlloadedupc:
  439.         add    ax,ovlseg        ; add fixup and segment address
  440.         mov    word ptr farcall+2,ax    ; into trampoline
  441.         mov    bx,bxreg        ; retore all registers
  442.         mov    es,esreg
  443.         pop    si
  444.         pop    ax
  445.         popf                ; don't forget these!
  446.         call    DWORD PTR farcall    ; and GO
  447.         pushf                ; preserve registers again!
  448.         mov    esreg,es
  449.         mov    bxreg,bx
  450.         mov    bx,curovl        ; stack frame pointer
  451.         mov    es,cs:[bx-6]        ; retrieve ovl tbl entry
  452.         push    cs:[bx-4]        ; set return address
  453.         push    cs:[bx-2]
  454.         push    cx
  455.         mov    cx,WORD PTR ovltim    ; do the lru thing again
  456.         inc    cx
  457.         mov    WORD PTR ovltim,cx
  458.         mov    WORD PTR ovllrudat,cx
  459.         jz    outinc
  460. crydncdon:    test    ovlflg,mask loaded    ; ovl loaded?
  461.         jz    outload         ; better get it before someone notices
  462. jmpback:
  463.         sub    curovl,6        ; adjust stack
  464.         mov    bx,bxreg        ; get registers back
  465.         mov    es,esreg
  466.         pop    cx
  467.         iret                ; and GO back
  468.  
  469. ininc:
  470.         mov    si,WORD PTR ovltim+2    ; high word of lru
  471.         inc    si
  472.         mov    WORD PTR ovltim+2,si    ; update global and
  473.         mov    WORD PTR ovllrudat+2,si ; ovl clocks
  474.         jmp    cryupcdon
  475.  
  476. inload:
  477.         call    loadoverlay        ; self explanatory
  478.         jmp    ovlloadedupc
  479.  
  480. outinc:
  481.         mov    cx,WORD PTR ovltim+2
  482.         inc    cx
  483.         mov    WORD PTR ovltim+2,cx
  484.         mov    WORD PTR ovllrudat+2,cx
  485.         jmp    crydncdon
  486.  
  487. outload:
  488.         call    loadoverlay
  489.         jmp    jmpback
  490.  
  491. ovlmgr        endp
  492.  
  493. ;-------------------------------------------------------------------------------
  494.  
  495. loadoverlay    proc    near            ; load overlay pointed to by es
  496.  
  497.         assume    cs:code,ds:nothing,es:ovltbl
  498.  
  499.         push    ax
  500.         push    bx
  501.         push    cx
  502.         push    dx
  503.         push    si
  504.         push    di
  505.         push    bp
  506.         push    ds
  507.         push    es            ; just in case
  508.         cmp    memstat,0
  509.         jz    dontrealloc
  510.         call    reallocmem
  511. dontrealloc:
  512.         call    setrunning        ; set the running flags
  513.         test    ovlflg,MASK running    ; was it already running?
  514.         jnz    fxdadr            ; Yup, it's a toughie
  515.         mov    ax,ovlsiz        ; How much?
  516.         call    getpages        ; never fail mem alloc, you bet.
  517.         jmp    gleaner
  518. fxdadr:
  519.         call    releasepages        ; free memory where this ovl should be loaded
  520. gleaner:
  521.         mov    ovlmemblk,ax        ; memory block to use
  522.         add    ax,memctlblksiz / PARSIZ; skip mem ctl blk
  523.         mov    ds,ax
  524.         mov    dx,ovlfiloff        ; where in the file is it?
  525.         mov    cl,dh
  526.         mov    dh,dl
  527.         xor    ch,ch
  528.         xor    dl,dl
  529.         shl    dx,1
  530.         rcl    cx,1            ; cx:dx = dx * 512
  531.         mov    ah,DOSSEEK        ; lseek to position
  532.         mov    al,0
  533.         mov    bx,ovlexefilhdl     ; never closing handle
  534.         int    DOS
  535.         jc    burnhead        ; oops!
  536.         xor    dx,dx
  537.         mov    cx,ovlsiz        ; number of paragraphs to load
  538.         shl    cx,1
  539.         shl    cx,1
  540.         shl    cx,1
  541.         shl    cx,1            ; * 16 = number of bytes
  542.         mov    ah,DOSREAD        ; prevent random DOS behaviour
  543.         int    DOS
  544.         jc    burnhead        ; double oops!
  545.         call    ovlrlc            ; perform relocation normally done by DOS EXE loader
  546.         pop    es            ; retrieve ovl tbl entry
  547.         or    ovlflg,MASK loaded    ; because it is now
  548.         pop    ds
  549.         pop    bp
  550.         pop    di
  551.         pop    si
  552.         pop    dx
  553.         pop    cx
  554.         pop    bx
  555.         pop    ax
  556.         ret
  557.  
  558. burnhead:
  559.         mov    al,5
  560.         mov    dx,offset nofile
  561.         jmp    putserr
  562.  
  563. loadoverlay    endp
  564.  
  565. ;-------------------------------------------------------------------------------
  566.  
  567. ovlrlc        proc    near            ; ds:0 -> the overlay to relocate
  568.  
  569.         assume    cs:code,ds:nothing,es:ovltbl
  570.  
  571.         mov    cx,ds:relocitems    ; roto-count
  572.         mov    ax,ds
  573.         add    ax,ds:hdrparas        ; skip header
  574.         mov    ovlseg,ax        ; actual code starts here
  575.         mov    di,ax
  576.         sub    di,ovlrootcode        ; segment fixup value
  577.         mov    si,ds:reloctbloff    ; ^ relocation tbl in header
  578.         jcxz    relocdone        ; not such a good idea, after all
  579. dorelocs:                    ; labels don't GET comments
  580.         lodsw                ; offset into load module
  581.         mov    bx,ax
  582.         lodsw                ; segment in load module (zero reference)
  583.         add    ax,pspadd        ; now it is psp relative
  584.         add    ax,di            ; and now it is relative to the actual load address
  585.         mov    es,ax
  586.         mov    ax,es:[bx]        ; pickup item to relocate
  587.         add    ax,pspadd        ; make it psp relative
  588.         cmp    ax,ovlrootcode        ; is it below the OVERLAY_AREA?
  589.         jc    reloccomputed        ; yup. it's relocated
  590.         cmp    ax,ovldata        ; is it above OVERLAY_AREA
  591.         jnc    reloccomputed        ; yup. it's relocated
  592.         add    ax,di            ; it's in OVERLAY_AREA, this one's ours.
  593. reloccomputed:
  594.         mov    es:[bx],ax        ; RAM it home!?!
  595.         loop    dorelocs        ; what goes around, comes around.
  596. relocdone:    ret
  597.  
  598. ovlrlc        endp
  599.  
  600. ;-------------------------------------------------------------------------------
  601.  
  602. getvictim    proc    near            ; select a victim to discard (and free up some memory)
  603.  
  604.         assume    cs:code,ds:ovltbl,es:nothing
  605.  
  606.         push    bx
  607.         push    cx
  608.         push    dx
  609.         push    si
  610.         push    di
  611.         push    bp
  612.         push    ds
  613.         mov    ds,ovltblbse        ; ^ ovl tbl
  614.         xor    ax,ax            ; will contain the low word of lru
  615.         mov    dx,ax            ; will contain the high word of lru
  616.         mov    bp,ax            ; will contain ovl tbl entry
  617.         mov    bx,ax            ; ovl tbl ptr
  618. ;         mov     cx,ovlcnt         ; number of ovl's to scan
  619. ;foon:         test     ovlflg[bx],MASK loaded  ; is this one loaded?
  620. ;         jz     skip             ; nope, try next one
  621. ;         test     ovlflg[bx],MASK locked  ; is this one loacked?
  622. ;         jnz     skip             ; yup, try next one
  623. ;         test     ovlflg[bx],MASK running ; is this one running?
  624. ;         jnz     skip             ; yup, try next one
  625. ;         mov     si,WORD PTR ovltim     ; get global lru
  626. ;         mov     di,WORD PTR ovltim+2
  627. ;         sub     si,WORD PTR ovllrudat[bx] ; subtract from ovl lru
  628. ;         sbb     di,WORD PTR ovllrudat[bx+2]
  629. ;         cmp     dx,di             ; is this one older?
  630. ;         jc     better          ; it sure is
  631. ;         jnz     skip             ; it definitely isn't
  632. ;         cmp     ax,si
  633. ;         jnc     skip             ; it really isn't
  634. ;better:     mov     ax,si             ; save the lru stuff and ovl ptr
  635. ;         mov     dx,di
  636. ;         mov     bp,bx
  637. ;skip:         add     bx,ovlsegsiz         ; do next ovl
  638. ;         loop     foon
  639. ;         or     bp,bp             ; did we find anyone to kill?
  640. ;         jnz     gotvictim         ; yes we did, partner.
  641. ;         xor     bx,bx             ; Oh well, do it again disregarding the running flag
  642.         mov    cx,ovlcnt
  643. foon1:        test    ovlflg[bx],MASK loaded
  644.         jz    skip1
  645.         test    ovlflg[bx],MASK locked
  646.         jnz    skip1
  647.         mov    si,WORD PTR ovltim
  648.         mov    di,WORD PTR ovltim+2
  649.         sub    si,WORD PTR ovllrudat[bx]
  650.         sbb    di,WORD PTR ovllrudat[bx+2]
  651.         cmp    dx,di
  652.         jc    better1
  653.         jnz    skip1
  654.         cmp    ax,si
  655.         jnc    skip1
  656. better1:    mov    ax,si
  657.         mov    dx,di
  658.         mov    bp,bx
  659. skip1:        add    bx,ovlsegsiz
  660.         loop    foon1
  661.         or    bp,bp            ; were we more successful this time?
  662.         jnz    gotvictim        ; now we got one.
  663. nomoremem:
  664.         mov    al,5            ; were really %$# now!
  665.         mov    dx,offset nocore
  666.         jmp    putserr
  667. gotvictim:
  668.         shr    bp,1            ; convert offset to segment
  669.         shr    bp,1
  670.         shr    bp,1
  671.         shr    bp,1
  672.         mov    ax,ds
  673.         add    ax,bp
  674.         pop    ds
  675.         pop    bp
  676.         pop    di
  677.         pop    si
  678.         pop    dx
  679.         pop    cx
  680.         pop    bx
  681.         ret
  682. getvictim    endp
  683.  
  684. ;-------------------------------------------------------------------------------
  685.  
  686. setrunning    proc    near            ; set running flag on overlays still running
  687.  
  688.         assume cs:code,ds:nothing,es:ovltbl
  689.  
  690.         push    es
  691.         mov    es,ovltblbse
  692.         mov    cx,ovlcnt
  693.         xor    bx,bx
  694. jim:        and    ovlflg[bx],NOT MASK running ; start by clearing them all
  695.         add    bx,ovlsegsiz
  696.         loop    jim
  697.  
  698.         ; Now chain down the stack links, setting running flags
  699.  
  700.         mov    bx,curovl
  701.         sub    bx,6
  702.         jmp    jam
  703. jamloop:
  704.         mov    ds,cs:[bx]
  705.         assume    ds:ovltbl
  706.         or    ovlflg,MASK running
  707.         sub    bx,6
  708. jam:
  709.         cmp    word ptr cs:[bx],-1    ; end of stack ?
  710.         jnz    jamloop
  711.         pop    es
  712.         ret
  713.  
  714. setrunning        endp
  715.  
  716. ;-------------------------------------------------------------------------------
  717.  
  718. int21        proc    near
  719.  
  720. ; free almost all overlay memory if app. tries to call the DOS exec function.
  721.  
  722.         cmp    ah,DOSEXEC
  723.         jz    freeall
  724.         cmp    ah,TERMINATE
  725.         jz    saybyebye
  726.         jmp    cs:oldint21
  727. saybyebye:
  728.         pop    ax            ; clean up stack
  729.         pop    ax
  730.         pop    ax
  731.         mov    al,0            ; return code 0
  732.         mov    dx,offset exitmsg
  733.         jmp    putserr
  734. freeall:
  735.         push    ax
  736.         push    bx
  737.         push    cx
  738.         push    dx
  739.         push    si
  740.         push    di
  741.         push    bp
  742.         push    es
  743.         push    ds            ; preserve calling env.
  744.  
  745.         assume cs:code,ds:nothing,es:ovltbl
  746.  
  747.         mov    ax,cs:memblk1st     ; start de-allocating from first blk
  748.         jmp    short lastblk
  749. unloadlp:
  750.         mov    ds,ax
  751.         cmp    ax,cs:ovltblbse     ; in alloced area ?
  752.         jc    nextmemblk
  753.         test    ds:memblkflg,MASK_used    ; mem blk used ?
  754.         jz    nextmemblk
  755.         mov    es,ds:memblkovl
  756.         and    ovlflg,NOT MASK loaded    ; flag overlay as unloaded
  757. nextmemblk:
  758.         mov    ax,ds:memblknxt
  759. lastblk:
  760.         or    ax,ax            ; keep going till no more
  761.         jnz    unloadlp
  762.  
  763.         mov    ax,cs:ovltblbse
  764.         add    ax,cs:ovlcnt
  765.         mov    es,ax            ; ^ to first mem blk in alloced mem
  766.         mov    es:memblksiz,2        ; adjust size
  767.         mov    es:memblknxt,0        ; no other blocks after this one
  768.         mov    es:memblkflg,0        ; not used
  769.         mov    cs:memstat,0ffh     ; memory needs to be re-alloced some day
  770.  
  771.         mov    dx,word ptr cs:oldint21
  772.         mov    ds,word ptr cs:oldint21+2
  773.         mov    ah,DOSSETVEC        ; put back DOS vector to avoid calling ourselves again!
  774.         mov    al,21h
  775.         int    DOS
  776.  
  777.         mov    es,cs:ovltblbse
  778.         mov    bx,cs:ovlcnt
  779.         add    bx,2            ; re-adjust alloced size
  780.         mov    ah,DOSREALLOC
  781.         int    DOS
  782.         pop    ds
  783.         pop    es
  784.         pop    bp
  785.         pop    di
  786.         pop    si
  787.         pop    dx
  788.         pop    cx
  789.         pop    bx
  790.         pop    ax
  791.         jmp    cs:oldint21        ; allow DOS to continue!
  792.  
  793. int21        endp
  794.  
  795. ;-------------------------------------------------------------------------------
  796.  
  797. reallocmem    proc    near
  798.  
  799. ; re-allocate our memory after a DOS exec function
  800.  
  801.         push    es
  802.         mov    ah,DOSREALLOC
  803.         mov    es,cs:ovltblbse     ; mem blk handle
  804.         mov    bx,0ffffh        ; find out how much there is
  805.         int    DOS
  806.         mov    ah,DOSREALLOC        ; re-allocate our own memory
  807.         mov    es,cs:ovltblbse
  808.         push    bx            ; contains largest available blk
  809.         int    DOS
  810.         mov    cs:memstat,0        ; flag it re-alloced
  811.         mov    ax,cs:ovltblbse
  812.         add    ax,cs:ovlcnt
  813.         mov    es,ax            ; ^ to first mem blk in alloced mem
  814.         pop    ax
  815.         sub    ax,cs:ovlcnt        ; remove ovl rbl size
  816.         mov    es:memblksiz,ax     ; fix mem blk size
  817.  
  818.         mov    ah,DOSGETVEC
  819.         mov    al,21h
  820.         int    DOS            ; get original vector
  821.         mov    word ptr cs:oldint21,bx ; save original vector
  822.         mov    word ptr cs:oldint21+2,es
  823.  
  824.         mov    ah,DOSSETVEC
  825.         mov    al,21h
  826.         mov    bx,cs
  827.         mov    ds,bx
  828.         mov    dx,OFFSET int21     ; point to int21
  829.         int    DOS            ; set vector
  830.  
  831.         pop    es
  832.         ret
  833.  
  834. reallocmem    endp
  835.  
  836. ;-------------------------------------------------------------------------------
  837.  
  838. releasepages    proc    near            ; Arg in es, result in ax
  839.  
  840. ; release any memory (and overlays) where this overlay should reside
  841.  
  842.         assume    es:ovltbl
  843.  
  844.         mov    bx,es:ovlmemblk     ; start of memory to release
  845. doitagain:
  846.         mov    ax,memblk1st        ; first memory blk
  847.         jmp    dvart
  848. dvartloop:
  849.         mov    ds,ax            ; memory blk to check
  850.         cmp    bx,ax            ; does it start below the memory to release?
  851.         jnc    dvartsmaller        ; yup
  852.         mov    dx,bx
  853.         add    dx,es:ovlsiz
  854.         add    dx,memctlblksiz / PARSIZ; end of memory to release
  855.         cmp    ax,dx            ; does it start above?
  856.         jnc    dvartsilly        ; yup
  857.         call    killmem         ; it's in the way. Zap it.
  858.         jmp    chkmemblk
  859. dvartsmaller:
  860.         add    ax,ds:memblksiz     ; end of this memory blk
  861.         cmp    bx,ax            ; does it end below the memory to release?
  862.         jnc    dvartsilly        ; yup
  863.         call    killmem         ; Oh well, zap it too.
  864. chkmemblk:                    ; was that enough?
  865.         mov    ax,ds            ; recently freed memory blk
  866.         cmp    bx,ax            ; does it start in the memory to be released?
  867.         jc    dvartsilly        ; yup, wasn't enough
  868.         mov    dx,bx
  869.         add    dx,es:ovlsiz
  870.         add    dx,memctlblksiz / PARSIZ; end of memory to be released
  871.         add    ax,ds:memblksiz     ; end of freed memory
  872.         cmp    ax,dx            ; does it end in the memory to be released?
  873.         jc    dvartsilly        ; yup, release more
  874. dvartgotblk:
  875.         mov    ax,ds            ; this is it!
  876.         mov    cx,bx
  877.         sub    cx,ax            ; # of paragraphs between start of memory to release and mem blk
  878.         jz    nosplit
  879.         call    splitblkhigh        ; split the block
  880. nosplit:
  881.         mov    cx,es:ovlsiz
  882.         add    cx,memctlblksiz / PARSIZ; paragraphs needed to load ovl
  883.         jmp    splitblklow        ; split remaining block
  884. dvartsilly:
  885.         mov    ax,ds:memblknxt
  886. dvart:
  887.         or    ax,ax            ; enf of mem list?
  888.         jz    dvartnocore
  889.         jmp    dvartloop        ; play it again Sam.
  890. dvartnocore:
  891.         mov    al,5            ; super OOPS!
  892.         mov    dx,offset nocore
  893.         jmp    putserr
  894.  
  895. releasepages    endp
  896.  
  897. ;-------------------------------------------------------------------------------
  898.  
  899. getpages    proc    near            ; get enough memory to load ovl
  900.  
  901.         mov    cx,ax
  902.         add    cx,memctlblksiz / PARSIZ; total paragraphs needed
  903.         call    largestmem        ; find largest free blk
  904.         cmp    dx,cx            ; large enough?
  905.         jnc    gotdork         ; yup.
  906. dorkkill:
  907.         call    getvictim        ; select a victim to release
  908.         call    killovl         ; kill the selected victim
  909.         cmp    ds:memblksiz,cx     ; was it enough?
  910.         jc    dorkkill        ; nope, select another one
  911. gotdork:
  912.         jmp    splitblklow        ; split the free blk
  913.  
  914. getpages    endp
  915.  
  916. ;-------------------------------------------------------------------------------
  917.  
  918. splitblklow    proc    near
  919.  
  920. ; split a block of memory returning the lower one to be used.
  921.  
  922.         push    es
  923.         or    ds:memblkflg,MASK_used    ; set low block used
  924.         mov    ax,ds
  925.         add    ax,cx
  926.         mov    es,ax            ; ^ to upper blk to be created
  927.         mov    ax,ds:memblksiz
  928.         sub    ax,cx
  929.         cmp    ax,1            ; must be at least 1 para remaining to split
  930.         jc    noodorksplit        ; don't split
  931.         mov    ds:memblksiz,cx     ; fix blk sizes
  932.         mov    es:memblksiz,ax
  933.         mov    ax,ds:memblknxt     ; fix pointers
  934.         mov    es:memblknxt,ax
  935.         mov    ds:memblknxt,es
  936.         mov    es:memblkprv,ds
  937.         mov    es:memblkflg,0        ; set upper to not used
  938.         push    ds
  939.         mov    ax,es:memblknxt
  940.         or    ax,ax
  941.         jz    domergelow
  942.         mov    ds,ax            ; fix blk after upper to point to upper
  943.         mov    ds:memblkprv,es
  944. domergelow:
  945.         mov    ax,es
  946.         mov    ds,ax
  947.         call    mergemem        ; merge remaining free memory
  948.         pop    ds
  949. noodorksplit:
  950.         pop    es
  951.         mov    ds:memblkovl,es     ; fix ptr to ovl
  952.         mov    ax,ds            ; return lower blk segment
  953.         ret
  954.  
  955. splitblklow    endp
  956.  
  957. ;-------------------------------------------------------------------------------
  958.  
  959. splitblkhigh    proc    near
  960.  
  961. ; split a block of memory returning the upper one to be used.
  962.  
  963.         push    es
  964.         mov    ax,ds
  965.         add    ax,cx
  966.         mov    es,ax            ; ^ to upper blk to be created
  967.         mov    ax,ds:memblksiz
  968.         sub    ax,cx            ; # of para remaining in upper blk
  969.         mov    ds:memblksiz,cx     ; fix blk sizes
  970.         mov    es:memblksiz,ax
  971.         mov    ax,ds:memblknxt     ; fix blk pointers
  972.         mov    es:memblknxt,ax
  973.         mov    ds:memblknxt,es
  974.         mov    es:memblkprv,ds
  975.         mov    ds:memblkflg,0        ; set lower to not used
  976.         or    es:memblkflg,MASK_used    ; set upper to used
  977.         mov    ax,es:memblknxt
  978.         or    ax,ax
  979.         jz    domergehigh
  980.         push    ds            ; fix blk after upper to point to upper
  981.         mov    ds,ax
  982.         mov    ds:memblkprv,es
  983.         pop    ds
  984. domergehigh:
  985.         call    mergemem        ; merge remaining free memory
  986. nodorksplit:
  987.         mov    ax,es
  988.         mov    ds,ax
  989.         pop    es
  990.         mov    ds:memblkovl,es     ; fix ovl ptr
  991.         mov    ax,ds            ; return upper blk segment
  992.         ret
  993.  
  994. splitblkhigh    endp
  995.  
  996. ;-------------------------------------------------------------------------------
  997.  
  998. largestmem    proc    near    ; returns seg in ax, size in dx; clobbers bx,ds,es
  999.                 ; retruns first block that's large enough if possible
  1000.  
  1001.         mov    ax,memblk1st        ; first mem blk
  1002.         xor    dx,dx            ; largest size found
  1003.         jmp    gook
  1004. gookloop:    mov    ds,ax
  1005.         test    ds:memblkflg,MASK_used    ; is this blk used?
  1006.         jnz    gookme            ; yup
  1007.         cmp    ds:memblksiz,cx     ; is it large enough?
  1008.         jc    gookme            ; nope
  1009.         mov    dx,ds:memblksiz     ; got one!
  1010.         ret
  1011. gookme:
  1012.         mov    ax,ds:memblknxt
  1013. gook:        or    ax,ax            ; end of list?
  1014.         jnz    gookloop        ; around and around
  1015.         ret
  1016.  
  1017. largestmem    endp
  1018.  
  1019. ;-------------------------------------------------------------------------------
  1020.  
  1021. killmem     proc    near
  1022.  
  1023.         test    ds:memblkflg,MASK_used    ; is it used?
  1024.         jz    memnotused        ; don't kill ovl
  1025.         push    es
  1026.         mov    es,ds:memblkovl
  1027.         and    es:ovlflg,NOT MASK loaded ; zap ovl associated with this blk
  1028.         pop    es
  1029. memnotused:
  1030.         jmp    mergemem        ; merge free memory
  1031.  
  1032. killmem     endp
  1033.  
  1034. ;-------------------------------------------------------------------------------
  1035.  
  1036. killovl     proc    near        ; preserves bx
  1037.  
  1038.         mov    ds,ax
  1039.         assume    ds:ovltbl
  1040.         and    ovlflg,NOT MASK loaded    ; ovl no longer loaded
  1041.         mov    ax,ovlmemblk        ; get mem blk
  1042.         mov    ds,ax
  1043.         jmp    mergemem        ; merge free memory
  1044.  
  1045. killovl     endp
  1046.  
  1047. ;-------------------------------------------------------------------------------
  1048.  
  1049. mergemem    proc    near
  1050.  
  1051. ; merge physically adjacent free memory blocks. Preserves es. ds -> a free block.
  1052.  
  1053.         push    es
  1054.         and    ds:memblkflg,NOT MASK_used ; set current free
  1055.         mov    ax,ds:memblkprv     ; get previous blk
  1056.         or    ax,ax            ; was there a previous blk?
  1057.         jz    gibber            ; nope
  1058.         mov    es,ax
  1059.         test    es:memblkflg,MASK_used    ; is the previous blk used?
  1060.         jnz    gibber            ; yup
  1061.         add    ax,es:memblksiz     ; end of previous blk
  1062.         mov    dx,ds
  1063.         cmp    dx,ax            ; physically adjacent?
  1064.         jnz    gibber            ; nope
  1065.         mov    ax,ds:memblksiz
  1066.         add    es:memblksiz,ax     ; adjust size of new larger blk
  1067.         mov    ax,ds:memblknxt     ; fix pointers
  1068.         mov    es:memblknxt,ax
  1069.         or    ax,ax
  1070.         jz    almostgibber
  1071.         mov    ds,ax            ; fix pointer of next blk
  1072.         mov    ds:memblkprv,es
  1073. almostgibber:
  1074.         mov    ax,es
  1075.         mov    ds,ax            ; new blk segment
  1076. gibber:
  1077.         mov    ax,ds:memblknxt     ; get next blk
  1078.         or    ax,ax            ; was there a next blk?
  1079.         jz    killdone        ; nope
  1080.         mov    es,ax
  1081.         test    es:memblkflg,MASK_used    ; is the nxt blk used?
  1082.         jnz    killdone        ; yup
  1083.         mov    ax,ds
  1084.         add    ax,ds:memblksiz     ; end of this blk
  1085.         mov    dx,es
  1086.         cmp    ax,dx            ; physically adjacent?
  1087.         jnz    killdone        ; nope
  1088.         mov    ax,es:memblksiz
  1089.         add    ds:memblksiz,ax     ; adjust size of new larger blk
  1090.         mov    ax,es:memblknxt     ; fix pointers
  1091.         mov    ds:memblknxt,ax
  1092.         or    ax,ax
  1093.         jz    killdone
  1094.         mov    es,ax            ; fix pointer of blk after nxt
  1095.         mov    es:memblkprv,ds
  1096. killdone:
  1097.         and    ds:memblkflg,NOT MASK_used ; make sure it's free
  1098.         pop    es
  1099.         ret
  1100.  
  1101. mergemem    endp
  1102.  
  1103. ;-------------------------------------------------------------------------------
  1104.  
  1105. gethdr        proc    near            ; read EXE header from handle
  1106.  
  1107.         push    cx
  1108.         mov    ax,cs
  1109.         mov    ds,ax
  1110.         mov    dx,offset hdr        ; a place to put it
  1111.         mov    bx,ovlexefilhdl     ; the file handle
  1112.         mov    cx,hdrsize        ; header size in bytes
  1113.         mov    ah,DOSREAD
  1114.         int    DOS            ; read from file
  1115.         jc    exegone         ; oops
  1116.         cmp    ax,cx            ; got correct number of bytes?
  1117.         jnz    exegone         ; nope
  1118.         pop    cx
  1119.         ret                ; Wow, it worked!
  1120. exegone:
  1121.         mov    al,5            ; You lose!
  1122.         mov    dx,offset nofile
  1123.         jmp    putserr
  1124.  
  1125. gethdr        endp
  1126.  
  1127. ;-------------------------------------------------------------------------------
  1128.  
  1129. putserr     proc    near
  1130.  
  1131. ; display error msg, close file, restore int vectors, free mem and return to DOS.
  1132.  
  1133.         push    ax            ; keep return code for later
  1134.         mov    ax,cs
  1135.         mov    ds,ax
  1136.         mov    ah,PRINT
  1137.         int    DOS            ; display error msg
  1138.         mov    dx,word ptr oldvec    ; get old vector
  1139.         cmp    dx,-1            ; was it ever replaced?
  1140.         jz    free21            ; nope
  1141.         push    ds
  1142.         mov    ds,word ptr oldvec+2
  1143.         mov    ah,DOSSETVEC        ; put it back then.
  1144.         mov    al,$$INTNO
  1145.         int    DOS
  1146.         pop    ds
  1147. free21:
  1148.         mov    dx,word ptr oldint21
  1149.         cmp    dx,-1
  1150.         jz    freemem
  1151.         push    ds
  1152.         mov    ds,word ptr oldint21+2
  1153.         mov    ah,DOSSETVEC        ; put it back then.
  1154.         mov    al,21h
  1155.         int    DOS
  1156.         pop    ds
  1157. freemem:
  1158.         mov    ax,ovltblbse        ; get memory blk segment
  1159.         cmp    ax,-1            ; was one ever allocated?
  1160.         jz    closefile        ; nope
  1161.         mov    es,ax
  1162.         mov    ah,DOSFREE        ; must free it.
  1163.         int    DOS
  1164. closefile:
  1165.         mov    bx,ovlexefilhdl     ; get file handle
  1166.         cmp    bx,-1            ; was the file ever opened?
  1167.         jz    byebye            ; nope
  1168.         mov    ah,DOSCLOSE        ; close it
  1169.         int    DOS
  1170. byebye:
  1171.         pop    ax            ; return code in al
  1172.         mov    ah,TERMINATE
  1173.         int    DOS            ; terminate this process
  1174.  
  1175. putserr     endp
  1176.  
  1177. code        ends
  1178.  
  1179.         end
  1180.