home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / src / stub / stub.asm < prev    next >
Encoding:
Assembly Source File  |  1996-10-05  |  23.7 KB  |  952 lines

  1. ; Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details
  2. ; Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
  3. ; -*- asm -*-
  4. ;
  5. ; KLUDGE-WARNING!
  6. ;
  7. ; So you say you want to change this file, right?  Are you really sure
  8. ; that's a good idea?  Let me tell you a bit about the pitfalls here:
  9. ;
  10. ; * Some code runs in protected mode, some in real-mode, some in both.
  11. ; * Some code must run on a 8088 without crashing it.
  12. ; * Registers and flags may be expected to survive for a long time.
  13. ; * The code is optimized for size, not for speed or readability.
  14. ; * Some comments are parsed by other programs.
  15. ;
  16. ; You still want to change it?  Oh well, go ahead, but don't come
  17. ; crying back saying you weren't warned.
  18. ;
  19. ;-----------------------------------------------------------------------------
  20. ;  djgpp extender-less stub loader
  21. ;
  22. ;  (C) Copyright 1993-1995 DJ Delorie
  23. ;
  24. ;  Redistribution and use in source and binary forms are permitted
  25. ;  provided that: (1) source distributions retain this entire copyright
  26. ;  notice and comment, (2) distributions including binaries display
  27. ;  the following acknowledgement:  ``This product includes software
  28. ;  developed by DJ Delorie and contributors to the djgpp project''
  29. ;  in the documentation or other materials provided with the distribution
  30. ;  and in all advertising materials mentioning features or use of this
  31. ;  software, and (3) binary distributions include information sufficient
  32. ;  for the binary user to obtain the sources for the binary and utilities
  33. ;  required to built and use it. Neither the name of DJ Delorie nor the
  34. ;  names of djgpp's contributors may be used to endorse or promote
  35. ;  products derived from this software without specific prior written
  36. ;  permission.
  37. ;
  38. ;  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  39. ;  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  40. ;  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  41. ;
  42. ;  Revision history:
  43. ;
  44. ;  93/12/05 DJ Delorie    Initial version v2.00, requires DPMI 0.9
  45. ;  94/10/13 CW Sandmann v2.01, accumlated changes: 60K load bug, limits, cwsdpmi, optimization
  46. ;  94/10/29 CW Sandmann v2.03, M Welinder changes; cwsdpmi load anywhere, size decrease
  47. ;
  48.    .copyright "The STUB.EXE stub loader is Copyright (C) 1993-1995 DJ Delorie. "
  49.    .copyright "Permission granted to use for any purpose provided this copyright "
  50.    .copyright "remains present and unmodified. "
  51.    .copyright "This only applies to the stub, and not neccessarily the whole program.\n"
  52.    .id
  53. ;
  54. ;-----------------------------------------------------------------------------
  55. ;  Interface to 32-bit executable:
  56. ;
  57. ;    cs:eip    according to COFF header
  58. ;    ds        32-bit data segment for COFF program
  59. ;    fs        selector for our data segment (fs:0 is stubinfo)
  60. ;    ss:sp    our stack (ss to be freed)
  61. ;    <others>    All unspecified registers have unspecified values in them.
  62. ;-----------------------------------------------------------------------------
  63. ;  This is the stubinfo structure.  The presence of this structure
  64. ;  indicates that the executable is a djgpp v2.00 executable.
  65. ;  Fields will never be deleted from this structure, only obsoleted.
  66. ;
  67.     .org    0            ; just in case
  68. stubinfo:
  69. stubinfo_magic:                ; char [16]
  70.     .db    "go32stub, v 2.00"    ; version may change, [0..7] won't
  71. stubinfo_size:                ; unsigned long
  72.     .dd    stubinfo_end        ; bytes in structure
  73. stubinfo_minstack:            ; unsigned long
  74.     .dd    0x40000            ; minimum amount of DPMI stack space (256K)
  75. stubinfo_memory_handle:            ; unsigned long
  76.     .dd    0            ; DPMI memory handle
  77. stubinfo_initial_size:            ; unsigned long
  78.     .dd    0            ; size of initial segment
  79. stubinfo_minkeep:            ; unsigned short
  80.     .dw    16384            ; amount of automatic real-mode buffer
  81. stubinfo_ds_selector:            ; unsigned short
  82.     .dw    0            ; our DS selector (used for transfer buffer)
  83. stubinfo_ds_segment:            ; unsigned short
  84.     .dw    0            ; our DS segment (used for simulated calls)
  85. stubinfo_psp_selector:            ; unsigned short
  86.     .dw    0            ; PSP selector
  87. stubinfo_cs_selector:            ; unsigned short
  88.     .dw    0            ; to be freed
  89. stubinfo_env_size:            ; unsigned short
  90.     .dw    0            ; number of bytes of environment
  91. stubinfo_basename:            ; char [8]
  92.     .db    8 .dup 0        ; base name of executable to load (asciiz if < 8)
  93. stubinfo_argv0:                ; char [16]
  94.     .db    16 .dup 0        ; used ONLY by the application (asciiz if < 16)
  95. stubinfo_dpmi_server:            ; char [16]
  96.     .db    "CWSDPMI.EXE\0\0\0\0\0"    ; used by stub to load DPMI server if no DPMI already present
  97.  
  98.     .align    4
  99. stubinfo_end:
  100.  
  101. ;-----------------------------------------------------------------------------
  102. ;  First, set up our memory and stack environment
  103.  
  104.     .start                ; execution begins here
  105.     push    cs
  106.     pop    ds
  107.     mov    [stubinfo_ds_segment], ds
  108.  
  109.     mov    [psp_segment], es    ; save the PSP segment
  110.     cld
  111.  
  112. ;-----------------------------------------------------------------------------
  113. ;  Check that we have DOS 3.00 or later.  (We need this because earlier
  114. ;  versions don't supply argv[0] to us and will scrog registers on dpmi exec).
  115.     mov    ah, 0x30
  116.     int    0x21
  117.     cmp    al, 3
  118.     jae    dos3ok
  119.     mov    dx, msg_bad_dos
  120.     jmpl    error
  121. dos3ok:
  122.     mov    [dos_major], al
  123.  
  124. ;-----------------------------------------------------------------------------
  125. ;  Resize memory in case we need to exec a DPMI server
  126.  
  127. resize_again:
  128.     mov    bx, end_of_memory    ; does not include PSP
  129.     mov    ax, [stubinfo_minkeep]
  130.     cmp    bx, ax            ; is our program big enough to hold it?
  131.     jae    @f1
  132.     mov    bx, ax
  133. @f1:
  134.     mov    [stubinfo_minkeep], bx    ; store for reference
  135.     inc    bh            ; add 256 bytes for PSP
  136.     mov    cx, 0xff04        ; 0xff is for below
  137.     shr    bx, cl            ; bytes to paragraphs
  138.  
  139.     mov    ah, 0x4a        ; ES = PSP segment from above
  140.     int    0x21            ; resize our memory block
  141.     jnc    @f1            ; did it work?
  142.     shl    bx,cl            ; calculate smaller [keep] value
  143.     dec    bh
  144.     mov    [stubinfo_minkeep], bx
  145.     jmp    resize_again        ; and try again
  146. @f1:
  147.  
  148. ;-----------------------------------------------------------------------------
  149. ;  Scan environment for "PATH=" and the stub's full name after environment
  150.  
  151.     mov    es, es:[0x2c]        ; get environment segment
  152.     xor    di, di            ; begin search for NUL/NUL (di = 0)
  153. ;    mov    cx, 0xff04        ; effectively `infinite' loop
  154.     xor     al, al
  155.     .db    0xa9            ; "test ax,...." -- skip 2 bytes
  156. scan_environment:
  157.     repne
  158.     scasb                ; search for NUL
  159.     cmpw    es:[di], 0x4150        ; "PA"
  160.     jne    not_path
  161.     scasw
  162.     cmpw    es:[di], 0x4854        ; "TH"
  163.     jne    not_path
  164.     scasw
  165.     cmpb    es:[di], '='
  166.     jne    not_path
  167.     inc    di            ; Point to PATH contents
  168.     mov    [path_off], di        ; save for later
  169.     dec    di            ; in case the PATH is empty
  170. not_path:
  171.     scasb
  172.     jne    scan_environment    ; no, still environment
  173.     scasw                ; adjust pointer to point to prog name
  174.  
  175. ;-----------------------------------------------------------------------------
  176. ;  Get DPMI information before doing anything 386-specific
  177.  
  178.     push    es
  179.     push    di
  180.     xor    cx, cx            ; flag for load attempt set cx = 0
  181.     jz    @f2            ; We always jump, shorter than jmp
  182. @b1:
  183.     mov    dx, msg_no_dpmi
  184.     jmpl    error
  185. @b2:
  186.     or    cx, cx
  187.     jnz    @b1            ; we already tried load once before
  188.     inc    cx
  189.     call    load_dpmi
  190.     jc    @b1
  191. @f2:
  192.     mov    ax, 0x1687        ; get DPMI entry point
  193.     int    0x2f
  194.     or    ax, ax
  195.     jnz    @b2            ; if 0 then it's there
  196.     and    bl, 1            ; 32 bit capable?
  197.     jz    @b2
  198. @f3:
  199.     mov    [modesw], di        ; store the DPMI entry point
  200.     mov    [modesw+2], es
  201.     mov    [modesw_mem], si
  202.     pop    di
  203.     pop    es
  204.  
  205. ;-----------------------------------------------------------------------------
  206. ;  Now, find the name of the program file we are supposed to load.
  207.  
  208. ;    xor    ah, ah            ; termination character (set above!)
  209.     call    store_env_string    ; copy it to loadname, set bx
  210.  
  211.     mov    [stubinfo_env_size], di
  212.     mov    [loadname_nul], si    ; remember nul so we can change it to $
  213.     cmpb    stubinfo_basename[0], 0
  214.     je    no_symlink
  215.  
  216. ;-----------------------------------------------------------------------------
  217. ;  Replace the stub's file name with the link's name after the directory
  218.  
  219.     mov    cx, 8            ; max length of basename
  220.     mov    di, stubinfo_basename    ; pointer to new basename
  221. @b1:
  222.     mov    al, [di]        ; get next character
  223.     inc    di
  224.     or    al, al            ; end of basename?
  225.     je    @f1
  226.     mov    [bx], al        ; store character
  227.     inc    bx
  228.     loop    @b1            ; eight characters?
  229. @f1:
  230.     movd    [bx+0], 0x4558452e    ; append ".EXE"
  231.     add    bx, 4
  232.     mov    [bx], al        ; al = 0
  233.     mov    [loadname_nul], bx    ; remember nul so we can change it to $
  234.  
  235. no_symlink:
  236.  
  237. ;-----------------------------------------------------------------------------
  238. ;  Load the COFF information from the file
  239.  
  240.     mov    ax, 0x3d00        ; open file for reading
  241.     mov    dx, loadname
  242.     int    0x21
  243.     jcl    error_no_progfile    ; do rest of error message
  244.  
  245. @f1:
  246.     mov    [program_file], ax    ; store for future reference
  247.  
  248.     mov    bx, ax
  249.     mov    cx, exe_header_length
  250.     mov    dx, exe_header
  251.     mov    ah, 0x3f        ; read EXE header
  252.     int    0x21
  253.  
  254.     xor    dx, dx            ; dx = 0
  255.     xor    cx, cx            ; offset of COFF header
  256.  
  257.     mov    ax, [exe_magic]
  258.     cmp    ax, 0x014c        ; COFF?
  259.     je    file_is_just_coff
  260.     cmp    ax, 0x5a4d        ; EXE magic value
  261.     jnel    error_not_exe
  262.  
  263.     mov    dx, [exe_sectors]
  264.     shl    dx, 9            ; 512 bytes per sector
  265.     mov    bx, [exe_bytes_last_page]
  266.     or    bx, bx            ; is bx = 0 ?
  267.     je    @f1
  268.     sub    dh, 2            ; dx -= 512
  269.     add    dx, bx
  270. @f1:
  271.  
  272. file_is_just_coff:            ; cx:dx is offset
  273.     mov    coff_offset[0], dx
  274.     mov    coff_offset[2], cx
  275.     mov    ax, 0x4200        ; seek from beginning
  276.     mov    bx, [program_file]
  277.     int    0x21
  278.  
  279.     mov    cx, coff_header_length
  280.     mov    dx, coff_header
  281.     mov    ah, 0x3f        ; read file (bx = handle)
  282.     int    0x21
  283.  
  284.     cmp    ax, coff_header_length
  285.     jne    @f2
  286.     cmpw    coff_header[coff_magic], 0x014c
  287. @f2:
  288.     jnel    error_not_coff
  289.  
  290.     mov    eax, aout_header[aout_entry]
  291.     mov    [start_eip], eax
  292.  
  293.     mov    ecx, [coff_offset]
  294.  
  295.     mov    eax, text_section[s_scnptr]
  296.     add    eax, ecx
  297.     mov    [text_foffset], eax
  298.  
  299.     mov    eax, data_section[s_scnptr]
  300.     add    eax, ecx
  301.     mov    [data_foffset], eax
  302.  
  303.     mov    ebx, bss_section[s_vaddr]
  304.     mov    eax, bss_section[s_size]
  305.     add    ebx, eax
  306.     mov    eax, 0x00010001
  307.     cmp    ebx, eax
  308.     jae    @f1
  309.     mov    ebx, eax         ; ensure 32-bit segment
  310. @f1:
  311.     add    ebx, 0x0000ffff        ; ensure 64K rounded
  312.     xor    bx, bx            ; clear rounded bits
  313.     mov    [stubinfo_initial_size], ebx
  314.  
  315. ;-----------------------------------------------------------------------------
  316. ;  Set up for the DPMI environment
  317.  
  318.     call    include_umb
  319.     mov    bx, [modesw_mem]
  320.     or    bx, bx
  321.     jz    no_dos_alloc
  322.  
  323.     mov    ah, 0x48        ; allocate memory for the DPMI host
  324.     int    0x21
  325.     jcl    error_no_dos_memory_umb
  326.     mov    es, ax
  327.  
  328. no_dos_alloc:
  329.     call    restore_umb
  330.     mov    ax, 1            ; indicates a 32-bit client
  331.     callf    [modesw]        ; enter protected mode
  332.  
  333.     jcl    error_in_modesw
  334.  
  335. ;-----------------------------------------------------------------------------
  336. ; We're in protected mode at this point.
  337.  
  338.     mov    [stubinfo_psp_selector], es
  339.     mov    [stubinfo_cs_selector], cs
  340.     mov    ax, ds
  341.     mov    [stubinfo_ds_selector], ax
  342.     mov    es, ax
  343.  
  344.     xor    ax, ax            ; AX = 0x0000
  345.     mov    cx, 1
  346.     int    0x31            ; allocate LDT descriptor
  347.     jc    @f2
  348.     mov    [client_cs], ax
  349.  
  350.     xor    ax, ax            ; AX = 0x0000
  351. ;    mov    cx, 1            ; already set above
  352.     int    0x31            ; allocate LDT descriptor
  353. @f2:
  354.     jcl    perror_no_selectors
  355.     mov    [client_ds], ax
  356.  
  357. ; Try getting a DPMI 1.0 memory block first, then try DPMI 0.9
  358. ; Note:  This causes the Borland Windows VxD to puke, commented for now with ;*
  359. ;*    mov    ax, 0x0504
  360. ;*    xor    ebx, ebx        ; don't specify linear address
  361.     mov    ecx, stubinfo_initial_size[0]
  362. ;*    mov    edx, 1            ; allocate committed pages
  363. ;*    int    0x31            ; allocate memory block
  364. ;*    jc    try_old_dpmi_alloc
  365. ;*    mov    client_memory[0], ebx
  366. ;*    mov    stubinfo_memory_handle[0], esi
  367. ;*    jmp    got_dpmi_memory
  368. try_old_dpmi_alloc:
  369.     mov    ax, 0x0501
  370.     mov    bx, stubinfo_initial_size[2]
  371. ;    mov    cx, stubinfo_initial_size[0]    ;Set above
  372.     int    0x31            ; allocate memory block
  373.     jcl    perror_no_dpmi_memory
  374.     mov    client_memory[2], bx
  375.     mov    client_memory[0], cx
  376.     mov    stubinfo_memory_handle[2], si
  377.     mov    stubinfo_memory_handle[0], di
  378. got_dpmi_memory:
  379.  
  380.     mov    ax, 0x0007
  381.     mov    bx, [client_cs]        ; initialize client CS
  382.     mov    cx, client_memory[2]
  383.     mov    dx, client_memory[0]
  384.     int    0x31            ; set segment base address
  385.  
  386.     mov    ax, 0x0009
  387. ;    mov    bx, [client_cs]        ; already set above
  388.     mov    cx, cs            ; get CPL
  389.     and    cx, 0x0003
  390.     shl    cx, 5
  391.     push    cx            ; save shifted CPL for below
  392.     or    cx, 0xc09b        ; 32-bit, big, code, non-conforming, readable
  393.     int    0x31            ; set descriptor access rights
  394.  
  395.     mov    ax, 0x0008
  396. ;    mov    bx, [client_cs]        ; already set above
  397.     mov    cx, stubinfo_initial_size[2]
  398.     dec    cx
  399.     mov    dx, 0xffff
  400.     int    0x31            ; set segment limit
  401.  
  402.     mov    ax, 0x0007
  403.     mov    bx, [client_ds]        ; initialize client DS
  404.     mov    cx, client_memory[2]
  405.     mov    dx, client_memory[0]
  406.     int    0x31            ; set segment base address
  407.  
  408.     mov    ax, 0x0009
  409. ;    mov    bx, [client_ds]        ; already set above
  410.     pop    cx            ; shifted CPL from above
  411.     or    cx, 0xc093        ; 32-bit, big, data, r/w, expand-up
  412.     int    0x31            ; set descriptor access rights
  413.  
  414.     mov    ax, 0x0008
  415. ;    mov    bx, [client_ds]        ; already set above
  416.     mov    cx, stubinfo_initial_size[2]
  417.     dec    cx
  418.     mov    dx, 0xffff
  419.     int    0x31            ; set segment limit
  420.  
  421. ;-----------------------------------------------------------------------------
  422. ;  Load the program data
  423.  
  424.     mov    ax, 0x0100
  425.     mov    bx, 0x0f00        ; 60K DOS block size
  426.     int    0x31            ; allocate DOS memory
  427.     jnc    @f1
  428.     cmp    ax, 0x0008
  429.     jnel    perror_no_dos_memory
  430.     mov    ax, 0x0100        ; try again with new value in bx
  431.     int    0x31            ; allocate DOS memory
  432.     jcl    perror_no_dos_memory
  433. @f1:
  434.     mov    [dos_block_seg], ax
  435.     mov    [dos_block_sel], dx
  436.     shl    bx, 4            ; paragraphs to bytes
  437.     mov    [dos_block_size], bx
  438.  
  439.     mov    esi, [text_foffset]    ; load text section
  440.     mov    edi, text_section[s_vaddr]
  441.     mov    ecx, text_section[s_size]
  442.     call    read_section
  443.  
  444.     mov    esi, [data_foffset]    ; load data section
  445.     mov    edi, data_section[s_vaddr]
  446.     mov    ecx, data_section[s_size]
  447.     call    read_section
  448.  
  449.     mov    es, [client_ds]        ; clear the BSS section
  450.     mov    edi, bss_section[s_vaddr]
  451.     mov    ecx, bss_section[s_size]
  452.     xor    eax,eax
  453.     shr    ecx,2
  454.     .addrsize
  455.     rep
  456.     stosd
  457.  
  458.     mov    ah,0x3e
  459.     mov    bx, [program_file]
  460.     int    0x21            ; close the file
  461.  
  462.     mov    ax, 0x0101
  463.     mov    dx, [dos_block_sel]
  464.     int    0x31            ; free up the DOS memory
  465.  
  466.     push    ds
  467.     pop    fs
  468.     mov    ds, [client_ds]
  469.     .opsize
  470.     jmpf    fs:[start_eip]        ; start program
  471.  
  472. ;-----------------------------------------------------------------------------
  473. ;  Read a section from the program file
  474.  
  475. read_section:
  476.     mov    eax, esi        ; sector alignment by default
  477.     and    eax, 0x1ff
  478.     add    ecx, eax
  479.     sub    si, ax            ; sector align disk offset (can't carry)
  480.     sub    edi, eax        ; memory maybe not aligned!
  481.  
  482.     mov    [read_size], ecx    ; store for later reference
  483.     mov    [read_soffset], edi
  484.  
  485.     call    zero_regs
  486.     mov    dpmi_regs[dr_dx], si    ; store file offset
  487.     shr    esi, 16
  488.     mov    dpmi_regs[dr_cx], si
  489.     mov    bx, [program_file]
  490.     mov    dpmi_regs[dr_bx], bx
  491.     movw    dpmi_regs[dr_ax], 0x4200
  492.     call    pm_dos            ; seek to start of data
  493.  
  494. ; Note, handle set above
  495.     mov    ax, [dos_block_seg]
  496.     mov    dpmi_regs[dr_ds], ax
  497.     movw    dpmi_regs[dr_dx], 0    ; store file offset
  498. read_loop:
  499.     movb    dpmi_regs[dr_ah], 0x3f
  500.     mov    ax, read_size[2]    ; see how many bytes to read
  501.     or    ax, ax
  502.     jnz    read_too_big
  503.     mov    ax, read_size[0]
  504.     cmp    ax, [dos_block_size]
  505.     jna    read_size_in_ax        ; jna shorter than jmp
  506. read_too_big:
  507.     mov    ax, [dos_block_size]
  508. read_size_in_ax:
  509.     mov    dpmi_regs[dr_cx], ax
  510.     call    pm_dos            ; read the next chunk of file data
  511.  
  512.     xor    ecx, ecx
  513.     mov    cx, dpmi_regs[dr_ax]    ; get byte count
  514.     mov    edi, [read_soffset]    ; adjust pointers
  515.     add    [read_soffset], ecx
  516.     sub    [read_size], ecx
  517.  
  518.     xor    esi, esi        ; esi=0 offset for copy data
  519.     shr    cx, 2            ; ecx < 64K
  520.     push    ds
  521.     push    es
  522.     mov    es, [client_ds]
  523.     mov    ds, [dos_block_sel]
  524.     .addrsize
  525.     rep
  526.     movsd
  527.     pop    es
  528.     pop    ds
  529.  
  530.     add    ecx, [read_size]    ; ecx zero from the rep movsd
  531.     jnz    read_loop
  532.  
  533.     ret
  534.  
  535. ;-----------------------------------------------------------------------------
  536. ;  Routine to check al for delimiter
  537.  
  538. test_delim:
  539.     cmp    al, ':'            ; watch for file name part
  540.     je    @f3
  541.     cmp    al, '/'
  542.     je    @f3
  543.     cmp    al, '\\'
  544. @f3:
  545.     ret
  546.  
  547. ;-----------------------------------------------------------------------------
  548. ;  Copy string from environment to loadname.
  549. ;   On entry: di = environment offset
  550. ;             ah = termination character (null also does)
  551. ;   On exit:  bx = pointer to one character after last observed file delimiter
  552. ;             di = pointer to one character after last copied
  553. ;             si = pointer to the copied termination character
  554. ;             al = terminating character
  555.  
  556. store_env_string:
  557.     mov    si, loadname        ; pointer to buffer
  558.     mov    bx, si            ; in case no delimiters
  559. @b1:
  560.     mov    al, es:[di]        ; copy a character to buffer
  561.     inc    di
  562.     mov    [si], al
  563.     cmp    al, ah            ; end of file name?
  564.     je    @f1
  565.     or    al, al            ; end of file name?
  566.     je    @f1
  567.     inc    si
  568.     call    test_delim
  569.     jne    @b1
  570.     mov    bx, si            ; remember pointer to first char of
  571.     je    @b1            ; next name component (shorter than jmp)
  572. @f1:
  573.     ret
  574.  
  575. ;-----------------------------------------------------------------------------
  576. ;  Most errors come here, early ones jump direct (8088 instructions)
  577.  
  578. error_no_progfile:
  579.     mov    dx, msg_no_progfile
  580.     jmp    error_fn
  581.  
  582. error_not_exe:
  583.     mov    dx, msg_not_exe
  584.     jmp    error_fn
  585.  
  586. error_not_coff:
  587.     mov    dx, msg_not_coff
  588. ;    jmp    error_fn
  589.  
  590. error_fn:
  591.     push    dx
  592.     mov    bx, [loadname_nul]    ; error, print file name
  593.     movb    [bx], '$'
  594.     mov    bx, loadname
  595.     jmp    @f1
  596.  
  597. error_no_dos_memory_umb:
  598.     call    restore_umb
  599. error_no_dos_memory:
  600.     mov    dx, msg_no_dos_memory
  601.     jmp    error
  602.  
  603. error_in_modesw:
  604.     mov    dx, msg_error_in_modesw
  605.     jmp    error
  606.  
  607. perror_no_selectors:
  608.     mov    dx, msg_no_selectors
  609.     jmp    error
  610.  
  611. perror_no_dpmi_memory:
  612.     mov    dx, msg_no_dpmi_memory
  613.     jmp    error
  614.  
  615. perror_no_dos_memory:
  616.     mov    dx, msg_no_dos_memory
  617. ;    jmp    error
  618.  
  619. error:
  620.     push    dx
  621.     mov    bx, err_string
  622. @f1:
  623.     call    printstr
  624.     pop    bx
  625.     call    printstr
  626. exit:
  627.     mov    bx, crlfdollar
  628.     call    printstr
  629.     mov    ax, 0x4cff        ; error exit
  630.     int    0x21
  631.  
  632. printstr1:
  633.     inc    bx
  634.     mov    ah, 2
  635.     int    0x21
  636. printstr:
  637.     mov    dl, [bx]
  638.     cmp    dl, '$'
  639.     jne    printstr1
  640.     ret
  641.  
  642. crlfdollar:
  643.     .db    13,10,'$'
  644. ;-----------------------------------------------------------------------------
  645. ;  DPMI utility functions
  646.  
  647. zero_regs:
  648.     push    ax
  649.     push    cx
  650.     push    di
  651.     xor    ax, ax
  652.     mov    di, dpmi_regs
  653.     mov    cx, 0x19
  654.     rep
  655.     stosw
  656.     pop    di
  657.     pop    cx
  658.     pop    ax
  659.     ret
  660.  
  661. pm_dos:
  662.     mov    ax, 0x0300        ; simulate interrupt
  663.     mov    bx, 0x0021        ; int 21, no flags
  664.     xor     cx, cx            ; cx = 0x0000 (copy no args)
  665.     mov    edi, dpmi_regs
  666.     int    0x31
  667.     ret
  668.  
  669. ;-----------------------------------------------------------------------------
  670. ;  load DPMI server if not present
  671. ;   First check directory from which stub is loaded, then path, then default
  672. ;   On entry di points to image name
  673.  
  674. path_off:
  675.     .dw    0            ; If stays zero, no path
  676.  
  677. load_dpmi:
  678.     xor    ah, ah            ; Copy until this character (=0)
  679.     call    store_env_string    ; copy stub image to "loadname"
  680.     mov    si, bx            ; remove name so we can add DPMI name
  681.     mov    di, [path_off]        ; Pointer to path contents (next try)
  682.     jmp    @f2
  683. loadloop:
  684.     mov    ah, ';'            ; Copy until this character
  685.     call    store_env_string    ; to "loadname"
  686.     cmp    si, loadname        ; anything there?
  687.     je    do_exec            ; final try (no path) let it return
  688.     mov    al, [si-1]
  689.     call    test_delim        ; is final character a path delimiter
  690.     je    @f2
  691.     movb    [si], '\\'        ; no, add separator between path & name
  692.     inc    si
  693. @f2:
  694.     call    do_exec            ; copy our name to string and try load
  695.     jc    loadloop
  696.     ret
  697.  
  698. ;-----------------------------------------------------------------------------
  699. ; add the string CWSDPMI to path ending
  700.  
  701. do_exec:
  702.     call    include_umb
  703.     mov    bx, stubinfo_dpmi_server
  704. @b1:
  705.     mov    al, [bx]
  706.     mov    [si], al
  707.     inc    bx
  708.     inc    si
  709.     or    al, al
  710.     jne    @b1
  711. ;    movw    [si], 0x0a0d        ;debug
  712. ;    movb    [si+2], '$'        ;debug
  713.  
  714.     push    es            ; Save in case of failure
  715.     push    di
  716.  
  717. ;memory saving - use dpmi_regs as a temporary parameter block
  718.     push    ds
  719.     pop    es            ;zero_regs needs es set
  720.     call    zero_regs
  721.     mov    bx, dpmi_regs
  722.     mov    [bx+4], ds        ;segment of command tail
  723.     mov    [bx+2], bx        ;offset (point to zero)
  724.  
  725.     mov    dx, loadname
  726. ;    mov    ah, 9            ;debug
  727. ;    int    0x21            ;debug
  728.     mov    ax, 0x4b00        ;Do program exec
  729.     int    0x21
  730.     pop    di
  731.     pop    es
  732.     jc    @f1            ;carry set if exec failed
  733.  
  734.     mov    ah, 0x4d        ;get return code
  735.     int    0x21
  736.     sub    ax, 0x300        ;ah=3 TSR, al=code (success)
  737.     neg    ax            ;CY, if not originally 0x300
  738. @f1:
  739.     jmp    restore_umb        ;called func. return for us.
  740.  
  741. ;-----------------------------------------------------------------------------
  742. ; Make upper memory allocatable.  Clobbers Ax and Bx.
  743.  
  744. include_umb:
  745.     cmpb    [dos_major], 5        ; Won't work before dos 5
  746.     jb    @f1
  747.     mov    ax, 0x5800        ; get allocation strategy
  748.     int    0x21
  749.     mov    [old_strategy],al
  750.     mov    ax, 0x5802        ; Get UMB status.
  751.     int    0x21
  752.     mov    [old_umb],al
  753.     mov    ax, 0x5801
  754.     mov    bx, 0x0080        ; first fit, first high then low
  755.     int    0x21
  756.     mov    ax, 0x5803
  757.     mov    bx, 0x0001        ; include UMB in memory chain
  758.     int    0x21
  759. @f1:
  760.     ret
  761.  
  762. ; Restore upper memory status.  All registers and flags preserved.
  763.  
  764. restore_umb:
  765.     pushf
  766.     cmpb    [dos_major], 5        ; Won't work before dos 5
  767.     jb    @f1
  768.     push    ax
  769.     push    bx
  770.     mov    ax, 0x5803        ; restore UMB status.
  771.     mov    bl,[old_umb]
  772.     xor    bh, bh
  773.     int    0x21
  774.     mov    ax, 0x5801        ; restore allocation strategy
  775.     mov    bl,[old_strategy]
  776.     xor    bh, bh
  777.     int    0x21
  778.     pop    bx
  779.     pop    ax
  780. @f1:
  781.     popf
  782.     ret
  783.  
  784. ;-----------------------------------------------------------------------------
  785. ;  Stored Data
  786. err_string:
  787.     .db    "Load error: $"
  788. msg_no_progfile:
  789.     .db    ": cannot open$"
  790. msg_not_exe:
  791.     .db    ": not EXE$"
  792. msg_not_coff:
  793.     .db    ": not COFF (Check for viruses)$"
  794. msg_no_dpmi:
  795.     .db    "no DPMI - Get csdpmi*b.zip$"
  796. msg_no_dos_memory:
  797.     .db    "no DOS memory$"
  798. msg_bad_dos:
  799.     .db    "need DOS 3$"
  800. msg_error_in_modesw:
  801.     .db    "can't switch mode$"
  802. msg_no_selectors:
  803.     .db    "no DPMI selectors$"
  804. msg_no_dpmi_memory:
  805.     .db    "no DPMI memory$"
  806.  
  807. ;-----------------------------------------------------------------------------
  808. ;  Unstored Data, available during and after mode switch
  809.  
  810. last_generated_byte:
  811.  
  812.     .align    512            ; Align ourselves to a sector
  813.                     ;  boundary for startup speed.
  814.     .bss                ; data after this isn't in file.
  815.  
  816. modesw:                    ; address of DPMI mode switch
  817.     .dd    0
  818. modesw_mem:                ; amount of memory DPMI needs
  819.     .dw    0
  820.  
  821. program_file:                ; file ID of program data
  822.     .dw    0
  823.  
  824. text_foffset:                ; offset in file
  825.     .dd    0
  826.  
  827. data_foffset:                ; offset in file
  828.     .dd    0
  829.  
  830. start_eip:                ; EIP value to start at
  831.     .dd    0
  832. client_cs:                ; must follow start_eip
  833.     .dw    0
  834. client_ds:
  835.     .dw    0
  836.  
  837. client_memory:
  838.     .dd    0
  839.  
  840. dos_block_seg:
  841.     .dw    0
  842. dos_block_sel:
  843.     .dw    0
  844. dos_block_size:
  845.     .dw    0
  846.  
  847. read_soffset:
  848.     .dd    0
  849. read_size:
  850.     .dd    0
  851.  
  852. dpmi_regs:
  853.     .db    0x32 .dup 0
  854. dr_edi = 0x00
  855. dr_di  = 0x00
  856. dr_esi = 0x04
  857. dr_si  = 0x04
  858. dr_ebp = 0x08
  859. dr_bp  = 0x08
  860. dr_ebx = 0x10
  861. dr_bx  = 0x10
  862. dr_bl  = 0x10
  863. dr_bh  = 0x11
  864. dr_edx = 0x14
  865. dr_dx  = 0x14
  866. dr_dl  = 0x14
  867. dr_dh  = 0x15
  868. dr_ecx = 0x18
  869. dr_cx  = 0x18
  870. dr_cl  = 0x18
  871. dr_ch  = 0x19
  872. dr_eax = 0x1c
  873. dr_ax  = 0x1c
  874. dr_al  = 0x1c
  875. dr_ah  = 0x1d
  876. dr_efl = 0x20
  877. dr_es  = 0x22
  878. dr_ds  = 0x24
  879. dr_fs  = 0x26
  880. dr_gs  = 0x28
  881. dr_ip  = 0x2a
  882. dr_cs  = 0x2c
  883. dr_sp  = 0x2e
  884. dr_ss  = 0x30
  885.  
  886. ;-----------------------------------------------------------------------------
  887.  
  888.     .align    16            ; so that stack ends on para boundary
  889.     .dw    128 .dup 0
  890.     .stack
  891.  
  892. ;-----------------------------------------------------------------------------
  893. ; At one time real mode only data.  Header stuff now used during image load.
  894.  
  895. psp_segment:
  896.     .dw    0
  897.  
  898. loadname_nul:                ; offset of NUL so it can become '$'
  899.     .dw    0
  900. loadname:                ; name of program file to load, if it
  901.     .db    81 .dup 0        ; gets really long ok to overwrite next
  902.  
  903. exe_header:                ; loaded from front of loadfile
  904. exe_magic:
  905.     .dw    0
  906. exe_bytes_last_page:
  907.     .dw    0
  908. exe_sectors:
  909.     .dw    0
  910. exe_header_length = . - exe_header
  911.  
  912. coff_offset:
  913.     .dd    0            ; from start of file
  914.  
  915. coff_header:                ; loaded from after stub
  916.     .db    20 .dup 0
  917. aout_header:
  918.     .db    28 .dup 0
  919. text_section:
  920.     .db    40 .dup 0
  921. data_section:
  922.     .db    40 .dup 0
  923. bss_section:
  924.     .db    40 .dup 0
  925. coff_header_length = . - coff_header
  926.  
  927. old_strategy:
  928.     .db    0
  929. old_umb:
  930.     .db    0
  931.  
  932. dos_major:
  933.     .db    0
  934.  
  935.     .align    16            ; Align ourselves to a paragraph
  936. end_of_memory:                ; resize is done early so must keep all
  937.  
  938. ;-----------------------------------------------------------------------------
  939. ;  structure definitions
  940. ;
  941.  
  942. coff_magic    = 0            ; from coff header
  943.  
  944. aout_entry    = 16            ; from aout header
  945.  
  946. s_paddr        = 8            ; from section headers
  947. s_vaddr        = 12
  948. s_size        = 16
  949. s_scnptr    = 20
  950.  
  951.  
  952.