; Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details ; Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details ; Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details ; Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details ; Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details ; Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details ; -*- asm -*- ; ; KLUDGE-WARNING! ; ; So you say you want to change this file, right? Are you really sure ; that's a good idea? Let me tell you a bit about the pitfalls here: ; ; * Some code runs in protected mode, some in real-mode, some in both. ; * Some code must run on a 8088 without crashing it. ; * Registers and flags may be expected to survive for a long time. ; * The code is optimized for size, not for speed or readability. ; * Some comments are parsed by other programs. ; ; You still want to change it? Oh well, go ahead, but don't come ; crying back saying you weren't warned. ; ;----------------------------------------------------------------------------- ; djgpp extender-less stub loader ; ; (C) Copyright 1993-1995 DJ Delorie ; ; Redistribution and use in source and binary forms are permitted ; provided that: (1) source distributions retain this entire copyright ; notice and comment, (2) distributions including binaries display ; the following acknowledgement: ``This product includes software ; developed by DJ Delorie and contributors to the djgpp project'' ; in the documentation or other materials provided with the distribution ; and in all advertising materials mentioning features or use of this ; software, and (3) binary distributions include information sufficient ; for the binary user to obtain the sources for the binary and utilities ; required to built and use it. Neither the name of DJ Delorie nor the ; names of djgpp's contributors may be used to endorse or promote ; products derived from this software without specific prior written ; permission. ; ; THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ; IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ; ; Revision history: ; ; 93/12/05 DJ Delorie Initial version v2.00, requires DPMI 0.9 ; 94/10/13 CW Sandmann v2.01, accumlated changes: 60K load bug, limits, cwsdpmi, optimization ; 94/10/29 CW Sandmann v2.03, M Welinder changes; cwsdpmi load anywhere, size decrease ; .copyright "The STUB.EXE stub loader is Copyright (C) 1993-1995 DJ Delorie. " .copyright "Permission granted to use for any purpose provided this copyright " .copyright "remains present and unmodified. " .copyright "This only applies to the stub, and not necessarily the whole program.\n" .id ; ;----------------------------------------------------------------------------- ; Interface to 32-bit executable: ; ; cs:eip according to COFF header ; ds 32-bit data segment for COFF program ; fs selector for our data segment (fs:0 is stubinfo) ; ss:sp our stack (ss to be freed) ; All unspecified registers have unspecified values in them. ;----------------------------------------------------------------------------- ; This is the stubinfo structure. The presence of this structure ; indicates that the executable is a djgpp v2.00 executable. ; Fields will never be deleted from this structure, only obsoleted. ; .org 0 ; just in case stubinfo: stubinfo_magic: ; char [16] .db "go32stub, v 2.05" ; version may change, [0..7] won't stubinfo_size: ; unsigned long .dd stubinfo_end ; bytes in structure stubinfo_minstack: ; unsigned long .dd 0x80000 ; minimum amount of DPMI stack space (512K) stubinfo_memory_handle: ; unsigned long .dd 0 ; DPMI memory handle stubinfo_initial_size: ; unsigned long .dd 0 ; size of initial segment stubinfo_minkeep: ; unsigned short .dw 16384 ; amount of automatic real-mode buffer stubinfo_ds_selector: ; unsigned short .dw 0 ; our DS selector (used for transfer buffer) stubinfo_ds_segment: ; unsigned short .dw 0 ; our DS segment (used for simulated calls) stubinfo_psp_selector: ; unsigned short .dw 0 ; PSP selector stubinfo_cs_selector: ; unsigned short .dw 0 ; to be freed stubinfo_env_size: ; unsigned short .dw 0 ; number of bytes of environment stubinfo_basename: ; char [8] .db 8 .dup 0 ; base name of executable to load (asciiz if < 8) stubinfo_argv0: ; char [16] .db 16 .dup 0 ; used ONLY by the application (asciiz if < 16) stubinfo_dpmi_server: ; char [16] .db "CWSDPMI.EXE\0\0\0\0\0" ; used by stub to load DPMI server if no DPMI already present .align 4 stubinfo_end: ;----------------------------------------------------------------------------- ; First, set up our memory and stack environment .start ; execution begins here push cs pop ds mov [stubinfo_ds_segment], ds mov [psp_segment], es ; save the PSP segment cld ;----------------------------------------------------------------------------- ; Check that we have DOS 3.00 or later. (We need this because earlier ; versions don't supply argv[0] to us and will scrog registers on dpmi exec). mov ah, 0x30 int 0x21 cmp al, 3 jae dos3ok mov al, 109 mov dx, msg_bad_dos jmpl error dos3ok: mov [dos_major], al mov si, stubinfo_minkeep ;----------------------------------------------------------------------------- ; Resize memory in case we need to exec a DPMI server resize_again: mov ax, [si] ; si=&stubinfo_minkeep or ax, ax jnz @f1 ; mov ax,0xfe00 ; 0 was probably 64k, so max it! (mod 512) mov ah,0xfe ; al already 0 @f1: mov bx, end_of_memory ; does not include PSP cmp bx, ax ; is our program big enough to hold it? jae @f1 mov bx, ax @f1: mov [si], bx ; si=&stubinfo_minkeep store for reference inc bh ; add 256 bytes for PSP mov cx, 0xff04 ; 0xff is for below shr bx, cl ; bytes to paragraphs mov ah, 0x4a ; ES = PSP segment from above int 0x21 ; resize our memory block jnc @f1 ; did it work? shl bx,cl ; calculate smaller [keep] value dec bh mov [si], bx ; si=&stubinfo_minkeep jmp resize_again ; and try again @f1: ;----------------------------------------------------------------------------- ; Scan environment for "PATH=" and the stub's full name after environment mov es, es:[0x2c] ; get environment segment xor di, di ; begin search for NUL/NUL (di = 0) ; mov cx, 0xff04 ; effectively `infinite' loop xor al, al .db 0xa9 ; "test ax,...." -- skip 2 bytes scan_environment: repne scasb ; search for NUL cmpw es:[di], 0x4150 ; "PA" jne not_path scasw cmpw es:[di], 0x4854 ; "TH" jne not_path scasw cmpb es:[di], '=' jne not_path inc di ; Point to PATH contents mov [path_off], di ; save for later dec di ; in case the PATH is empty not_path: scasb jne scan_environment ; no, still environment scasw ; adjust pointer to point to prog name ;; When we are spawned from a program which has more than 20 handles in use, ;; all the handles passed to us by DOS are taken (since only the first 20 ;; handles are inherited), and opening the .exe file will fail. ;; Therefore, we forcefully close handles 18 and 19, to make sure at least two ;; handles are available. mov ah, 0x3e mov bx, 19 int 0x21 ; don't care about errors mov ah, 0x3e mov bx, 18 int 0x21 ; don't care about errors ;----------------------------------------------------------------------------- ; Get DPMI information before doing anything 386-specific push es push di xor cx, cx ; flag for load attempt set cx = 0 jz @f2 ; We always jump, shorter than jmp @b1: mov al, 110 mov dx, msg_no_dpmi jmpl error @b2: or cx, cx jnz @b1 ; we already tried load once before inc cx call load_dpmi jc @b1 @f2: mov ax, 0x1687 ; get DPMI entry point int 0x2f or ax, ax jnz @b2 ; if 0 then it's there and bl, 1 ; 32 bit capable? jz @b2 @f3: mov [modesw], di ; store the DPMI entry point mov [modesw+2], es mov [modesw_mem], si pop di pop es ;----------------------------------------------------------------------------- ; Now, find the name of the program file we are supposed to load. ; xor ah, ah ; termination character (set above!) call store_env_string ; copy it to loadname, set bx mov [stubinfo_env_size], di mov [loadname_nul], si ; remember nul so we can change it to $ cmpb stubinfo_basename[0], 0 je no_symlink ;----------------------------------------------------------------------------- ; Replace the stub's file name with the link's name after the directory mov cx, 8 ; max length of basename mov di, stubinfo_basename ; pointer to new basename @b1: mov al, [di] ; get next character inc di or al, al ; end of basename? je @f1 mov [bx], al ; store character inc bx loop @b1 ; eight characters? @f1: movd [bx], 0x4558452e ; append ".EXE" add bx, 4 movb [bx], 0 ; null terminate mov [loadname_nul], bx ; remember nul so we can change it to $ no_symlink: ;----------------------------------------------------------------------------- ; Load the COFF information from the file mov ax, 0x3d00 ; open file for reading mov dx, loadname int 0x21 jcl error_no_progfile ; do rest of error message @f1: mov [program_file], ax ; store for future reference mov bx, ax mov cx, exe_header_length mov dx, exe_header mov ah, 0x3f ; read EXE header int 0x21 xor dx, dx ; dx = 0 xor cx, cx ; offset of COFF header mov ax, [exe_magic] cmp ax, 0x014c ; COFF? je file_is_just_coff cmp ax, 0x5a4d ; EXE magic value jnel error_not_exe mov dx, [exe_sectors] shl dx, 9 ; 512 bytes per sector mov bx, [exe_bytes_last_page] or bx, bx ; is bx = 0 ? je @f1 sub dh, 2 ; dx -= 512 add dx, bx @f1: file_is_just_coff: ; cx:dx is offset mov coff_offset[0], dx mov coff_offset[2], cx mov ax, 0x4200 ; seek from beginning mov bx, [program_file] int 0x21 mov cx, coff_header_length mov dx, coff_header mov ah, 0x3f ; read file (bx = handle) int 0x21 cmp ax, coff_header_length jne @f2 cmpw coff_header[coff_magic], 0x014c @f2: jnel error_not_coff mov eax, aout_header[aout_entry] mov [start_eip], eax mov ecx, [coff_offset] mov eax, text_section[s_scnptr] add eax, ecx mov [text_foffset], eax add ecx, data_section[s_scnptr] ; Ok to destroy ecx now: last use. mov [data_foffset], ecx mov ebx, bss_section[s_vaddr] add ebx, bss_section[s_size] mov eax, 0x00010001 cmp ebx, eax jae @f1 mov ebx, eax ; ensure 32-bit segment @f1: add ebx, 0x0000ffff ; ensure 64K rounded xor bx, bx ; clear rounded bits mov [stubinfo_initial_size], ebx ;----------------------------------------------------------------------------- ; Set up for the DPMI environment call include_umb mov bx, [modesw_mem] or bx, bx jz no_dos_alloc mov ah, 0x48 ; allocate memory for the DPMI host int 0x21 jcl error_no_dos_memory_umb mov es, ax no_dos_alloc: call restore_umb mov ax, 1 ; indicates a 32-bit client callf [modesw] ; enter protected mode jcl error_in_modesw ;----------------------------------------------------------------------------- ; We're in protected mode at this point. mov [stubinfo_psp_selector], es mov [stubinfo_cs_selector], cs mov ax, ds mov [stubinfo_ds_selector], ax mov es, ax xor ax, ax ; AX = 0x0000 mov cx, 1 int 0x31 ; allocate LDT descriptor jc @f2 mov [client_cs], ax xor ax, ax ; AX = 0x0000 ; mov cx, 1 ; already set above int 0x31 ; allocate LDT descriptor @f2: jcl perror_no_selectors mov [client_ds], ax ; Try getting a DPMI 1.0 memory block first, then try DPMI 0.9 ; Note: This causes the Borland Windows VxD to puke, commented for now with ;* ;* mov ax, 0x0504 ;* xor ebx, ebx ; don't specify linear address mov ecx, stubinfo_initial_size[0] ;* mov edx, 1 ; allocate committed pages ;* int 0x31 ; allocate memory block ;* jc try_old_dpmi_alloc ;* mov client_memory[0], ebx ;* mov stubinfo_memory_handle[0], esi ;* jmp got_dpmi_memory try_old_dpmi_alloc: mov ax, 0x0501 mov bx, stubinfo_initial_size[2] ; mov cx, stubinfo_initial_size[0] ;Set above int 0x31 ; allocate memory block jcl perror_no_dpmi_memory mov client_memory[2], bx mov client_memory[0], cx mov stubinfo_memory_handle[2], si mov stubinfo_memory_handle[0], di got_dpmi_memory: mov ax, 0x0007 mov bx, [client_cs] ; initialize client CS mov cx, client_memory[2] mov dx, client_memory[0] int 0x31 ; set segment base address mov ax, 0x0009 ; mov bx, [client_cs] ; already set above mov cx, cs ; get CPL and cx, 0x0003 shl cx, 5 push cx ; save shifted CPL for below or cx, 0xc09b ; 32-bit, big, code, non-conforming, readable int 0x31 ; set descriptor access rights mov ax, 0x0008 ; mov bx, [client_cs] ; already set above mov cx, stubinfo_initial_size[2] dec cx mov dx, 0xffff int 0x31 ; set segment limit mov ax, 0x0007 mov bx, [client_ds] ; initialize client DS mov cx, client_memory[2] mov dx, client_memory[0] int 0x31 ; set segment base address mov ax, 0x0009 ; mov bx, [client_ds] ; already set above pop cx ; shifted CPL from above or cx, 0xc093 ; 32-bit, big, data, r/w, expand-up int 0x31 ; set descriptor access rights mov ax, 0x0008 ; mov bx, [client_ds] ; already set above mov cx, stubinfo_initial_size[2] dec cx mov dx, 0xffff int 0x31 ; set segment limit ;----------------------------------------------------------------------------- ; Load the program data mov ax, 0x0100 mov bx, 0x0f00 ; 60K DOS block size int 0x31 ; allocate DOS memory jnc @f1 cmp ax, 0x0008 jnel perror_no_dos_memory mov ax, 0x0100 ; try again with new value in bx int 0x31 ; allocate DOS memory jcl perror_no_dos_memory @f1: mov [dos_block_seg], ax mov [dos_block_sel], dx shl bx, 4 ; paragraphs to bytes mov [dos_block_size], bx mov esi, [text_foffset] ; load text section mov edi, text_section[s_vaddr] mov ecx, text_section[s_size] call read_section mov esi, [data_foffset] ; load data section mov edi, data_section[s_vaddr] mov ecx, data_section[s_size] call read_section mov es, [client_ds] ; clear the BSS section mov edi, bss_section[s_vaddr] mov ecx, bss_section[s_size] xor eax,eax shr ecx,2 .addrsize rep stosd mov ah,0x3e mov bx, [program_file] int 0x21 ; close the file mov ax, 0x0101 mov dx, [dos_block_sel] int 0x31 ; free up the DOS memory push ds pop fs mov ds, [client_ds] .opsize jmpf fs:[start_eip] ; start program ;----------------------------------------------------------------------------- ; Read a section from the program file read_section: mov eax, esi ; sector alignment by default and eax, 0x1ff add ecx, eax sub si, ax ; sector align disk offset (can't carry) sub edi, eax ; memory maybe not aligned! mov [read_size], ecx ; store for later reference mov [read_soffset], edi call zero_regs mov dpmi_regs[dr_dx], si ; store file offset shr esi, 16 mov dpmi_regs[dr_cx], si mov bx, [program_file] mov dpmi_regs[dr_bx], bx movw dpmi_regs[dr_ax], 0x4200 call pm_dos ; seek to start of data ; Note, handle set above mov ax, [dos_block_seg] mov dpmi_regs[dr_ds], ax movw dpmi_regs[dr_dx], 0 ; store file offset read_loop: movb dpmi_regs[dr_ah], 0x3f mov ax, read_size[2] ; see how many bytes to read or ax, ax jnz read_too_big mov ax, read_size[0] cmp ax, [dos_block_size] jna read_size_in_ax ; jna shorter than jmp read_too_big: mov ax, [dos_block_size] read_size_in_ax: mov dpmi_regs[dr_cx], ax call pm_dos ; read the next chunk of file data xor ecx, ecx mov cx, dpmi_regs[dr_ax] ; get byte count mov edi, [read_soffset] ; adjust pointers add [read_soffset], ecx sub [read_size], ecx xor esi, esi ; esi=0 offset for copy data shr cx, 2 ; ecx < 64K push ds push es mov es, [client_ds] mov ds, [dos_block_sel] .addrsize rep movsd pop es pop ds add ecx, [read_size] ; ecx zero from the rep movsd jnz read_loop ret ;----------------------------------------------------------------------------- ; Routine to check al for delimiter test_delim: cmp al, ':' ; watch for file name part je @f3 cmp al, '/' je @f3 cmp al, '\\' @f3: ret ;----------------------------------------------------------------------------- ; Copy string from environment to loadname. ; On entry: di = environment offset ; ah = termination character (null also does) ; On exit: bx = pointer to one character after last observed file delimiter ; di = pointer to one character after last copied ; si = pointer to the copied termination character ; al = terminating character store_env_string: mov si, loadname ; pointer to buffer mov bx, si ; in case no delimiters @b1: mov al, es:[di] ; copy a character to buffer inc di mov [si], al cmp al, ah ; end of file name? je @f1 or al, al ; end of file name? je @f1 inc si call test_delim jne @b1 mov bx, si ; remember pointer to first char of je @b1 ; next name component (shorter than jmp) @f1: ret ;----------------------------------------------------------------------------- ; Most errors come here, early ones jump direct (8088 instructions) error_no_progfile: mov al, 102 mov dx, msg_no_progfile jmp error_fn error_not_exe: mov al, 103 mov dx, msg_not_exe jmp error_fn error_not_coff: mov al, 104 mov dx, msg_not_coff ; jmp error_fn error_fn: push dx mov bx, [loadname_nul] ; error, print file name movb [bx], '$' mov bx, loadname jmp @f1 error_no_dos_memory_umb: call restore_umb error_no_dos_memory: mov al, 105 mov dx, msg_no_dos_memory jmp error error_in_modesw: mov al, 106 mov dx, msg_error_in_modesw jmp error perror_no_selectors: mov al, 107 mov dx, msg_no_selectors jmp error perror_no_dpmi_memory: mov al, 108 mov dx, msg_no_dpmi_memory jmp error perror_no_dos_memory: mov al, 105 mov dx, msg_no_dos_memory ; jmp error error: push dx mov bx, err_string @f1: call printstr pop bx call printstr exit: mov bx, crlfdollar call printstr mov ah, 0x4c ; error exit - exit code is in al int 0x21 printstr1: inc bx push ax ; have to preserve al set by error call mov ah, 2 int 0x21 pop ax ; restore ax (John A.) printstr: mov dl, [bx] cmp dl, '$' jne printstr1 ret crlfdollar: .db 13,10,'$' ;----------------------------------------------------------------------------- ; DPMI utility functions zero_regs: push ax push cx push di xor ax, ax mov di, dpmi_regs mov cx, 0x19 rep stosw pop di pop cx pop ax ret pm_dos: mov ax, 0x0300 ; simulate interrupt mov bx, 0x0021 ; int 21, no flags xor cx, cx ; cx = 0x0000 (copy no args) mov edi, dpmi_regs int 0x31 ret ;----------------------------------------------------------------------------- ; load DPMI server if not present ; First check directory from which stub is loaded, then path, then default ; On entry di points to image name path_off: .dw 0 ; If stays zero, no path load_dpmi: xor ah, ah ; Copy until this character (=0) call store_env_string ; copy stub image to "loadname" mov si, bx ; remove name so we can add DPMI name mov di, [path_off] ; Pointer to path contents (next try) jmp @f2 loadloop: mov ah, ';' ; Copy until this character call store_env_string ; to "loadname" or al,al ; Check terminating character jne @f1 ; If ';', continue dec di ; else point at null for next pass. @f1: cmp si, loadname ; anything there? je do_exec ; final try (no path) let it return mov al, [si-1] call test_delim ; is final character a path delimiter je @f2 movb [si], '\\' ; no, add separator between path & name inc si @f2: call do_exec ; copy our name to string and try load jc loadloop ret ;----------------------------------------------------------------------------- ; add the string CWSDPMI to path ending do_exec: call include_umb mov bx, stubinfo_dpmi_server @b1: mov al, [bx] mov [si], al inc bx inc si or al, al jne @b1 ; movw [si], 0x0a0d ;debug ; movb [si+2], '$' ;debug push es ; Save in case of failure push di ;memory saving - use dpmi_regs as a temporary parameter block push ds pop es ;zero_regs needs es set call zero_regs mov bx, dpmi_regs mov [bx+4], ds ;segment of command tail mov [bx+2], bx ;offset (point to zero) mov dx, loadname ; mov ah, 9 ;debug ; int 0x21 ;debug mov ax, 0x4b00 ;Do program exec int 0x21 pop di pop es jc @f1 ;carry set if exec failed mov ah, 0x4d ;get return code int 0x21 sub ax, 0x300 ;ah=3 TSR, al=code (success) neg ax ;CY, if not originally 0x300 @f1: jmp restore_umb ;called func. return for us. ;----------------------------------------------------------------------------- ; Make upper memory allocatable. Clobbers Ax and Bx. include_umb: cmpb [dos_major], 5 ; Won't work before dos 5 jb @f1 mov ax, 0x5800 ; get allocation strategy int 0x21 mov [old_strategy],al mov ax, 0x5802 ; Get UMB status. int 0x21 mov [old_umb],al mov ax, 0x5801 mov bx, 0x0080 ; first fit, first high then low int 0x21 mov ax, 0x5803 mov bx, 0x0001 ; include UMB in memory chain int 0x21 @f1: ret ; Restore upper memory status. All registers and flags preserved. restore_umb: pushf cmpb [dos_major], 5 ; Won't work before dos 5 jb @f1 push ax push bx mov ax, 0x5803 ; restore UMB status. mov bl,[old_umb] xor bh, bh int 0x21 mov ax, 0x5801 ; restore allocation strategy mov bl,[old_strategy] xor bh, bh int 0x21 pop bx pop ax @f1: popf ret ;----------------------------------------------------------------------------- ; Stored Data err_string: .db "Load error: $" msg_no_progfile: .db ": can't open$" msg_not_exe: .db ": not EXE$" msg_not_coff: .db ": not COFF (Check for viruses)$" msg_no_dpmi: .db "no DPMI - Get csdpmi*b.zip$" msg_no_dos_memory: .db "no DOS memory$" msg_bad_dos: .db "need DOS 3$" msg_error_in_modesw: .db "can't switch mode$" msg_no_selectors: .db "no DPMI selectors$" msg_no_dpmi_memory: .db "no DPMI memory$" ;----------------------------------------------------------------------------- ; Unstored Data, available during and after mode switch last_generated_byte: .align 512 ; Align ourselves to a sector ; boundary for startup speed. .bss ; data after this isn't in file. modesw: ; address of DPMI mode switch .dd 0 modesw_mem: ; amount of memory DPMI needs .dw 0 program_file: ; file ID of program data .dw 0 text_foffset: ; offset in file .dd 0 data_foffset: ; offset in file .dd 0 start_eip: ; EIP value to start at .dd 0 client_cs: ; must follow start_eip .dw 0 client_ds: .dw 0 client_memory: .dd 0 dos_block_seg: .dw 0 dos_block_sel: .dw 0 dos_block_size: .dw 0 read_soffset: .dd 0 read_size: .dd 0 dpmi_regs: .db 0x32 .dup 0 dr_edi = 0x00 dr_di = 0x00 dr_esi = 0x04 dr_si = 0x04 dr_ebp = 0x08 dr_bp = 0x08 dr_ebx = 0x10 dr_bx = 0x10 dr_bl = 0x10 dr_bh = 0x11 dr_edx = 0x14 dr_dx = 0x14 dr_dl = 0x14 dr_dh = 0x15 dr_ecx = 0x18 dr_cx = 0x18 dr_cl = 0x18 dr_ch = 0x19 dr_eax = 0x1c dr_ax = 0x1c dr_al = 0x1c dr_ah = 0x1d dr_efl = 0x20 dr_es = 0x22 dr_ds = 0x24 dr_fs = 0x26 dr_gs = 0x28 dr_ip = 0x2a dr_cs = 0x2c dr_sp = 0x2e dr_ss = 0x30 ;----------------------------------------------------------------------------- .align 16 ; so that stack ends on para boundary .dw 128 .dup 0 .stack ;----------------------------------------------------------------------------- ; At one time real mode only data. Header stuff now used during image load. psp_segment: .dw 0 loadname_nul: ; offset of NUL so it can become '$' .dw 0 loadname: ; name of program file to load, if it .db 81 .dup 0 ; gets really long ok to overwrite next exe_header: ; loaded from front of loadfile exe_magic: .dw 0 exe_bytes_last_page: .dw 0 exe_sectors: .dw 0 exe_header_length = . - exe_header coff_offset: .dd 0 ; from start of file coff_header: ; loaded from after stub .db 20 .dup 0 aout_header: .db 28 .dup 0 text_section: .db 40 .dup 0 data_section: .db 40 .dup 0 bss_section: .db 40 .dup 0 coff_header_length = . - coff_header old_strategy: .db 0 old_umb: .db 0 dos_major: .db 0 .align 16 ; Align ourselves to a paragraph end_of_memory: ; resize is done early so must keep all ;----------------------------------------------------------------------------- ; structure definitions ; coff_magic = 0 ; from coff header aout_entry = 16 ; from aout header s_paddr = 8 ; from section headers s_vaddr = 12 s_size = 16 s_scnptr = 20