; SCCS Id: @(#)ovlmgr.asm 91/09/04 ; Copyright (c) 1989, 1990, 1991, 1992, 1993 Pierre Martineau and ; Stephen Spackman. All Rights Reserved. ; This product may be freely redistributed. See NetHack license for details. VERSION EQU 3100h PAGE 57,132 TITLE 'DOS Overlay Manager for MSC 5.1+' SUBTTL 'Copyright (c) 1989, 1990, 1991, 1992, 1993 Pierre Martineau and Stephen Spackman. All Rights Reserved.' ; Multiple overlay file support for v30a0 by Norm Meluch with some input from Stephen. ; acknowledgements: - Many thanks to Norm Meluch for his invaluable help ; - No thanks to Microsoft ; - alltrsidsctysti!!! ; - izchak and friends for impetus ; - us for brilliance ; - coffee for speed ; - others as necessary ; assumptions: - all registers are preserved including flags ; - the stack is preserved ; - re-entrancy is not required ; options: /Di386 use 80386-specific opcodes ; (faster, but no good for weaker machines) ; /DNOEMS omit EMS support ; (needed if application uses EMS) ; /DNOSPLIT omit support for external .OVL files DOSALLOC EQU 48h ; memory allocation DOSFREE EQU 49h ; free allocated memory DOSREALLOC EQU 4ah ; modify memory block DOSREAD EQU 3fh ; read bytes from handle DOSSEEK EQU 42h ; logical handle seek DOSOPEN EQU 3dh ; open handle DOSCLOSE EQU 3eh ; close handle DOSSETDTA EQU 1ah ; Set Data transfer area DOSGETDTA EQU 2fh ; Get Data transfer area DOSSEARCH EQU 4eh ; Search for 1st file match DOSNEXTFILE EQU 4fh ; Search for next file match DOSEXEC EQU 4bh ; exec child process DOSPUTC EQU 02h ; print a char DOSVERSION EQU 30h ; get version number DOSGETVEC EQU 35h ; get interrupt vector DOSGETSWITCH EQU 3700h ; get DOS switchar DOS EQU 21h ; Dos interrupt # PRINT EQU 09h ; print string TERMINATE EQU 4ch ; terminate process EMM EQU 67h ; EMM handler int vector EMMSTATUS EQU 40h ; get EMM status EMMFRAME EQU 41h ; get EMM page frame EMMTOTALS EQU 42h ; get EMM pages available EMMALLOC EQU 43h ; allocate EMM pages EMMMAP EQU 44h ; map EMM pages EMMFREE EQU 45h ; free EMM pages MAXNAMESIZE EQU 50h ; max path name size MAXFILES EQU 0Eh ; max # of *.OVL files EXESIGNUM EQU 5a4dh ; Exe header signature CR EQU 0dh LF EQU 0ah ESCAPE EQU 1bh BELL EQU 07h PARSIZ EQU 10h ; this is the size of a paragraph - this better not change! FAERIE EQU 00h ; Used for dummy segment allocation NOERR EQU 0 DOSERR EQU 1 FILEERR EQU 2 NOMEMERR EQU 3 FILEIOERR EQU 4 VICTIMERR EQU 5 RELERR EQU 6 EMSERR EQU 7 HDRERR EQU 8 NAMERR EQU 9 OVLERR EQU 10 NOHDRERR EQU 11 ; The following EXTRNs are supplied by the linker EXTRN $$OVLBASE:BYTE ; segment of OVERLAY_AREA EXTRN $$MPGSNOVL:BYTE ; ^ to module table EXTRN $$MPGSNBASE:WORD ; ^ to module segment fixups EXTRN $$INTNO:BYTE ; interrupt number to be used EXTRN $$COVL:WORD ; number of physical overlays EXTRN $$CGSN:WORD ; number of modules EXTRN $$MAIN:FAR ; ^ to function main() PUBLIC $$OVLINIT ; Our entry point ; called by the c startup code IFDEF i386 OP32 MACRO ; 32 bit operand override DB 066h ENDM pusha MACRO ; push all registers DB 060h ENDM popa MACRO ; pop all registers DB 061h ENDM ENDIF ovlflgrec RECORD locked:1=0,ems:1=0,loaded:1=0,file:4=0,pad:1 ; overlay flags ; "file" is the overlay file this overlay is in; 0 is the .EXE ; itself. Otherwise, the numbers are arbitrary. ; This is a dirty hack. What we need is a virtual segment that will be built ; by the (our) loader in multiple copies, one per overlay. Unfortunately, this ; doesn't seem to be a sensible idea in the minds of the folks at Microsoft. ; Declaring this segment AT will ensure that it never appears in the exefile, ; and ASSUME is dumb enough to be fooled. ; ; The reason we want to do this is also not-to-be-tried-at-home: it turns out ; that we can code a faster interrupt handler if we map overlay numbers to ; segment values. Normally we would consider this unacceptable programming ; practise because it is 86-mode specific, but the *need* for this entire ; programme is 86-mode specific, anyway. pspseg SEGMENT PARA AT FAERIE ; dummy segment for psp ORG 2ch ; ^ to segment of environmemt in psp pspenv LABEL WORD pspseg ENDS ovltbl SEGMENT PARA AT FAERIE ; Dummy segment definition for overlay table ; NOTE: This segment definition MUST be exactly 16 bytes long ovlflg ovlflgrec <0,0,0,0,0> ; overlay flags ovlinvcnt DB ? ; invocation count ovlmemblk DW ? ; ^ to allocated memory block ovllrudat DD ? ; misc lru data (pseudo time stamp) ovlemshdl DW ? ; ovl ems memory handle ovlfiloff DW ? ; ovl file offset in pages (512 bytes) ovlsiz DW ? ; ovl size in paragraphs ovlhdrsiz DW ? ; hdr size in paragraphs IF1 IF ($ - ovlflg) GT PARSIZ .ERR %OUT This segment MUST be no more than 16 bytes, REALLY!!! ENDIF ENDIF OVLSEGSIZ EQU PARSIZ ; this had better be true!!! (16 bytes) ovltbl ENDS DTASTRUC STRUC ; internal DTA for ovlmgr DB 21 DUP (0) file_attr DB 0 file_time DW 0 file_date DW 0 file_size DD 0 file_name DB 9 DUP (0) file_ext DB 3 DUP (0) dtapad DB 86 DUP (0) ; Pad to 128 bytes DTASTRUC ENDS EXEHDR STRUC ; structure of an EXE header exesign DW EXESIGNUM ; signature exelstpgesiz DW ? ; last page size (512 byte pages) exesiz DW ? ; total pages (including partial last page) relocitems DW ? ; number of relocation entries hdrparas DW ? ; number of paragraphs in the header minalloc DW ? ; minimum paragraph allocation maxalloc DW ? ; maximum patagraph allocation exess DW ? ; initial stack segment exesp DW ? ; initial stack pointer exechksum DW ? ; checksum exeip DW ? ; initial instruction pointer execs DW ? ; initial code segment reloctbloff DW ? ; offset from beginning of header to relocation table exeovlnum DW ? ; overlay number EXEHDR ENDS MASK_used EQU 1 ; memory block flag MEMCTLBLK STRUC ; memory block structure memblkflg DB ? ; flags memblkpad1 DB ? ; go ahead, delete me! memblknxt DW ? ; ^ to next block memblkprv DW ? ; ^ to previous block memblkovl DW ? ; ^ to overlay occupying this block memblksiz DW ? ; size in paragraphs memblkpad DB PARSIZ - memblkpad MOD PARSIZ DUP (?) ; pad to 16 bytes MEMCTLBLK ENDS MEMCTLBLKSIZ EQU TYPE MEMCTLBLK / PARSIZ ; should equal 1 paragraph ;------------------------------------------------------------------------------- code SEGMENT PUBLIC ; NOTE: the following order is optimum for alignment purposes across the ; entire INTEL 80x86 family of processors. ovltim DD ? ; pseudo-lru time variable farcall DD ? ; internal trampoline. oldvec DD -1 ; saved interrupt vector oldint21 DD -1 ; saved int 21 vector sireg DW ? ; temp save area IFDEF i386 DW ? ; for esi ENDIF dsreg DW ? ; temp save area ssreg DW ? spreg DW ? ovlfilhdl DW MAXFILES+1 DUP (-1) ; always-open file handles for .EXE, .OVL ovltblbse DW -1 ; segment of first overlay descriptor memblks DW 16 DUP (-1) ; allocated memory blocks memblk1st DW ? ; first memory block emsmemblks DW 16 DUP (-1) ; ems allocated memory blocks (64K each) curemshandle DW -1 ; currently mapped handle ovlcnt DW ? ; # overlays modcnt DW ? ; # of modules ovlrootcode DW ? ; logical segment of OVERLAY_AREA ovldata DW ? ; logical segment of OVERLAY_END pspadd DW ? ; our psp address + 10h (for relocations) emsframe DW ? ; EMM page frame segment moduletbl DD 256 DUP (?) ; module lookup table (256 modules) curovl DW OFFSET stkframe ; ^ into stack frame stkframe DW 256*3 DUP (?) ; internal stack (256 ovls deep) tempmem DW 16 DUP (-1) ; temp mem block storage intnum DW ? ; ovlmgr int number hdr EXEHDR <> ; EXE header work area DB 512-TYPE EXEHDR DUP (?) ; exe hdr buffer for relocations EXEHDRTMPSIZ EQU $ - hdr ; size of temp reloc buffer filestring DB MAXNAMESIZE DUP (0) ; string space for file specs pathlen DW ? ; path length of file spec namelen DW ? ; length of file names ovldta DTASTRUC <> ; DTA for ovlmgr use dtaseg DW ? ; DTA segment for program dtaoffset DW ? ; DTA offset for program errortbl DW -1 ; error message pointers DW OFFSET baddos DW OFFSET nofile DW OFFSET noroom DW OFFSET badio DW OFFSET nocore DW OFFSET nocore DW OFFSET badems DW OFFSET badhdr DW OFFSET badnam DW OFFSET noovl DW OFFSET nohdr DW OFFSET unknown DW OFFSET unknown DW OFFSET unknown DW OFFSET unknown emmname DB "EMMXXXX0" ; EMM device driver name emmtot DW 0 ; total emm blocks free emmframesiz DW 4 ; frame size in blocks emmflg DB 0 ; EMM present flag i386code DB '386 specific code enabled.',CR,LF,'$' memavl DB 'Conventional memory available: $' paragraphs DB 'H paragraphs.',CR,LF,'$' emsavl DB 'EMS memory available: $' pages DB 'H 16K-pages.',CR,LF,'$' baddos DB 'Incorrect DOS version. Must be 3.00 or later.$' nofile DB 'Inaccessible EXE file. Can',39,'t load overlays.$' noroom DB 'Not enough free memory left to run this program.$' badio DB 'File I/O error.$' nocore DB 'Internal memory allocation failure.$' badems DB 'EMS memory manager error.$' badhdr DB 'Executable or overlay header missing or damaged.$' badnam DB 'Unable to resolve overlay file names.$' noovl DB 'Inaccessible OVL file. Can',39,'t load overlays.$' nohdr DB 'Incomplete executable. OVL files missing?$' unknown DB 'Unknown error!$' msghead DB ESCAPE,'[0m',ESCAPE,'[K',CR,LF,ESCAPE,'[K',ESCAPE,'[1mOVLMGR:',ESCAPE,'[0m $' diag DB ESCAPE,'[K',CR,LF,ESCAPE,'[K',' ($' msgtail DB ESCAPE,'[K',CR,LF,ESCAPE,'[K',BELL,'$' ovlext DB '?.OVL',0 ;------------------------------------------------------------------------------- $$OVLINIT PROC FAR ; Init entry point ASSUME CS:code,DS:pspseg,ES:NOTHING push ax push bx push cx push dx push si push di push bp push ds push es ; save the world cld mov ax,ds ; get our psp add ax,10h mov pspadd,ax ; save it mov ah,DOSVERSION int DOS cmp al,3 ; DOS 3.0 or later jnc doenvthing mov al,DOSERR ; incorrect version of dos jmp putserr doenvthing: mov ds,pspenv ; get environment segment mov si,-1 envloop: ; search for end of environment inc si cmp WORD PTR [si],0 jnz envloop add si,4 ; point to EXE filename call openfiles ; Search & open overlay files IFNDEF NOEMS chkems: mov ah,DOSGETVEC mov al,EMM int DOS mov ax,cs mov ds,ax mov di,0ah mov si,OFFSET emmname mov cx,8 repe cmpsb mov al,0 jnz setemmflg mov al,-1 setemmflg: mov emmflg,al jnz noemshere mov ah,EMMFRAME int EMM mov emsframe,bx mov ah,EMMTOTALS int EMM mov emmtot,bx noemshere: ENDIF mov ax,SEG $$OVLBASE ; OVERLAY_AREA segment mov ovlrootcode,ax mov ax,SEG $$COVL ; segment of DGROUP mov ds,ax mov bx,$$CGSN ; number of modules mov modcnt,bx ; save for later use mov bx,$$COVL ; number of physical overlays mov ovlcnt,bx ; save for later use ; Now allocate memory mov ah,DOSALLOC ; bx contains # paras needed for ovltbl int DOS jnc gotovlram jmp buyram gotovlram: mov ovltblbse,ax ; overlay descriptor table begins at start of memory block mov cx,ovlcnt zeromem: mov es,ax mov es:ovlflg,0 ; initialise ovl flags mov es:ovlinvcnt,0 ; initialise invocation count mov es:ovlmemblk,0 mov WORD PTR es:ovllrudat,0 ; initialise ovl lru mov WORD PTR es:ovllrudat+2,0 mov es:ovlemshdl,-1 mov es:ovlfiloff,0 ; initialize file offset mov es:ovlsiz,0 ; initialize overlay size mov es:ovlhdrsiz,0 inc ax loop zeromem mov ax,cs mov ds,ax IFDEF DEBUG IFDEF i386 mov ah,print mov dx,OFFSET msghead int DOS mov ah,print mov dx,OFFSET i386code int DOS ENDIF mov ah,print mov dx,OFFSET msghead int DOS mov ah,print mov dx,OFFSET memavl int DOS mov ax,0a000h sub ax,ovltblbse call itoa mov ah,print mov dx,OFFSET paragraphs int DOS IFNDEF NOEMS mov ah,print mov dx,OFFSET msghead int DOS mov ah,print mov dx,OFFSET emsavl int DOS mov ax,emmtot call itoa mov ah,print mov dx,OFFSET pages int DOS ENDIF ENDIF ASSUME ES:ovltbl xor bp,bp xor di,di xor si,si ; file handle loop ctr filsegtbllpp: ; initialise ovl table call gethdr ; get an EXE header mov ax,ovltblbse add ax,hdr.exeovlnum mov es,ax ; ^ to ovl table entry IFNDEF NOSPLIT mov cx,si ; set file # in ovlflg shl cx,1 mov ovlflg,cl ENDIF mov ax,hdr.exesiz shl ax,1 shl ax,1 shl ax,1 shl ax,1 shl ax,1 ; * 32 mov dx,hdr.exelstpgesiz or dx,dx jz emptypage shr dx,1 shr dx,1 shr dx,1 shr dx,1 ; / 16 inc dx sub ax,20h add ax,dx emptypage: sub ax,hdr.hdrparas ; actual size of code mov ovlsiz,ax ; overlay size in paragraphs cmp hdr.exeovlnum,0 ; skip if ovl 0 (root code) jz notlargest cmp ax,di ; find largest ovl jc notlargest mov di,ax notlargest: mov ax,hdr.hdrparas shl ax,1 shl ax,1 shl ax,1 shl ax,1 mov ovlhdrsiz,ax ; hdr size in bytes mov ovlfiloff,bp ; initialise ovl file offset add bp,hdr.exesiz ; ^ to next overlay mov dx,bp mov cl,dh mov dh,dl xor ch,ch xor dl,dl shl dx,1 rcl cx,1 ; cx:dx = bp * 512 mov al,0 mov ah,DOSSEEK ; seek to next ovl int DOS mov cx,ovlcnt ; check if all overlays found mov ax,ovltblbse dec cx ; ovlcnt includes root add ax,cx ovloop: mov es,ax IFNDEF NOSPLIT mov bl,ovlflg and bx,MASK file cmp bx,0 ; if file # is 0 jne again ENDIF cmp ovlfiloff,0 ; and offset is 0 jne again jmp filsegtbllpp ; then we're still looking again: dec ax loop ovloop ASSUME ES:nothing ; prepare first memory block mov ax,ovlrootcode ; OVERLAY_AREA segment mov memblk1st,ax ; save pointer to first mem block mov es,ax mov es:memblkflg,0 ; clear mem flags mov es:memblknxt,0 ; set next to nothing mov es:memblkprv,0 ; set previous to nothing mov es:memblkovl,0 ; no overlay loaded mov es:memblksiz,di ; di contains OVERLAY_AREA size in paragraphs add ax,di mov ovldata,ax ; end of OVERLAY_END push di mov es,ovltblbse ; temporary call getemsmem ; see if any ems available mov es:ovlemshdl,-1 ; fix these! and es:ovlflg,NOT MASK ems push dx call getmoreram ; see if there are any other pieces lying around pop ax pop di or ax,ax ; any ems? jnz noramcheck inc di cmp dx,di jc buyram noramcheck: mov WORD PTR ovltim,0 ; initialise global lru time stamp mov WORD PTR ovltim+2,0 mov di,OFFSET stkframe mov WORD PTR cs:[di],-1 ; initialise stack frame add di,6 mov ax,ovltblbse mov cs:[di],ax mov curovl,di ; initialise stack frame pointer mov es,ax mov es:ovlflg,MASK locked OR MASK loaded ; set flags on ovl 0 jmp short chgintvec buyram: mov al,NOMEMERR ; free up some TSRs or something jmp putserr chgintvec: mov ax,SEG $$INTNO mov ds,ax mov al,$$INTNO ; get int number to use xor ah,ah shl ax,1 shl ax,1 mov intnum,ax call setvectors ; set up interrupt vectors mov cx,modcnt ; module count mov ax,SEG $$MPGSNBASE mov es,ax mov ax,cs mov ds,ax ASSUME DS:code mov bx,OFFSET $$MPGSNBASE ; ^ to linker provided overlay segment fixups mov si,OFFSET $$MPGSNOVL ; ^ to linker provided module table mov di,OFFSET moduletbl ; ^ to our module table modloop: mov al,es:[si] ; real physical ovl number xor ah,ah add ax,ovltblbse ; ovlctlseg address mov [di],ax ; save in module table mov ax,es:[bx] ; get seg fixup sub ax,ovlrootcode ; adjust for relative reference mov [di+2],ax ; save in module table add di,4 add bx,2 inc si loop modloop pop es pop ds pop bp pop di pop si pop dx pop cx pop bx pop ax ; restore the world jmp $$MAIN ; And away we go! $$OVLINIT ENDP ;------------------------------------------------------------------------------- ovlmgr PROC FAR ; This is the it! ASSUME DS:NOTHING,ES:NOTHING IFDEF i386 OP32 ENDIF mov sireg,si ; preserve si mov dsreg,ds ; and ds pop si ; retrieve caller ip pop ds ; " " cs push ax push bx cld lodsb ; module # to call xor ah,ah mov bx,ax lodsw ; offset in ovl to call mov WORD PTR farcall,ax ; into trampoline mov ax,si mov si,curovl ; get stack frame pointer add si,6 ; update stack mov cs:[si-4],ds ; save return seg mov cs:[si-2],ax ; and return offset shl bx,1 shl bx,1 ; * 4 (2 words/entry in module tbl) add bx,OFFSET moduletbl mov ds,cs:[bx] ; ovl tbl entry mov ax,cs:[bx+2] ; segment fixup mov cs:[si],ds ; ovl entry into stack frame mov curovl,si ASSUME DS:ovltbl IFDEF i386 OP32 ENDIF mov si,WORD PTR ovltim ; lru time stamp IFDEF i386 OP32 ENDIF inc si ; time passes! IFDEF i386 OP32 ENDIF mov WORD PTR ovltim,si ; update global clock IFDEF i386 OP32 ENDIF mov WORD PTR ovllrudat,si ; as well as ovl clock IFNDEF i386 mov si,WORD PTR ovltim+2 jz ininc ; dword increment cryupcdon: mov WORD PTR ovllrudat+2,si ; as well as ovl clock ENDIF test ovlflg,MASK loaded ; ovl loaded? jz inload ; load it or map it then. ovlloadedupc: inc ovlinvcnt add ax,ovlmemblk ; add fixup and segment address mov WORD PTR farcall+2,ax ; into trampoline IFDEF i386 OP32 ENDIF mov si,sireg ; retore all registers mov ds,dsreg pop bx pop ax popf ; don't forget these! call DWORD PTR farcall ; and GO pushf ; preserve registers again! mov dsreg,ds IFDEF i386 OP32 ENDIF mov sireg,si mov si,curovl ; stack frame pointer mov ds,cs:[si] dec ovlinvcnt sub si,6 ; adjust stack mov ds,cs:[si] ; retrieve ovl tbl entry push cs:[si+2] ; set return address push cs:[si+4] mov curovl,si IFDEF i386 OP32 ENDIF mov si,WORD PTR ovltim ; do the lru thing again IFDEF i386 OP32 ENDIF inc si IFDEF i386 OP32 ENDIF mov WORD PTR ovltim,si IFDEF i386 OP32 ENDIF mov WORD PTR ovllrudat,si IFNDEF i386 mov si,WORD PTR ovltim+2 jz outinc crydncdon: mov WORD PTR ovllrudat+2,si ENDIF test ovlflg,MASK loaded ; ovl loaded? jz outload ; better get it before someone notices jmpback: IFDEF i386 OP32 ENDIF mov si,sireg ; get registers back mov ds,dsreg iret ; and GO back IFNDEF i386 ininc: inc si mov WORD PTR ovltim+2,si ; update global and jmp cryupcdon ENDIF inload: test ovlflg,MASK ems jz infile push ax mov ax,ovlemshdl call mappage pop ax jmp ovlloadedupc infile: call loadoverlay ; self explanatory jmp ovlloadedupc IFNDEF i386 outinc: inc si mov WORD PTR ovltim+2,si jmp crydncdon ENDIF outload: test ovlflg,MASK ems jz outfile push ax mov ax,ovlemshdl call mappage pop ax jmp jmpback outfile: call loadoverlay jmp jmpback ovlmgr ENDP ;------------------------------------------------------------------------------- loadoverlay PROC NEAR ; load overlay pointed to by es ASSUME DS:NOTHING,ES:ovltbl IFDEF i386 OP32 pusha ; eax,ecx,edx,ebx,esp,ebp,esi,edi ELSE push ax push cx push dx push bx push bp push si push di ENDIF push ds push es ; just in case mov ax,ds mov es,ax cmp ovlinvcnt,0 jnz fxdadr ; Yup, it's a toughie mov ax,ovlsiz ; How much? call getpages ; never fail mem alloc, you bet. jmp gleaner fxdadr: call releasepages ; free memory where this ovl should be loaded gleaner: add ax,MEMCTLBLKSIZ ; skip mem ctl blk mov ovlmemblk,ax ; memory block to use mov ds,ax mov dx,ovlfiloff ; where in the file is it? mov cl,dh mov dh,dl xor ch,ch xor dl,dl shl dx,1 rcl cx,1 ; cx:dx = dx * 512 mov ax,ovlhdrsiz push cx push dx add dx,ax adc cx,0 ; position to code mov ah,DOSSEEK ; lseek to code mov al,0 ; from beginning of file mov bl,ovlflg and bx,MASK file mov bx,ovlfilhdl[bx] ; never closing handle int DOS jc burnhead ; oops! xor dx,dx ; buf = ds:0 mov cx,ovlsiz ; number of paragraphs to load shl cx,1 shl cx,1 shl cx,1 shl cx,1 ; * 16 = number of bytes mov ah,DOSREAD ; prevent random DOS behaviour int DOS ; read in code jc burnhead ; double oops! pop dx pop cx ; position of hdr mov ah,DOSSEEK ; lseek to hdr mov al,0 ; from beginning of file mov bl,ovlflg and bx,MASK file mov bx,ovlfilhdl[bx] ; never closing handle int DOS jc burnhead ; oops! mov cx,EXEHDRTMPSIZ ; reloc buffer size mov dx,OFFSET hdr push ds mov ax,cs mov ds,ax mov ah,DOSREAD ; prevent random DOS behaviour int DOS ; read in header pop ds jc burnhead ; double oops! call ovlrlc ; perform relocation normally done by DOS EXE loader pop es ; retrieve ovl tbl entry pop ds ASSUME DS:ovltbl,ES:NOTHING or ovlflg,MASK loaded ; because it is now IFDEF i386 OP32 popa ELSE pop di pop si pop bp pop bx pop dx pop cx pop ax ENDIF ret burnhead: mov al,FILEIOERR ; some kind of I/O error jmp putserr loadoverlay ENDP ;------------------------------------------------------------------------------- ovlrlc PROC NEAR ; ds:0 -> the overlay to relocate ASSUME DS:NOTHING,ES:ovltbl mov si,OFFSET hdr mov bp,si add bp,EXEHDRTMPSIZ ; ^ to end of buf+1 mov cx,cs:[si.relocitems] ; roto-count jcxz relocdone ; not such a good idea, after all mov di,ds sub di,ovlrootcode ; segment fixup value add si,cs:[si.reloctbloff] ; ^ relocation table dorelocs: ; labels don't GET comments cmp si,bp ; past the end ? jc getoffsetl call getnxtreloc ; get another hunk getoffsetl: mov bl,cs:[si] ; offset into load module inc si cmp si,bp ; past the end ? jc getoffseth call getnxtreloc ; get another hunk getoffseth: mov bh,cs:[si] ; offset into load module inc si cmp si,bp ; past the end ? jc getsegmentl call getnxtreloc ; get another hunk getsegmentl: mov al,cs:[si] ; segment in load module (zero reference) inc si cmp si,bp ; past the end ? jc getsegmenth call getnxtreloc ; get another hunk getsegmenth: mov ah,cs:[si] ; segment in load module (zero reference) inc si add ax,pspadd ; now it is psp relative add ax,di ; and now it is relative to the actual load address mov ds,ax mov ax,[bx] ; pickup item to relocate add ax,pspadd ; make it psp relative cmp ax,ovlrootcode ; is it below the OVERLAY_AREA? jc reloccomputed ; yup. it's relocated cmp ax,ovldata ; is it above OVERLAY_AREA jnc reloccomputed ; yup. it's relocated add ax,di ; it's in OVERLAY_AREA, this one's ours. reloccomputed: mov [bx],ax ; RAM it home!?! loop dorelocs ; what goes around, comes around. relocdone: ret ovlrlc ENDP ;------------------------------------------------------------------------------- getnxtreloc PROC NEAR ASSUME DS:NOTHING,ES:ovltbl push bx push cx push di push bp push ds push es mov cx,EXEHDRTMPSIZ ; reloc buffer size mov dx,OFFSET hdr mov ax,cs mov ds,ax mov bl,ovlflg and bx,MASK file mov bx,ovlfilhdl[bx] ; never closing handle mov ah,DOSREAD ; prevent random DOS behaviour int DOS ; read in header jnc nxtrelocok jmp burnhead ; double oops! nxtrelocok: mov si,OFFSET hdr pop es pop ds pop bp pop di pop cx pop bx ret getnxtreloc ENDP ;------------------------------------------------------------------------------- getvictim PROC NEAR ; select a victim to discard (and free up some memory) ASSUME DS:ovltbl,ES:NOTHING push bx push cx push dx push si push di push bp push ds mov ds,ovltblbse ; ^ ovl tbl IFDEF i386 OP32 ENDIF xor ax,ax ; will contain the low word of lru IFDEF i386 OP32 ENDIF mov dx,ax ; will contain the high word of lru mov bp,ax ; will contain ovl tbl entry mov bx,ax ; ovl tbl ptr mov cx,ovlcnt foon1: test ovlflg[bx],MASK locked jnz skip1 test ovlflg[bx],MASK ems jnz foon2 test ovlflg[bx],MASK loaded jz skip1 foon2: IFDEF i386 OP32 ENDIF mov si,WORD PTR ovltim IFNDEF i386 mov di,WORD PTR ovltim+2 ENDIF IFDEF i386 OP32 ENDIF sub si,WORD PTR ovllrudat[bx] IFNDEF i386 sbb di,WORD PTR ovllrudat[bx+2] ENDIF IFDEF i386 OP32 cmp dx,si ELSE cmp dx,di ENDIF IFDEF i386 jnc skip1 ELSE jc better1 jnz skip1 cmp ax,si jnc skip1 ENDIF better1: IFDEF i386 OP32 mov dx,si ELSE mov ax,si mov dx,di ENDIF mov bp,bx skip1: add bx,OVLSEGSIZ loop foon1 or bp,bp ; were we more successful this time? jnz gotvictim ; now we got one. nomoremem: mov al,VICTIMERR ; were really %$# now! jmp putserr gotvictim: shr bp,1 ; convert offset to segment shr bp,1 shr bp,1 shr bp,1 mov ax,ds add ax,bp pop ds pop bp pop di pop si pop dx pop cx pop bx ret getvictim ENDP ;------------------------------------------------------------------------------- int21 PROC FAR ; free almost all overlay memory if app. tries to call the DOS exec function. cmp ah,DOSEXEC jz freeall cmp ah,TERMINATE jz saybyebye notours: jmp cs:oldint21 saybyebye: mov al,NOERR ; return code 0 jmp putserr freeall: or al,al ; is it load and exec? jnz notours push ax push cx push dx push bx push bp push si push di push es push ds ; preserve calling env. ASSUME DS:NOTHING,ES:ovltbl mov es,ovltblbse mov cx,ovlcnt ; unload all overlays that are mov bx,OVLSEGSIZ ; in EMS or are in alloced mem. dec cx memunloadlp: test [bx.ovlflg],MASK ems jnz memunload test [bx.ovlflg],MASK loaded jz nxtmemunload mov ax,[bx.ovlmemblk] sub ax,MEMCTLBLKSIZ cmp ax,memblks ; allocated memory ? jc nxtmemunload memunload: and [bx.ovlflg],NOT MASK loaded ; you're outta there! nxtmemunload: add bx,OVLSEGSIZ loop memunloadlp mov curemshandle,-1 ; no current handle anymore mov ax,memblks cmp ax,-1 jz nosecondblk mov es,ax ; ^ to second mem blk mov es,es:memblkprv ; get previous pointer mov es:memblknxt,0 ; no other blocks after this one nosecondblk: mov cx,16 ; do all allocated mem blocks mov si,OFFSET memblks freememblklp: mov ax,cs:[si] ; get memory blk segment cmp ax,-1 ; was one ever allocated? jz nxtmemblklp ; nope mov es,ax mov ah,DOSFREE ; must free it. int DOS mov WORD PTR cs:[si],-1 nxtmemblklp: add si,2 loop freememblklp call rstvectors ; restore all int vectors mov bp,sp push [bp+22] ; ensure returned flags are based on user's! popf pop ds pop es pop di pop si pop bp pop bx pop dx pop cx pop ax mov ssreg,ss ; preserve these due to a mov spreg,sp ; DOS bug. int DOS ; allow DOS to continue! mov ss,ssreg mov sp,spreg push ax push cx push dx push bx push bp push si push di push es push ds ; preserve calling env. mov bp,sp pushf pop [bp+22] ; fix return flags call getmoreram ; re-allocate our memory call setvectors ; patch vectors again pop ds pop es pop di pop si pop bp pop bx pop dx pop cx pop ax iret int21 ENDP ;------------------------------------------------------------------------------- releasepages PROC NEAR ; Arg in es, result in ax ; release any memory (and overlays) where this overlay should reside ASSUME DS:NOTHING,ES:ovltbl mov bx,ovlmemblk ; start of memory to release sub bx,MEMCTLBLKSIZ mov dx,bx add dx,es:ovlsiz add dx,MEMCTLBLKSIZ ; end of memory to release mov ax,ovlemshdl cmp ax,-1 jz doitagain call mappage or ovlflg,MASK ems mov ax,emsframe jmp dvart doitagain: mov ax,memblk1st ; first memory blk jmp dvart dvartloop: mov ds,ax ; memory blk to check cmp bx,ax ; does it start below the memory to release? jnc dvartsmaller ; yup cmp ax,dx ; does it start above? jnc dvartnocore ; yup call killmem ; it's in the way. Zap it. jmp dvartloop dvartsmaller: add ax,ds:memblksiz ; end of this memory blk cmp bx,ax ; does it end below the memory to release? jnc dvartsilly ; yup test ds:memblkflg,MASK_used jz dvartfree call killmem ; Oh well, zap it too. add ax,ds:memblksiz ; end of this memory blk dvartfree: cmp ax,dx ; does it end in the memory to be released? jc dvartsilly dvartgotblk: mov ax,ds ; this is it! mov cx,bx sub cx,ax ; # of paragraphs between start of memory to release and mem blk jz unsplit push es call splitblk or es:memblkflg,MASK_used ; set high block used call mergemem ; merge remaining free memory mov ax,es mov ds,ax pop es unsplit: mov cx,es:ovlsiz add cx,MEMCTLBLKSIZ ; paragraphs needed to load ovl jmp splitblklow ; split remaining block dvartsilly: mov ax,ds:memblknxt dvart: or ax,ax ; end of mem list? jz dvartnocore jmp dvartloop ; play it again Sam. dvartnocore: mov al,RELERR ; super OOPS! jmp putserr releasepages ENDP ;------------------------------------------------------------------------------- getpages PROC NEAR ; get enough memory to load ovl ASSUME DS:NOTHING,ES:ovltbl mov ovlemshdl,-1 ; clear any EMS stuff and ovlflg,NOT MASK ems mov cx,ax add cx,MEMCTLBLKSIZ ; total paragraphs needed dorkagain: call largestmem ; find largest free blk cmp dx,cx ; large enough? jnc gotdork ; yup. call getemsmem ; try to allocate ems cmp dx,cx ; any available ? jnc gotdork dorkkill: call getvictim ; select a victim to release call killovl ; kill the selected victim jmp dorkagain gotdork: jmp splitblklow ; split the free blk getpages ENDP ;------------------------------------------------------------------------------- splitblklow PROC NEAR ; split a block of memory returning the lower one to be used. ASSUME DS:NOTHING,ES:NOTHING push es or ds:memblkflg,MASK_used ; set low block used call splitblk jc splitlowdone push ds mov ax,es mov ds,ax call mergemem ; merge remaining free memory pop ds splitlowdone: pop es mov ds:memblkovl,es ; fix ptr to ovl mov ax,ds ; return lower blk segment ret splitblklow ENDP ;------------------------------------------------------------------------------- splitblk PROC NEAR ASSUME DS:NOTHING,ES:NOTHING mov ax,ds add ax,cx mov es,ax ; ^ to upper blk to be created mov ax,ds:memblksiz sub ax,cx jbe nofix ; must be at least 1 para remaining to split mov ds:memblksiz,cx ; fix blk sizes mov es:memblksiz,ax mov ax,ds:memblknxt ; fix pointers mov es:memblknxt,ax mov ds:memblknxt,es mov es:memblkprv,ds mov es:memblkflg,0 ; set upper to not used mov ax,es:memblknxt or ax,ax jz nofix push ds mov ds,ax ; fix blk after upper to point to upper mov ds:memblkprv,es pop ds clc ret nofix: stc ret splitblk ENDP ;------------------------------------------------------------------------------- largestmem PROC NEAR ; returns seg in ax, size in dx ; retruns first block that's large enough if possible ASSUME DS:NOTHING,ES:ovltbl mov ax,memblk1st ; first mem blk xor dx,dx ; largest size found jmp gook gookloop: mov ds,ax test ds:memblkflg,MASK_used ; is this blk used? jnz gookme ; yup cmp ds:memblksiz,cx ; is it large enough? jc gookme ; nope mov dx,ds:memblksiz ; got one! ret gookme: mov ax,ds:memblknxt gook: or ax,ax ; end of list? jnz gookloop ; around and around ret largestmem ENDP ;------------------------------------------------------------------------------- killmem PROC NEAR ASSUME DS:NOTHING,ES:ovltbl test ds:memblkflg,MASK_used ; is it used? jz memnotused ; don't kill ovl push es mov es,ds:memblkovl and ovlflg,NOT MASK loaded ; zap ovl associated with this blk and ovlflg,NOT MASK ems pop es memnotused: jmp mergemem ; merge free memory killmem ENDP ;------------------------------------------------------------------------------- killovl PROC NEAR ; preserves bx ASSUME DS:ovltbl,ES:NOTHING mov ds,ax and ovlflg,NOT MASK loaded ; ovl no longer loaded test ovlflg,MASK ems ; was it in ems ? jz noemskill and ovlflg,NOT MASK ems ; no longer in ems mov ax,ovlemshdl call mappage noemskill: mov ax,ovlmemblk ; get mem blk sub ax,MEMCTLBLKSIZ mov ds,ax jmp mergemem ; merge free memory killovl ENDP ;------------------------------------------------------------------------------- mergemem PROC NEAR ; merge physically adjacent free memory blocks. Preserves es. ds -> a free block. ASSUME DS:NOTHING,ES:NOTHING push dx push es and ds:memblkflg,NOT MASK_used ; set current free mov ax,ds:memblkprv ; get previous blk or ax,ax ; was there a previous blk? jz gibber ; nope mov es,ax test es:memblkflg,MASK_used ; is the previous blk used? jnz gibber ; yup add ax,es:memblksiz ; end of previous blk mov dx,ds cmp dx,ax ; physically adjacent? jnz gibber ; nope mov ax,ds:memblksiz add es:memblksiz,ax ; adjust size of new larger blk mov ax,ds:memblknxt ; fix pointers mov es:memblknxt,ax or ax,ax jz almostgibber mov ds,ax ; fix pointer of next blk mov ds:memblkprv,es almostgibber: mov ax,es mov ds,ax ; new blk segment gibber: mov ax,ds:memblknxt ; get next blk or ax,ax ; was there a next blk? jz killdone ; nope mov es,ax test es:memblkflg,MASK_used ; is the nxt blk used? jnz killdone ; yup mov ax,ds add ax,ds:memblksiz ; end of this blk mov dx,es cmp ax,dx ; physically adjacent? jnz killdone ; nope mov ax,es:memblksiz add ds:memblksiz,ax ; adjust size of new larger blk mov ax,es:memblknxt ; fix pointers mov ds:memblknxt,ax or ax,ax jz killdone mov es,ax ; fix pointer of blk after nxt mov es:memblkprv,ds killdone: and ds:memblkflg,NOT MASK_used ; make sure it's free pop es pop dx mov ax,ds ret mergemem ENDP ;------------------------------------------------------------------------------- getmoreram PROC NEAR ; try to alloc remaining pieces ; of memory if any ASSUME DS:NOTHING,ES:NOTHING ; return dx = biggest block push cx push bx push si push di push ds push es xor dx,dx mov ax,memblk1st nxtlowblk: mov ds,ax mov ax,ds:memblknxt or ax,ax jnz nxtlowblk mov si,OFFSET memblks ; a place to store the handles mov di,OFFSET tempmem ; a place to store the rejects mov cx,16 ; 16 more max getramlp: mov ah,DOSALLOC mov bx,0ffffh ; Everything int DOS cmp bx,10h ; nothing smaller than .25k please jc gotallram mov ah,DOSALLOC ; allocate our own memory int DOS jc gotallram ; oops! cmp ax,ovltblbse ; is it after our first mem blk? jc releaseblk cmp dx,bx jnc notbigger mov dx,bx notbigger: mov cs:[si],ax ; save it mov es,ax mov es:memblkflg,0 ; clear mem flags mov es:memblknxt,0 ; set next to nothing mov es:memblkovl,0 ; no overlays loaded mov es:memblkprv,ds ; point to previous mov es:memblksiz,bx ; allocated memory block size mov ds:memblknxt,es ; point to next add si,2 mov ds,ax jmp short getnxtram releaseblk: mov cs:[di],ax add di,2 getnxtram: loop getramlp gotallram: mov si,OFFSET tempmem mov cx,16 releaselp: mov ax,cs:[si] cmp ax,-1 jz relnext mov es,ax mov ah,DOSFREE int DOS mov WORD PTR cs:[si],-1 relnext: add si,2 loop releaselp pop es pop ds pop di pop si pop bx pop cx ret getmoreram ENDP ;------------------------------------------------------------------------------- getemsmem PROC NEAR ASSUME DS:NOTHING,ES:ovltbl xor dx,dx ; no ems memory cmp emmflg,-1 jz testemsslots ret testemsslots: mov curemshandle,-1 mov di,OFFSET emsmemblks mov bx,cx mov cx,16 emsfreeslot: mov ax,cs:[di] cmp ax, -1 jz gotemsslot call mappage cmp ax,bx jnc foundpage add di,2 loop emsfreeslot mov cx,bx xor dx,dx ret gotemsslot: mov cx,bx mov bx,4 mov ah,EMMALLOC push cx ; paranoia ! shouldn't be necessary. push di push es int EMM pop es pop di pop cx or ah,ah jz gotsomeems xor dx,dx ret gotsomeems: mov cs:[di],dx mov ovlemshdl,dx or ovlflg,MASK ems mov ax,dx call mapemspages mov ax,emsframe mov ds,ax mov ds:memblkflg,0 ; clear mem flags mov ds:memblknxt,0 ; set next to nothing mov ds:memblkprv,0 ; set previous to nothing mov ds:memblkovl,0 ; no overlay loaded mov dx,1000h mov ds:memblksiz,dx ret foundpage: mov cx,bx mov ds,si mov dx,ax mov ax,cs:[di] mov ovlemshdl,ax or ovlflg,MASK ems ret getemsmem ENDP ;------------------------------------------------------------------------------- mappage PROC NEAR ; map a 64K block of EMS mem. ASSUME DS:NOTHING,ES:ovltbl cmp ax,curemshandle jnz doems ret doems: push bx push dx push ds push es call mapemspages mov ax,emsframe xor dx,dx xor si,si emsset: mov ds,ax test ds:memblkflg,MASK_used ; mem blk used ? jz emsfreeblk mov es,ds:memblkovl or ovlflg,MASK ems OR MASK loaded jmp emsnext emsfreeblk: mov ax,ds:memblksiz cmp dx,ax jnc emsnext mov dx,ax mov si,ds emsnext: mov ax,ds:memblknxt or ax,ax jnz emsset mov ax,dx pop es pop ds pop dx pop bx ret mappage ENDP ;------------------------------------------------------------------------------- mapemspages PROC NEAR ASSUME DS:NOTHING,ES:ovltbl push es push bx push cx push dx mov curemshandle,ax mov dx,ax mov ah,EMMMAP xor al,al ; physical page 0 xor bx,bx ; logical page 0 push dx int EMM pop dx or ah,ah jnz emmerror mov ah,EMMMAP mov al,1 ; physical page 1 mov bx,1 ; logical page 1 push dx int EMM pop dx or ah,ah jnz emmerror mov ah,EMMMAP mov al,2 ; physical page 2 mov bx,2 ; logical page 2 push dx int EMM pop dx or ah,ah jnz emmerror mov ah,EMMMAP mov al,3 ; physical page 3 mov bx,3 ; logical page 3 int EMM or ah,ah jnz emmerror mov es,ovltblbse mov cx,ovlcnt xor bx,bx testems: test ovlflg[bx],MASK ems jz nxttestems and ovlflg[bx],NOT MASK loaded nxttestems: add bx,OVLSEGSIZ loop testems pop dx pop cx pop bx pop es ret emmerror: mov al,EMSERR ; ems manager error jmp putserr mapemspages ENDP ;------------------------------------------------------------------------------- gethdr PROC NEAR ; read EXE header from handle ASSUME DS:NOTHING,ES:NOTHING mov dx,OFFSET hdr ; a place to put it mov bx,si shl bx,1 mov bx,ovlfilhdl[bx] ; the file handle readagain: mov cx,TYPE EXEHDR ; header size in bytes mov ah,DOSREAD int DOS ; read from file jc exegone ; oops? cmp ax,cx ; got correct number of bytes? je gothdr IFNDEF NOSPLIT cmp ax,0 ; Anything? je gotonxtfil ENDIF jmp exerotten IFNDEF NOSPLIT gotonxtfil: inc si cmp si,MAXFILES+1 je exegone ; We're out of files! mov bx,si shl bx,1 cmp ovlfilhdl[bx],-1 ; Any more files? je gotonxtfil ; not here. mov bx,ovlfilhdl[bx] ; Slide in new handle xor bp,bp ; reset file offset jmp readagain ENDIF gothdr: cmp hdr.exesign,EXESIGNUM ; sanity check jne exerotten ret ; Wow, it worked! exegone: mov al,NOHDRERR ; missing overlays! jmp putserr ; You lose! IFNDEF NOSPLIT exerotten: mov al,HDRERR ; corruption! jmp putserr ; You lose! ENDIF gethdr ENDP ;------------------------------------------------------------------------------- openfiles PROC NEAR ; Find our cohorts in crime push es IFNDEF NOSPLIT mov ah,DOSGETDTA ; Pick up DTA int DOS ; and mov dtaseg,es ; store mov dtaoffset,bx ; it push ds mov dx,OFFSET ovldta ; Set new DTA for file search mov ax,cs mov ds,ax ; point to the right seg mov ah,DOSSETDTA int DOS pop ds ; set this back for upcoming lodsb ENDIF mov cx,MAXNAMESIZE/2 mov bx,cs mov es,bx mov di, OFFSET filestring rep movsw ; load path from si to di IFNDEF NOSPLIT mov di, OFFSET filestring mov al,0 mov cx,MAXNAMESIZE cld repne scasb ; search null for end of string sub cx,MAXNAMESIZE neg cx mov bx,cx cmp cx,MAXNAMESIZE je checkslash dec bx ; keep string length dec di ; cause were past null now cmp bx,7 jle patherr ; "C:\.EXE" = 7 checkslash: mov ax,DOSGETSWITCH ; divine switchar int DOS ; it influences the path mov al,'\' ; if swichar = '/' pathsep = '\' cmp dl,'/' je searchslash mov al,'/' ; else pathsep = '/' searchslash: std repne scasb ; search back for '\' cld mov dx,bx sub dx,cx ; keep file name length dec dx mov cx,0 ; reset for upcoming loop mov pathlen,bx ; hold these for recall mov namelen,dx cmp dx,12 ; "LONGNAME.EXE" = 12 jle openroot ; Path name too long? patherr: mov al,NAMERR ; real problems here. jmp putserr openroot: ENDIF mov ax,cs mov ds,ax ; set ds to code mov dx, OFFSET filestring ; open root code mov al,0 ; access code mov ah,DOSOPEN int DOS ; open sez me jnc dontdie mov al,FILEERR ; can't open root jmp putserr dontdie: mov ovlfilhdl[0],ax ; save handle in array IFNDEF NOSPLIT cmp namelen,11 ; Max sized exe name (8.3)? jg bigfilename ; if not inc pathlen ; add one to path length inc namelen bigfilename: mov di,OFFSET filestring ; es is still code add di,pathlen sub di,5 ; append mov si,OFFSET ovlext ; wildcard extension mov cx,6 ; and null rep movsb ; to filestring mov cx,0 ; Match "normal" files mov dx,OFFSET filestring mov ah,DOSSEARCH int DOS ; Set DTA with Wildcard. jc aok ; Not a single match mov cx,MAXFILES ; set upcoming loop mov dx,namelen sub pathlen,dx ; shorten absolute path openloop: push cx mov bx,pathlen mov di,OFFSET filestring ; es is still in code add di,bx mov si,OFFSET ovldta.file_name mov cx,namelen ; since this *should* be rep movsb pop cx mov dx,OFFSET filestring ; path to overlay file mov al,0 ; access code mov ah,DOSOPEN int DOS ; open overlay file jnc dontdie2 fileopenerr: call itoa mov al,OVLERR ; can't open file! jmp putserr dontdie2: mov bx,cx ; put file number in bx shl bx,1 ; 2 * bx for array index mov ovlfilhdl[bx],ax ; save handle in array mov ah,DOSNEXTFILE ; Look for more files int DOS jc aok loop openloop ; open only 15 overlays aok: mov dx,dtaoffset ; Time to unset DTA mov ds,dtaseg mov ah,DOSSETDTA int DOS ENDIF pop es ret openfiles ENDP ;------------------------------------------------------------------------------- putserr PROC NEAR ; display error msg, close file, restore int vectors, free mem and return to DOS. ASSUME DS:NOTHING,ES:NOTHING xor ah,ah push ax ; keep return code for later push cs pop ds mov bx,ax shl bx,1 add bx,OFFSET errortbl mov dx,[bx] cmp dx,-1 jz freeints push dx mov dx,OFFSET msghead mov ah,PRINT int DOS pop dx mov ah,PRINT int DOS ; display error msg mov ah,PRINT mov dx,OFFSET diag int DOS pop ax push ax call itoa ; error number mov ah,DOSPUTC mov dl,':' int DOS mov ax,VERSION call itoa ; version number mov ah,DOSPUTC mov dl,':' int DOS mov ax,0a000h sub ax,ovltblbse ; conventional memory call itoa mov ah,DOSPUTC mov dl,':' int DOS mov si,OFFSET emsmemblks mov cx,16 xor ax,ax emstotlp: cmp WORD PTR cs:[si],-1 jz gotemstot add ax,emmframesiz add si,2 loop emstotlp gotemstot: call itoa ; ems usage in blocks mov ah,DOSPUTC mov dl,')' int DOS mov dx,OFFSET msgtail mov ah,PRINT int DOS freeints: call rstvectors ; restore all int vectors mov ax,ovltblbse cmp ax,-1 jz freememblks mov es,ax mov ah,DOSFREE int DOS freememblks: mov cx,16 ; do all allocated mem blocks mov si,OFFSET memblks freememlp: mov ax,cs:[si] ; get memory blk segment cmp ax,-1 ; was one ever allocated? jz nxtmemlp ; nope mov es,ax mov ah,DOSFREE ; must free it. int DOS nxtmemlp: add si,2 loop freememlp mov cx,16 ; do all allocated ems blocks mov si,OFFSET emsmemblks freeemsmemlp: mov dx,cs:[si] ; get memory blk segment cmp dx,-1 ; was one ever allocated? jz nxtemsmemlp ; nope mov ah,EMMFREE ; must free it. int EMM nxtemsmemlp: add si,2 loop freeemsmemlp closefile: IFNDEF NOSPLIT mov cx,MAXFILES+1 nextfile: mov bx,cx dec bx shl bx,1 mov bx,ovlfilhdl[bx] ; get file handle ELSE mov bx,ovlfilhdl[0] ENDIF cmp bx,-1 ; was the file ever opened? jz byebye ; nope mov ah,DOSCLOSE ; close it int DOS byebye: IFNDEF NOSPLIT loop nextfile ENDIF pop ax ; return code in al mov ah,TERMINATE int DOS ; terminate this process putserr ENDP ;------------------------------------------------------------------------------- itoa PROC NEAR push ax xchg ah,al call putbyte pop ax jmp putbyte itoa ENDP ;------------------------------------------------------------------------------- putbyte PROC NEAR push ax shr al,1 shr al,1 shr al,1 shr al,1 call nibble pop ax jmp nibble putbyte ENDP ;------------------------------------------------------------------------------- nibble PROC NEAR push ax and al,0fh add al,30h cmp al,3ah jc nibok add al,7 nibok: push dx mov dl,al mov ah,DOSPUTC int DOS pop dx pop ax ret nibble ENDP ;------------------------------------------------------------------------------- setvectors PROC NEAR push ds xor ax,ax mov ds,ax mov si,cs:intnum cli mov ax,[si] mov WORD PTR cs:oldvec,ax ; save original vector mov ax,[si+2] mov WORD PTR cs:oldvec+2,ax mov ax,OFFSET ovlmgr ; point to ovlmgr mov [si],ax ; set int vector mov [si+2],cs mov si,DOS*4 mov ax,[si] mov WORD PTR cs:oldint21,ax ; save original vector mov ax,[si+2] mov WORD PTR cs:oldint21+2,ax mov ax,OFFSET int21 ; point to new int21 mov [si],ax ; set int vector mov [si+2],cs sti pop ds ret setvectors ENDP ;------------------------------------------------------------------------------- rstvectors PROC NEAR push ds xor ax,ax mov ds,ax mov si,DOS*4 cli mov ax,WORD PTR cs:oldint21 ; put back dos vector cmp ax,-1 jz rstvec mov [si],ax mov ax,WORD PTR cs:oldint21+2 mov [si+2],ax rstvec: mov si,cs:intnum mov ax,WORD PTR cs:oldvec ; put back ovlmgr vector cmp ax,-1 jz rstdone mov [si],ax mov ax,WORD PTR cs:oldvec+2 mov [si+2],ax sti rstdone: pop ds ret rstvectors ENDP code ENDS END