venkat@matrix.UUCP (D Venkatrangan) (05/25/90)
In article <935@ashton.UUCP> tomr@ashton.UUCP (Tom Rombouts) writes: >I am posting this for a friend. Please no flames if obvious answer: > >Does anyone have any experience writing overlays (via DOS EXEC 4Bh AL=3) >in assembler using an .EXE file format ?. The reference manual I have talks >about .COM file formats only and just says .EXE formats are handled >differently. I'd like to see a simple shell of how this is done. Thanks. > >Tom Rombouts, Ashton-Tate Torrance, Voice: (213) 538-7108 The following will load an .EXE file, after allocating its CS and 64K DS using DOS 48h call. After loading, you need to perhaps initialize things in the overlay. The following code calls the function at the first byte of the DOS memory segment. This means that the .EXE file has to be prepared in a special way. The following segments of code should be enough to guide you through some of the steps involved. D. Venkatrangan, Matrix Computer Systems, Voice: (603) 888-7790 ---------------- CODE TO LOAD OVERLAY ---------------------- ifdef LARGECODE ovload_text segment word public 'code' ASSUME cs:ovload_text, ss:nothing, ds:nothing else _text segment word public 'code' ASSUME cs:_text, ss:nothing, ds:nothing endif ; ; Caller: ; sets up ovload_frame ; ; Returns: ; ax = 0, if success ; ax = error code, if failure ; ; Side effects: ; causes the .EXE module to be loaded into memory public _ov_load ; ; _ov_load(char *filename, char *options) ; ovload_frame struc saved_bp dw ? ifdef LARGECODE ret_addr dd ? else ret_addr dw ? endif ifdef LARGEDATA filename_seg dw ? filename_off dw ? options dd ? else filename dw ? options dw ? endif ovload_frame ends ; parameter block for the Load Overlay call ; param_block struc start_seg dw ? rel_factor dw ? param_block ends param_block_size equ 4 ; In some versions of DOS, the load function 4b trashes all registers ; except CS:IP. Save the registers for this case. saved_sp dw ? saved_ss dw ? ifdef LARGECODE _ov_load proc far else _ov_load proc near endif push bp mov bp, sp sub sp, param_block_size ; param block off stack push ds ; push registers push es push bx push cx push dx mov ax, ss mov cs:saved_ss, ax ; and save ss:sp mov cs:saved_sp, sp ; ; allocate a fixed amount of memory for the overlay. ; after we load the file, the initialization code will shrink this ; mov ah, 48h mov bx, 1000h ; 64K for now int 21h jnc alloc_okay cmp ax, 8 je retry jmp mcb_error retry: mov ah, 48h int 21h ; failed -- try new size jnc alloc_okay mcb_error: cmp ax, 8 jne mcb_error_1 mov ax, OVERR_NOMEM jmp error mcb_error_1: mov ax, OVERR_MCB jmp error ; here we are, if alloc went off alright. ; NOTE: we may have less than 64K at this point. ; if this is insufficient to load our program, the load call ; should fail. ifdef not_done XXX the above fact is not yet completely tested... endif alloc_okay: mov bx, bp ; start of param block in bx sub bx, param_block_size mov word ptr [bx].start_seg, ax mov word ptr [bx].rel_factor, ax ifdef LARGEDATA ; set filename mov dx, word ptr [bp].filename_seg mov ds, dx mov dx, word ptr [bp].filename_off else mov dx, word ptr [bp].filename endif mov ax, 4b03h ; load overlay int 21h jnc load_okay ; error? load_error_1: cmp ax, 1 jne load_error_2 mov ax, OVERR_BUG ; shouldn't happen jmp error load_error_2: cmp ax, 2 jne load_error_5 mov ax, OVERR_NOFILE ; file not found jmp error load_error_5: cmp ax, 5 jne load_error_8 mov ax, OVERR_ACCESS ; access denied jmp error load_error_8: cmp ax, 8 jne load_error_x mov ax, OVERR_NOMEM ; access denied jmp error load_error_x: mov ax, OVERR_BUG jmp error load_okay: mov es, word ptr [bp-param_block_size].start_seg cmp byte ptr es:0, 0eah ; minimal check je valid_code mov ax, OVERR_BADOVFILE ; bad overlay file jmp error valid_code: push es ; set up the call address xor ax, ax push ax ; ; pass the overlay options address to overlay initialization code ; ifdef LARGEDATA mov bx, word ptr [bp+2].options ; seg options push bx else push ds endif mov bx, word ptr [bp].options ; off options push bx mov bx, sp call dword ptr ss:[bx+4] ; call overlay initializtion ; it should return status ; in ax (one of OVERR_) add sp, +8 error: mov bx, cs:saved_ss ; restore regs mov ss, bx mov sp, cs:saved_sp pop dx pop cx pop bx pop es pop ds mov sp, bp pop bp ret _ov_load endp ifdef LARGECODE ovload_text ends else _text ends endif END Now, some tips on preparing the .EXE file. Create a segment with a single jmp instruction to init of overlay. ---------------- START SEGMENT ------------------------ ; NOTE: after the overlay is loaded through the 4b03h call, the init code ; in OV is run. But since the caller of 4b03h does not know the entry ; point of the code to run, the first segment (which is loaded at the ; location the file was loaded) contains a single instruction to jump ; to the location of the overlay initialization code. START_TEXT SEGMENT db 0eah ; jmp op code dw offset TRANSIENT_TEXT:_init_overlay dw seg TRANSIENT_TEXT START_TEXT ENDS Place this segment as the first segment using an include file such as: (Avoid linking crt0.obj) ---------------- SEGMENT ORDERING ------------------------ START_SEGMENT SEGMENT START_SEGMENT ENDS START_TEXT SEGMENT word public 'CODE' START_TEXT ENDS _TEXT SEGMENT word public 'CODE' _TEXT ENDS _DATA SEGMENT word public 'DATA' _DATA ENDS STACK SEGMENT word public 'STACK' STACK ENDS CONST SEGMENT WORD PUBLIC 'CONST' CONST ENDS _BSS SEGMENT WORD PUBLIC 'BSS' _BSS ENDS DGROUP GROUP CONST, _BSS, _DATA ; now place the transient segments ; Segments named TRANSIENT_ contain code and data that are used during the ; initialization of the overlay. They are not included in the size ; calculation for the resident part of the 1 code. If you have C modules ; that you wish to discard after overlay initialization, change their class to ; 'TSTACK', 'TDATA' or 'TTEXT' TRANSIENT_TEXT SEGMENT para public 'TCODE' TRANSIENT_TEXT ENDS TRANSIENT_DATA SEGMENT word public 'TDATA' TRANSIENT_DATA ENDS ---------------- OVERLAY INITIALIZING ------------------------ ; Structure Templates mcb STRUC ; memory control block structure TypeMCB db ? ; block type OwnerMCB dw ? ; block owner SizeMCB dw ? ; block size mcb ENDS public __acrtused public __atopsp public __aheap public __asizheap public __asizds public __asizovl public _errno public __doserrno public __osversion public __osmajor public __osminor _DATA segment ; ; define these here, to avoid getting the ones in crt0.obj ; __acrtused dw 1 __atopsp dw ? __aheap dw ? __asizheap dw ? __asizds dw ? __asizovl dw ? _errno dw ? __doserrno dw ? __osversion label word __osmajor db ? __osminor db ? _DATA ends STACK segment db OVSTK_SIZE dup (?) STACK ends TRANSIENT_DATA segment ifdef sccs sccsid db '%W% %G%' endif TRANSIENT_DATA ends TRANSIENT_TEXT segment ASSUME cs:TRANSIENT_TEXT, ss:nothing, ds:nothing extrn _locate_int2f:near extrn _install_ivt:near public _init_overlay saved_ss dw ? saved_sp dw ? params dd ? _init_overlay proc far push bp mov cs:saved_ss, ss mov cs:saved_sp, sp mov bp, sp mov ax, [bp+4] ; we are passed params on stack mov word ptr cs:params+2, ax mov ax, [bp+6] mov word ptr cs:params, ax mov di, DGROUP ; set up stack mov sp, OVSTK_SIZE cli mov ss, di add sp, offset DGROUP:STACK ; ax is now top of stack and sp, 0fffeh ; even boundary sub sp, 2 ; leave one word sti mov ss:__atopsp, sp mov si, 1000h ; 64K data segment add si, di ; si now at DS start + 64K ; ; modify memory at start segment to the required size ; mov ax, seg START_SEGMENT mov es, ax ; set up memory block to shrink mov bx, seg START_SEGMENT sub bx, si neg bx ; bx = end of prog - start of prog mov ah, 4ah ; modify memory -- may expand int 21h jnc mcb_done ; no errors cmp ax, 8 ; insufficient memory je retry jmp mcb_error ; some other mcb error ; ; could not modify memory -- too large a DS? see if maximum size possible for ; block, which is returned on the call from modify memory is enough to run ; our code. retry: mov ax, ss:__atopsp add ax, 0fh ; round to next para mov cl, 4 shr ax, cl ; ax now is top of stack in paragraphs inc ax ; round to next add ax, di ; ax now at end of DS in para add bx, seg START_SEGMENT sub bx, ax ; we need up to ax atleast cmp bx, 0 jg L100 mov ax, OVERR_NOMEM jmp done ; ; what we have may be enough to get going, but may fail later ; to provide much dynamic memory. L100: int 21h jnc mcb_done mcb_error: cmp ax, 7 ; memory destroyed jne L200 mov ax, OVERR_MCB ; we probably can't recover anyway jmp done L200: cmp ax, 8 ; memory destroyed jne L201 mov ax, OVERR_NOMEM jmp done L201: cmp ax, 9 ; incorrect es jne retry mov ax, OVERR_BADES jmp done ; ; we get here if the memory block is adjusted atleast for degraded performance ; ; ; get the actual size of overlay, size of DS and size of heap mcb_done: mov bx, seg START_SEGMENT ; size of ovlay is size of MCB dec bx ; start of MCB mov es, bx xor di, di mov bx, es:[di].SizeMCB ; bx is size of ovlay in paragraphs mov ss:__asizovl, bx mov ax, bx sub ax, seg DGROUP add ax, seg START_SEGMENT mov cl, 4 shl ax, cl ; ax = size of DS in bytes dec ax ; less one byte to avoid 64K==0 mov ss:__asizds, ax mov ax, ss:__atopsp add ax, 2 ; two bytes beyond top of stack mov ss:__aheap, ax ; start of heap mov bx, ss:__asizds sub bx, ax ; bx = size of heap mov ss:__asizheap, bx ; ; init DS and SS ; push ss ; set DS and ES pop es push ss pop ds ; ; zero out the _BSS area, ie, all bytes from start of _BSS to STACK ; cld mov di, offset DGROUP:_BSS mov cx, offset DGROUP:STACK sub cx, di xor ax, ax rep stosb ; set __osversion mov ah, 30h int 21 mov word ptr ds:__osversion,ax call _locate_int2f ; gets ISR address jnc done ; if not present proceed to install ; set IVT entry to be our function not_present: call _install_ivt ; install new IVT, 2f ; ax set to proper error ; ... in install_int2f done: mov ss, cs:saved_ss mov sp, cs:saved_sp pop bp ret ; return if present _init_overlay endp TRANSIENT_TEXT ends END