home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Science / Science.zip / imdisp79.zip / SWAP.ASM < prev    next >
Assembly Source File  |  1992-12-02  |  94KB  |  2,056 lines

  1. page 60, 132
  2.  
  3. ;   SWAP.ASM        Version 3.01    January 4, 1991
  4. ;   SWAP.ASM        Version 3.00    October 4, 1990
  5. ;
  6. ;   Contains code and data needed to swap most of the current program out
  7. ;   to extended memory, expanded memory, or disk; execute another program;
  8. ;   and re-load the original program back into memory.
  9. ;
  10. ;   Copyright (C) 1990 by Marty Del Vecchio
  11. ;   Released to the public domain for free use by all
  12. ;   Product is supplied as is and author disclaims all warranties,
  13. ;   explicit or implied, about the functionality of the source code
  14. ;   or object modules supplied, and shall not be held responsible
  15. ;   for any damages caused by use of product.
  16. ;
  17. ;   Code to parse default FCB's written and generously donated
  18. ;   by David E. Jenkins (jenkins@wang.com or dave.jenkins@office.wang.com).
  19. ;
  20. ;   Code to correctly handle disk-full errors written by Archie Warnock
  21. ;   (warnock@stars.gsfc.nasa.gov)
  22. ;
  23. ;   Contributions not solicited.  Just appreciate the fact that somebody
  24. ;   took the time to write and comment this code.  If you have any
  25. ;   questions, please contact me at:
  26. ;
  27. ;   Marty Del Vecchio                   Channel 1 BBS
  28. ;   99 Marlboro Road                    Boston, MA
  29. ;   Southborough, MA  01772             (617) 354-8873
  30. ;   (508) 485-9718
  31. ;
  32. ;   internet:  marty@bsn.mceo.dg.com
  33. ;
  34. ;   For information about the contents of this file, see the accompanying
  35. ;   file SWAP.DOC.
  36. ;
  37.  
  38.  
  39. ; 'DOSSEG' gives us support for Microsoft C and Turbo C segment naming
  40. ; and ordering schemes
  41. DOSSEG
  42.  
  43. ; Figure out which memory model we're assembling for.  Specified on
  44. ;  MASM command line with /D followed by either _Small, _Compact, _Medium,
  45. ;  or _Large.  If none specified, _Small is assumed.
  46.  
  47. ; Once the model is defined, MASM provides two definitions, @codesize
  48. ;  and @datasize, to determine the size of code and data pointers.  If
  49. ;  @codesize is 0 (Small and Compact), there is one code segment, and
  50. ;  code addresses are 16 bits (offset only).  If @codesize is 1 (Medium
  51. ;  and Large), there are multiple code segments, and code addresses are
  52. ;  32 bits (segment and offset).  Similarly, @datasize of 0 means one
  53. ;  data segment (16-bit pointers), and @datasize of 1 means multiple
  54. ;  data segments (32-bit pointers).
  55.  
  56. IFDEF _large
  57.    .MODEL Large, C
  58.    IF1
  59.       %out Assembling for C, Large memory model
  60.    ENDIF
  61. ELSE
  62.    IFDEF _compact
  63.       .MODEL Compact, C
  64.       IF1
  65.          %out Assembling for C, Compact memory model
  66.       ENDIF
  67.    ELSE
  68.       IFDEF _medium
  69.          .MODEL Medium, C
  70.          IF1
  71.             %out Assembling for C, Medium memory model
  72.          ENDIF
  73.       ELSE
  74.          .MODEL Small, C
  75.          IF1
  76.             %out Assembling for C, Small memory model
  77.          ENDIF
  78.       ENDIF
  79.    ENDIF
  80. ENDIF
  81.  
  82.  
  83. ifdef _286
  84. .286
  85. endif
  86.  
  87. ; Report whether multiple DOS memory blocks will be swapped
  88. IF1
  89.    IFDEF NOFRAG
  90.       %out Multiple DOS memory blocks will NOT be swapped
  91.    ELSE
  92.       %out Multiple DOS memory blocks will be swapped
  93.    ENDIF
  94. ENDIF
  95.  
  96. ; Figure out which save method we are using--EMS, XMS, disk, or a
  97. ;  combination.
  98.  
  99. ; Specified on MASM command line with /D followed by either "xms", "ems",
  100. ;  "disk", or "all".  For example, to create a swap() that will try using
  101. ;  XMS and EMS, you would use "masm swap.asm /Dems /Dxms".
  102.  
  103. ; If none specified, it will use all.  To change the order in which swap()
  104. ;  attempts to save the program to different places, see the function
  105. ;  save_program below.
  106.  
  107. ; First, see if they want all of them...
  108. IFDEF all
  109.    USE_DISK  EQU 1
  110.    USE_XMS   EQU 1
  111.    USE_EMS   EQU 1
  112. ELSE
  113.    ; /Dall not specified--try each individually...
  114.    IFDEF disk
  115.       USE_DISK  EQU 1
  116.    ENDIF
  117.  
  118.    IFDEF xms
  119.       USE_XMS   EQU 1
  120.     ENDIF
  121.  
  122.    IFDEF ems
  123.       USE_EMS   EQU 1
  124.    ENDIF
  125.  
  126. ENDIF
  127.  
  128. ; Now see if they declared anything--if not, it will use them all
  129. IFNDEF USE_DISK
  130.    IFNDEF USE_EMS
  131.       IFNDEF USE_XMS
  132.          USE_DISK  EQU 1
  133.          USE_XMS   EQU 1
  134.          USE_EMS   EQU 1
  135.       ENDIF
  136.    ENDIF
  137. ENDIF
  138.  
  139. ; Constant definitions for easier reading
  140. STDERR          equ     2           ; Standard DOS file handle for error output
  141. GET_VECTOR      equ     35h         ; DOS function to get interrupt vector
  142. EMM_INT         equ     67h         ; EMS interrupt vector
  143. EMM_NAME_LEN    equ     8           ; Length of EMS device driver name
  144. MAX_DOS_CMD     equ     127         ; Maximum DOS command-line length
  145.  
  146. ; If we will swap out all DOS memory blocks a program owns, we need a
  147. ;   place to store information about them
  148. MAX_EXTRA       equ     16          ; Maximum number of extra DOS allocation
  149.                                     ; blocks to swap
  150.  
  151. dos_block       struc               ; Structure for extra DOS memory blocks
  152. block_seg       dw      0           ; User's segment address of block
  153. block_size      dw      0           ; Size in paragraphs of block
  154. dos_block       ends
  155.  
  156.  
  157. bptr            equ     byte ptr    ; Means we're loading/storing 8 bits
  158. wptr            equ     word ptr    ; Means we're loading/storing 16 bits
  159. dptr            equ     dword ptr   ; Means we're loading/storing 32 bits
  160.  
  161.  
  162. ; All code and data must be in the code segment, which is the first segment
  163. ;  in all Turbo C, Turbo C++, and Microsoft C memory models.
  164.  
  165. ; If we are in the Medium or Large models, there are multiple code segments.
  166. ;  If this is the case, our default code segment name will be "SWAP_TEXT".
  167. ;  This is acceptable in most cases, except when using the Turbo C integrated
  168. ;  development environment.  See SWAP.DOC for details.
  169.  
  170. ; If you are using Turbo C's Integrated Development Environment, the line
  171. ;  right after "IF @codesize" MUST say ".CODE  _TEXT"!!!!!!!!!!!!!!!!!!!!
  172. IF @codesize
  173. ;.CODE   _TEXT
  174. .CODE   SWAP_TEXT
  175. ELSE
  176. .CODE
  177. ENDIF
  178.  
  179. ; *****************************************************************************
  180. ; Our resident data declarations--this data will be needed after the swap
  181. ;  has occurred, and thus must be above the resident line
  182. ; *****************************************************************************
  183.  
  184. ; *****************************************************************************
  185. ; First, all variables that will be used by all versions assembled from
  186. ; this source file, regardless of what save options are selected
  187. ; *****************************************************************************
  188. ret_code    dw      0           ; Return code (to C caller) of the swap routine
  189.                                 ;   0 = success
  190.                                 ;   1 = unable to shrink DOS memory allocation
  191.                                 ;   2 = unable to save program to EMS
  192.                                 ;   3 = unable to execute requested program
  193.                                 ; These values must be the same as those listed
  194.                                 ;  in SWAP.H!!!!!!!!!
  195.  
  196. ; *****************************************************************************
  197. ; Variables that deal with DOS' memory allocation blocks
  198. old_size    dw      0           ; The old size (in paragraphs) of this program
  199. new_size    dw      0           ; The new "resident" size, doesn't include
  200.                                 ;  code/data swapped
  201. prog_size   dw      0           ; Size in paragraphs of saved part of program
  202.                                 ; block (old_size - new_size)
  203. total_paras dw      0           ; Size (in paragraphs) of all blocks combined
  204. my_psp      dw      0           ; This program's Program Segment Prefix (PSP)
  205. mcb_psp     dw      0           ; The PSP address in this program's memory block
  206. start_seg   dw      0           ; Segment address of released memory
  207.  
  208. ; If we are swapping all DOS memory blocks a program owns, we store
  209. ;  them in this array of structures
  210. IFNDEF NOFRAG
  211. extra_count dw      0           ; # of extra blocks to save (not including
  212.                                 ;  program block)
  213. dos_blocks  dos_block MAX_EXTRA dup (<>)    ; Array for extra blocks
  214. ENDIF
  215. ; *****************************************************************************
  216.  
  217. ; *****************************************************************************
  218. ; Variable used during the save/restore process
  219. handle      dw      0           ; EMS/XMS/disk file handle
  220. ; *****************************************************************************
  221.  
  222. ; *****************************************************************************
  223. ; A temporary stack in our code segment, and associated variables
  224. old_sp      dw      0               ; Place to save this program's stack
  225. old_ss      dw      0               ;  information while executing new program
  226.  
  227. ; XMS driver needs a large stack (at least 256 bytes free when called)
  228. IFDEF USE_XMS
  229. new_stack   db      320 dup ('?')   ; Temporary stack we can address after swap
  230. ELSE
  231. new_stack   db      128 dup ('?')   ; Temporary stack we can address after swap
  232. ENDIF
  233. new_sp      label   word            ; Point SP to "top" of stack
  234. ; *****************************************************************************
  235.  
  236. ; *****************************************************************************
  237. ; Variables that deal with the execution of the new program
  238. prog_name   db      128 dup (0)     ; Storage for name of program to execute
  239. cmd_pad     db      0               ; Maintain word-alignment for variables
  240. cmd_len     db      0               ; Storage for length of command line
  241.                                     ;  parameters
  242. cmd_line    db      128 dup (0)     ; Storage for command line parameters
  243.  
  244. param_blk   label   byte            ; Program Parameter Block--pass to DOS on
  245.                                     ;  exec call
  246. env_seg     dw      0               ; Environment segment address, 0 means a
  247.                                     ;  COPY of ours
  248. cmd_ofs     dw      offset @code:cmd_len    ; Offset address of command line
  249. cmd_seg     dw      seg cmd_line    ; Segment address of command line
  250. fcb_5C_ofs  dw      offset fcb5C    ; Far pointers to default FCB's.  Some
  251. fcb_5C_seg  dw      seg fcb5C       ;  programs (such as DOS' CHKDSK.COM)
  252. fcb_6C_ofs  dw      offset fcb6C    ;  depend on these being parsed from
  253. fcb_6C_seg  dw      seg fcb6C       ;  the command line before the EXEC call
  254. ; *****************************************************************************
  255.  
  256. ; *****************************************************************************
  257. ; Variables needed to parse the command line into the default FCB's
  258. c_l_length  dw      0               ; Command line length
  259. si_5C       dw      0               ; Save area for pointer to cmd line arg 1
  260. si_6C       dw      0               ; Save area for pointer to cmd line arg 2
  261.  
  262. ; Default FCB to be passed to PSP offset 5C (hex)
  263. fcb5C       label   byte
  264. fcb5C_drive db      0               ; drive
  265. fcb5C_fname db      8 dup (?)       ; file name
  266. fcb5C_ext   db      3 dup (?)       ; extension
  267. fcb5C_pad   db      4 dup (?)       ; unused
  268.  
  269. ; Default FCB to be passed to PSP offset 6C (hex)
  270. fcb6C       label   byte
  271. fcb6C_drive db      0               ; drive
  272. fcb6C_fname db      8 dup (?)       ; file name
  273. fcb6C_ext   db      3 dup (?)       ; extension
  274. fcb6C_pad   db      4 dup (?)       ; unused
  275. ; *****************************************************************************
  276.  
  277. exec_ret    db      0               ; Return code from executed program
  278. exec_pad    db      0               ; Maintain word-alignment for variables
  279. restore_proc dw     0               ; Address of appropriate restore routine
  280.  
  281. ; *****************************************************************************
  282. ; Message to display to screen when we can't reload program
  283. abort_msg   db      0dh, 0ah, 'SWAP: Unable to reload program.', 0dh, 0ah
  284. abort_len   dw      $ - offset @code:abort_msg
  285. ; *****************************************************************************
  286.  
  287. ; *****************************************************************************
  288. ; Next, the variables needed only for certain versions of the routine,
  289. ;  depending on which save/restore options are chosen
  290. ; *****************************************************************************
  291.  
  292. ; *****************************************************************************
  293. ; Variables needed only when swapping to XMS
  294. IFDEF USE_XMS
  295. XMS_proc    dd      0               ; Address of XMS entry point
  296.  
  297. XMS_struc       label   byte        ; Structure needed to move memory with XMS
  298. XMS_size        dd      0           ; # of bytes to move (must be even)
  299. XMS_from        dw      0           ; Handle of source, 0=conventional memory
  300. XMS_from_addr   dd      0           ; Address of source memory
  301. XMS_to          dw      0           ; Handle of destination, 0=conventional
  302.                                     ;  memory
  303. XMS_to_addr     dd      0           ; Address of destination memory
  304. ENDIF
  305. ; *****************************************************************************
  306.  
  307. ; *****************************************************************************
  308. ; Variables needed only when swapping to EMS
  309. IFDEF USE_EMS
  310. pages_used  db      0           ; # of pages of EMS used
  311. emm_name    db      'EMMXXXX0'  ; Name of EMS device driver
  312.  
  313. EMS_struc   label   byte        ; Structure needed to move memory with EMS 4.0+
  314. EMS_size    dd      0           ; # of bytes to move
  315. EMS_from    db      0           ; Type of source memory (0 = conventional,
  316.                                 ;  1 = expanded)
  317. EMS_from_h  dw      0           ; Source memory handle (0 = conventional)
  318. EMS_from_o  dw      0           ; Offset of source memory (expanded = 0-16K,
  319.                                 ;  conventional = 0-64K)
  320. EMS_from_s  dw      0           ; Segment/page of source (expanded = logical
  321.                                 ;  page, conventional = segment)
  322. EMS_to      db      0           ; Type of desination memory (0 = conventional,
  323.                                 ;  1 = expanded)
  324. EMS_to_h    dw      0           ; Destination memory handle (0 = conventional)
  325. EMS_to_o    dw      0           ; Offset of destination memory (expanded =
  326.                                 ;  0-16K, conventional = 0-64K)
  327. EMS_to_s    dw      0           ; Segment/page of destination (expanded =
  328.                                 ;  logical page, conventional = segment)
  329.  
  330. ems_offset  dd      0           ; Destination pointer--absolute byte offset
  331.                                 ;  into handle
  332. ENDIF
  333. ; *****************************************************************************
  334.  
  335. ; *****************************************************************************
  336. ; Variables needed only when swapping to disk
  337. IFDEF USE_DISK
  338. fname       db      80 dup (0)  ; Name of the file data is saved to/read from
  339. paras_left  dw      0           ; temporary counter
  340. ENDIF
  341. ; *****************************************************************************
  342.  
  343.  
  344.  
  345. ; *****************************************************************************
  346. ; Version-dependent code--only assemble the routine to restore the program
  347. ; from each media (XMS, EMS, disk) if it was specified on the command line
  348. ; *****************************************************************************
  349.  
  350.  
  351. ; *****************************************************************************
  352. ; restore_xms   Attempts to restore program from XMS extended memory
  353. ;
  354. ; Entry:        DS points to our variables
  355. ;               Program was saved to XMS extended memory (block referred to by
  356. ;               handle)
  357. ;
  358. ; Return:       Carry set on error, carry clear on success
  359. ; *****************************************************************************
  360. IFDEF USE_XMS
  361. restore_xms     proc    near
  362.                 push    es
  363.  
  364.                 assume  ds:@code                    ; Tell MASM that DS points
  365.                                                     ;  to our variables
  366.  
  367. ; First, attempt to restore the portion of the program block that was saved
  368. xms_prog_rest:  mov     ax, wptr start_seg          ; Released segment address
  369.                 mov     es, ax
  370.                 mov     ax, wptr prog_size          ; Size (in paragraphs)
  371.  
  372.                 xor     bx, bx
  373.                 mov     wptr XMS_from_addr, bx      ; Initialize XMS source
  374.                 mov     wptr XMS_from_addr + 2, bx  ; address (offset into
  375.                                                     ;  extended memory block)
  376.  
  377.                 call    rest_xms_seg                ; Attempt to restore it
  378.  
  379. IFNDEF NOFRAG
  380.                 jc      xms_dealloc                 ; Carry set = error, exit
  381.  
  382. ; Next, restore the extra DOS segments
  383. xms_extra_rest: mov     cx, wptr extra_count    ; Number of extra blocks to save
  384.                 jcxz    xms_dealloc             ; If CX = 0, we exit routine
  385.  
  386.                 mov     di, offset dos_blocks   ; DI -> array of segment/size
  387.                                                 ;  pairs
  388.  
  389. xms_extra_rest_loop:
  390.                 mov     ax, wptr [di].block_seg
  391.                 mov     es, ax                  ; ES = segment to restore
  392.                 mov     ax, wptr [di].block_size; AX = size in paragraphs
  393.                 push    cx
  394.                 push    di
  395.                 call    rest_xms_seg            ; Attempt to restore this block
  396.                 pop     di
  397.                 pop     cx
  398.                 jc      xms_dealloc             ; Carry flag set == error, exit
  399.                 add     di, size dos_block
  400.                 loop    xms_extra_rest_loop     ; Keep going through all blocks
  401.  
  402. ENDIF
  403.  
  404. xms_dealloc:    rcl     bl, 1                   ; Save carry flag in low bit of
  405.                                                 ;  bl
  406.  
  407.                 mov     dx, wptr handle         ; First, free XMS handle
  408.                 mov     ah, 0Ah
  409.                 push    bx
  410.                 call    dptr XMS_proc
  411.                 pop     bx
  412.  
  413.                 rcr     bl, 1                   ; Restore carry flag from bl
  414.                                                 ;  low bit
  415.  
  416. restore_xms_ret:pop     es
  417.                 ret
  418. restore_xms     endp
  419.  
  420.  
  421. ; *****************************************************************************
  422. ; rest_xms_seg  Attempts to restore a chunk of RAM from XMS memory
  423. ;
  424. ; Entry:        ES points to the segment to restore
  425. ;               AX contains its length (in paragraphs)
  426. ;               handle holds the XMS handle to read from
  427. ;               XMS_from_addr contains offset into extended memory for read
  428. ;
  429. ; Return:       Carry set on error, carry clear on success
  430. ;               Updates XMS_from_addr for next read
  431. ; *****************************************************************************
  432. rest_xms_seg    proc    near
  433.                 push    ds
  434.                 push    es
  435.  
  436. ; Call the XMS copy memory function to do this; fill in request block
  437. xms_read_size:  mov     bx, 10h                     ; AX = # of paragraphs,
  438.                                                     ;  convert to bytes
  439.                 mul     bx                          ; DX:AX = AX * 10h, # of
  440.                                                     ;  bytes to read
  441.                 mov     wptr XMS_size, ax           ; Store # of bytes to read
  442.                 mov     wptr XMS_size + 2, dx
  443.  
  444. xms_read_from:  mov     ax, wptr handle             ; Source XMS handle
  445.                 mov     wptr XMS_from, ax           ; XMS_from_addr already
  446.                                                     ;  filled in
  447.  
  448. xms_read_to:    xor     bx, bx
  449.                 mov     wptr XMS_to, bx             ; Read into conventional
  450.                                                     ;  memory
  451.                 mov     wptr XMS_to_addr, bx        ; Offset of dest address
  452.                 mov     ax, es                      ; Segment of destination
  453.                                                     ;  address
  454.                 mov     wptr XMS_to_addr + 2, ax
  455.  
  456. do_xms_read:    mov     si, offset @code:XMS_struc  ; DS:SI -> XMS structure
  457.                 mov     ah, 0Bh
  458.                 call    dptr XMS_proc               ; Do the move
  459.                 cmp     ax, 1
  460.                 jnz     rest_xms_seg_er
  461.  
  462. rest_xms_seg_ok:mov     ax, wptr XMS_size           ; Retrieve length
  463.                 mov     dx, wptr XMS_size + 2       ;  (32 bits)
  464.                 add     wptr XMS_from_addr, ax      ; Add two 32-bit values
  465.                 adc     wptr XMS_from_addr + 2, dx  ; Update XMS read pointer
  466.                 clc                                 ; Signal success
  467.                 jmp     short rest_xms_seg_ret
  468.  
  469. rest_xms_seg_er:stc
  470.  
  471. rest_xms_seg_ret:
  472.                 pop     es
  473.                 pop     ds
  474.                 ret
  475. rest_xms_seg    endp
  476.  
  477. ENDIF
  478. ; *****************************************************************************
  479.  
  480.  
  481. ; *****************************************************************************
  482. ; restore_ems   Attempts to restore program from EMS expanded memory
  483. ;
  484. ; Entry:        DS points to our variables
  485. ;               Program was saved to EMS expanded memory (block referred to by
  486. ;               handle)
  487. ;
  488. ; Return:       Carry set on error, carry clear on success
  489. ; *****************************************************************************
  490. IFDEF USE_EMS
  491. restore_ems     proc    near
  492.                 push    es
  493.  
  494.                 assume  ds:@code                    ; Tell MASM that DS points
  495.                                                     ;  to our variables
  496.  
  497. ; First, attempt to restore the portion of the program block that was saved
  498. ems_prog_rest:  mov     ax, wptr start_seg          ; Released segment address
  499.                 mov     es, ax
  500.                 mov     ax, wptr prog_size          ; Size (in paragraphs)
  501.  
  502.                 xor     bx, bx
  503.                 mov     wptr ems_offset, bx         ; Maintain absolute by
  504.                 mov     wptr ems_offset + 2, bx     ;  offset pointer relative
  505.                                                     ;  to handle
  506.  
  507.                 call    rest_ems_seg                ; Attempt to restore it
  508.  
  509. IFNDEF NOFRAG
  510.                 jc      ems_dealloc                 ; Carry set = error, exit
  511.  
  512. ; Next, restore the extra DOS segments
  513. ems_extra_rest: mov     cx, wptr extra_count    ; Number of extra blocks to save
  514.                 jcxz    ems_dealloc             ; If CX = 0, we exit routine
  515.  
  516.                 mov     di, offset dos_blocks   ; DI -> array of segment/size
  517.                                                 ;  pairs
  518.  
  519. ems_extra_rest_loop:
  520.                 mov     ax, wptr [di].block_seg
  521.                 mov     es, ax                  ; ES = segment to restore
  522.                 mov     ax, wptr [di].block_size; AX = size in paragraphs
  523.                 push    cx
  524.                 push    di
  525.                 call    rest_ems_seg            ; Attempt to restore this block
  526.                 pop     di
  527.                 pop     cx
  528.                 jc      ems_dealloc             ; Carry flag set == error, exit
  529.                 add     di, size dos_block
  530.                 loop    ems_extra_rest_loop     ; Keep going through all blocks
  531.  
  532. ENDIF
  533.  
  534. ems_dealloc:    rcl     bl, 1                   ; Save carry flag in low bit of
  535.                                                 ;  bl
  536.  
  537.                 mov     ah, 45h                 ; Deallocate EMS memory
  538.                 mov     dx, wptr handle         ; Specify which handle
  539.                 push    bx
  540.                 int     67h
  541.                 pop     bx
  542.  
  543.                 rcr     bl, 1                   ; Restore carry flag from bl
  544.                                                 ;  low bit
  545.  
  546. restore_ems_ret:pop     es
  547.                 ret
  548. restore_ems     endp
  549.  
  550. ; *****************************************************************************
  551. ; rest_ems_seg  Attempts to restore a chunk of RAM from EMS memory
  552. ;
  553. ; Entry:        ES points to the segment to restore
  554. ;               AX contains its length (in paragraphs)
  555. ;               handle holds the EMS handle to write to
  556. ;               ems_offset holds the 32-bit absolute offset in expanded
  557. ;                memory to read this block from
  558. ;
  559. ; Return:       Carry set on error, carry clear on success
  560. ;               Updates ems_offset with proper offset for next read
  561. ; *****************************************************************************
  562. rest_ems_seg    proc    near
  563.                 push    ds
  564.                 push    es
  565.  
  566.                 assume  ds:@code                ; Tell MASM DS points to our
  567.                                                 ;  variables
  568.  
  569. ; Call the EMS copy memory function to do this; fill in the EMS request block
  570. ems_read_size:  mov     bx, 10h                     ; AX = # of paragraphs
  571.                 mul     bx                          ; DX:AX = AX * 10h, convert
  572.                                                     ;  paragraphs to bytes
  573.                 mov     wptr EMS_size, ax           ; Store # of bytes to write
  574.                 mov     wptr EMS_size + 2, dx
  575.  
  576. ems_read_to:    xor     bx, bx
  577.                 mov     bptr EMS_to, bl             ; Copying to conventional
  578.                                                     ;  memory (0)
  579.                 mov     wptr EMS_to_h, bx           ; Destination handle is 0
  580.                                                     ;  (conventional memory)
  581.                 mov     wptr EMS_to_o, bx           ; Destination offset is 0
  582.                 mov     ax, es                      ; Segment of destination
  583.                                                     ;  address is ES
  584.                 mov     wptr EMS_to_s, ax
  585.  
  586. ems_read_from:  mov     bptr EMS_from, 1            ; Copying to expanded memory
  587.                 mov     ax, wptr handle
  588.                 mov     wptr EMS_from_h, ax         ; Specify EMS handle
  589.  
  590.                 ; 32-bit absolute offset for copy is in ems_offset
  591.                 ;  convert to EMS page:offset (16K pages) values
  592.                 mov     ax, wptr ems_offset         ; Load 32-byte offset
  593.                 mov     dx, wptr ems_offset + 2
  594.                 mov     bx, ax                      ; Save a copy of ax (low 16
  595.                                                     ;  bits)
  596.                 and     ax, 0011111111111111b       ; Get (ax & (16K - 1)),
  597.                                                     ;  this is the offset (14
  598.                                                     ;  bits)
  599.                 mov     wptr EMS_from_o, ax         ; Save page offset
  600.                 mov     cl, 14
  601.                 shr     bx, cl                      ; Move low 2 bits of page
  602.                                                     ;  into low 2 bits of bx
  603.                 mov     cl, 2
  604.                 shl     dx, cl                      ; Move hi ? bits of page
  605.                                                     ;  into dx shl 2
  606.                 or      dx, bx                      ; DX = page number (combine
  607.                                                     ;  two values)
  608.                 mov     wptr EMS_from_s, dx         ; Save
  609.  
  610.                 mov     ax, wptr EMS_size           ; Retrieve size of copy
  611.                 mov     dx, wptr EMS_size + 2
  612.                 add     wptr ems_offset, ax         ; Update EMS copy pointer
  613.                 adc     wptr ems_offset + 2, dx     ;  for next EMS write
  614.  
  615. do_ems_read:    mov     si, offset @code:EMS_struc  ; DS:SI -> EMS request
  616.                                                     ;  structure
  617.                 mov     ax, 5700h                   ; Function 57 (copy/exchange
  618.                                                     ;  memory), sub 0, copy
  619.                                                     ;  memory
  620.                 int     67h                         ; Call EMS manager
  621.                 or      ah, ah                      ; AH = 0 means success
  622.                 jnz     rest_ems_seg_er             ; Not 0 means error
  623.  
  624. rest_ems_seg_ok:clc                                 ; Signal success
  625.                 jmp     short rest_ems_seg_ret
  626.  
  627. rest_ems_seg_er:stc
  628.  
  629. rest_ems_seg_ret:
  630.                 pop     es
  631.                 pop     ds
  632.                 ret
  633. rest_ems_seg    endp
  634.  
  635. ENDIF
  636. ; *****************************************************************************
  637.  
  638.  
  639. ; *****************************************************************************
  640. ; restore_disk  Attempts to restore program from DOS disk file
  641. ;
  642. ; Entry:        DS points to our code segment
  643. ;               Program was saved to DOS disk file (full path stored in fname)
  644. ;
  645. ; Return:       Carry set on error, carry clear on success
  646. ; *****************************************************************************
  647. IFDEF USE_DISK
  648. restore_disk    proc    near
  649.  
  650.                 push    ds
  651.  
  652.                 assume  ds:@code                ; Tell MASM that DS points to
  653.                                                 ;  our variables
  654.  
  655. open_file:      mov     dx, offset @code:fname  ; DS:DX -> file name
  656.                 mov     ax, 3D42h               ; DOS function 3Dh, open file
  657.                                                 ;  al = open for read only,
  658.                                                 ;  deny none
  659.                 int     21h                     ; Call DOS
  660.                 jnc     open_ok                 ; Carry clear = all OK
  661.                 jmp     short restore_disk_ret  ; Carry set, just exit with
  662.                                                 ;  error
  663.  
  664. open_ok:        mov     wptr handle, ax         ; File handle returned from DOS
  665.  
  666. ; First, restore the program block contents saved to disk
  667. disk_prog_rest: mov     ax, wptr start_seg      ; Get segment of program block
  668.                                                 ;  saved
  669.                 mov     es, ax
  670.                 mov     ax, wptr prog_size      ; Get size of program block
  671.                                                 ;  saved
  672.                 call    rest_disk_seg           ; Try to restore it
  673.                 jc      restore_disk_er         ; Carry set == error
  674.  
  675. IFNDEF NOFRAG
  676. ; Next, restore the contents of the extra blocks saved to disk
  677. disk_extra_rest:
  678.                 mov     cx, wptr extra_count    ; Number of extra blocks to
  679.                                                 ;  restore
  680.                 jcxz    close_read              ; IF CX = 0, we're done
  681.                                                 ;  restoring
  682.  
  683.                 mov     di, offset dos_blocks   ; DI -> array of segment/size
  684.                                                 ;  pairs
  685.  
  686. disk_extra_rest_loop:
  687.                 mov     ax, wptr [di].block_seg
  688.                 mov     es, ax                  ; ES = segment to restore to
  689.                 mov     ax, wptr [di].block_size; AX = size in paragraphs
  690.                 push    cx
  691.                 push    di
  692.                 call    rest_disk_seg           ; Attempt to restore this block
  693.                 pop     di
  694.                 pop     cx
  695.                 jc      restore_disk_er         ; Error--exit routine
  696.                 add     di, size dos_block
  697.                 loop    disk_extra_rest_loop    ; Look for next DOS block
  698.  
  699. ENDIF
  700.  
  701. close_read:     mov     ah, 3Eh                 ; Close file
  702.                 int     21h                     ; Call DOS
  703.  
  704. restore_disk_ok:clc                             ; Signal success
  705.                 jmp     short restore_disk_ret  ;  and Exit
  706.  
  707. restore_disk_er:
  708.                 mov     ah, 3Eh                 ; Error, close file first
  709.                 int     21h                     ; Call DOS
  710.                 stc                             ; Signal failure
  711.  
  712. restore_disk_ret:
  713.                 pop     ds                      ; Restore our DS! (error in
  714.                                                 ;  revs 2.11 and before)
  715.  
  716.                 rcl     bl, 1                   ; Save carry flag in low bit of
  717.                                                 ;  bl
  718.  
  719.                 mov     dx, offset @code:fname  ; DS:DX -> file name
  720.                 mov     ah, 41h                 ; DOS function 41h, delete file
  721.                 push    bx
  722.                 int     21h                     ; Call DOS
  723.                 pop     bx
  724.  
  725.                 rcr     bl, 1                   ; Restore carry flag from low
  726.                                                 ;  bit of bl
  727.  
  728.                 ret
  729. restore_disk    endp
  730.  
  731. ; *****************************************************************************
  732. ; rest_disk_seg Attempts to restore a chunk of RAM from the DOS disk file
  733. ;
  734. ; Entry:        ES points to the segment to restore
  735. ;               AX contains its length (in paragraphs)
  736. ;               handle contains the file handle to read from
  737. ;               Program was saved to DOS disk file (fname)
  738. ;
  739. ; Return:       Carry set on error, carry clear on success
  740. ; *****************************************************************************
  741. rest_disk_seg   proc    near
  742.                 push    es
  743.                 push    ds
  744.  
  745.                 mov     bx, es
  746.                 mov     ds, bx                  ; DS -> segment to restore to
  747.  
  748.                 assume  ds:nothing
  749.  
  750.                 mov     wptr cs:paras_left, ax  ; Keep count in this variable
  751.  
  752. disk_read_32k:  cmp     ax, 0800h                   ; Less than 32K left?
  753.                 jb      last_disk_read              ; Yes, do last read
  754.                 sub     wptr cs:paras_left, 0800h   ; 32K left to read
  755.                 mov     ah, 3Fh                 ; DOS function 3Fh, read file
  756.                 mov     bx, wptr cs:handle      ; BX = handle to read from
  757.                 mov     cx, 8000h               ; Read 32K bytes
  758.                 xor     dx, dx                  ; DS:DX -> buffer to read to
  759.                 int     21h                     ; Call DOS
  760.                 jc      rest_disk_seg_er        ; Carry set = error
  761.  
  762. disk_read_ok:   mov     ax, ds                  ; Address next read location
  763.                 add     ax, 0800h               ; It's 800h paragraphs ahead
  764.                 mov     ds, ax                  ; DS -> new restore location
  765.                 mov     ax, wptr cs:paras_left  ; Expecting this above
  766.                 jmp     short disk_read_32k     ; Read next 32K
  767.  
  768. last_disk_read: mov     cx, 4                   ; Convert paragraphs to bytes
  769.                 shl     ax, cl
  770.                 mov     cx, ax                  ; # of bytes left in cx
  771.                 mov     ah, 3Fh                 ; Read last bytes
  772.                 mov     bx, wptr cs:handle      ; BX = handle to read from
  773.                 xor     dx, dx                  ; DS:DX -> buffer to restore to
  774.                 int     21h                     ; Call DOS
  775.                 jc      rest_disk_seg_er        ; Error reading!  Close file
  776.                                                 ;  first
  777.  
  778. rest_disk_seg_ok:
  779.                 clc
  780.                 jmp     short rest_disk_seg_ret
  781.  
  782. rest_disk_seg_er:
  783.                 stc
  784.  
  785. rest_disk_seg_ret:
  786.                 pop     ds
  787.                 pop     es
  788.                 ret
  789. rest_disk_seg   endp
  790.  
  791. ENDIF
  792. ; *****************************************************************************
  793.  
  794.  
  795.                 
  796. ; *****************************************************************************
  797. ; execute_program   Execute the program specified
  798. ;
  799. ; Entry:            param_blk has been initialized
  800. ;                   DS points to our data
  801. ; Return:           puts return code in cs:exec_ret
  802. ; *****************************************************************************
  803. execute_program proc    near                    ; Called only from inside our
  804.                                                 ;  segment
  805.  
  806.                 push    ds                      ; These are destroyed by the
  807.                 push    es                      ;  DOS EXEC call
  808.  
  809.                 assume  ds:@code                ; Tell MASM that DS points to
  810.                                                 ;  our variables
  811.  
  812. exec_program:   mov     ax, ds                  ; Our path name is in CS (point
  813.                                                 ;  DS to our segment)
  814.                 mov     es, ax                  ; Our parameter block is in CS
  815.                                                 ;  (point ES to our segment)
  816.                 mov     ax, 4B00h               ; Load and execute program
  817.                 mov     bx, offset @code:param_blk
  818.                 mov     dx, offset @code:prog_name
  819.                 int     21h                     ; Sets carry flag if error
  820.                                                 ; All registers destroyed
  821.                                                 ;  except CS:IP!
  822.  
  823.                 assume  ds:nothing              ; Tell MASM that DS doesn't
  824.                                                 ;  point to our variables
  825.  
  826.                 mov     bptr cs:exec_ret, al    ; Store EXEC code
  827.                 jc      exec_err                ; Ooops
  828.  
  829. get_return:     mov     ah, 4Dh                 ; DOS function to get ret code
  830.                 int     21h                     ; All registers destroyed
  831.                 mov     bptr cs:exec_ret, al    ; Store EXEC code
  832.                 jmp     short exec_exit
  833.  
  834. exec_err:       mov     wptr cs:ret_code, 3     ; Signal error on executing
  835.  
  836. exec_exit:      pop     es
  837.                 pop     ds
  838.  
  839.                 ret
  840.  
  841. execute_program endp
  842.  
  843.  
  844. ; *****************************************************************************
  845. ; err_exit          Prints error message and terminates program
  846. ;
  847. ; Entry:            Nothing.
  848. ; Returns:          Doesn't return--calls DOS terminate function.
  849. ;                   Naturally, we can't use the C runtime routines,
  850. ;                   since they are swapped out.
  851. ; *****************************************************************************
  852. err_exit        proc    near                    ; Called only from inside our
  853.                                                 ;  segment
  854.  
  855.                 mov     ax, cs
  856.                 mov     ds, ax                  ; Point DS to our data
  857.  
  858.                 assume  ds:@code                ; Tell MASM that DS points to
  859.                                                 ;  our data
  860.  
  861.                 mov     ah, 40h                 ; DOS function to write to file
  862.                 mov     bx, STDERR              ; Write to standard error handle
  863.                 mov     cx, wptr abort_len      ; CX = length of message
  864.                 mov     dx, offset @code:abort_msg  ; DS:DX = message
  865.                 int     21h
  866.  
  867.                 mov     ax, 4CFFh           ; Exit, return code 255 decimal
  868.                                             ;  (FF hex)
  869.                 int     21h                 ; Exit to DOS, no return
  870.  
  871. err_exit        endp
  872.  
  873.  
  874. ; *****************************************************************************
  875. ; do_exec           Calls the execute routine, then restores program
  876. ;
  877. ; Entry:            Nothing
  878. ; Returns:          Since it is called from the non-resident area, it
  879. ;                   can only return if the program is restored completely.
  880. ; *****************************************************************************
  881. do_exec         proc
  882.                 call    near ptr execute_program    ; Execute the specified
  883.                                                     ;  program
  884.                 jnc     re_size                     ; No carry, OK
  885.  
  886. exec_er:        mov     wptr ret_code, 3        ; Signal error
  887.  
  888. re_size:        mov     es, wptr my_psp         ; Get our PSP address
  889.                 mov     bx, wptr old_size       ; Increase back to old size
  890.                 mov     ah, 4Ah                 ; DOS function 4Ah = resize
  891.                 int     21h
  892.                 jc      resize_err              ; Carry clear = all OK
  893.  
  894. IFNDEF NOFRAG
  895. ; If necessary, allocate all extra DOS memory blocks our program owned
  896.  
  897.                 mov     cx, wptr extra_count    ; CX=number of extra DOS blocks
  898.                 jcxz    restore_prog            ; If zero, don't bother
  899.                 mov     di, offset dos_blocks   ; DI -> array of addresses/sizes
  900.  
  901.                 push    es
  902.  
  903. alloc_extra_loop:
  904.                 mov     bx, wptr [di].block_size; BX = old size
  905.                 mov     ah, 48h                 ; DOS function to allocate
  906.                                                 ;  memory block
  907.                 push    cx
  908.                 push    di
  909.                 int     21h
  910.                 pop     di
  911.                 pop     cx
  912.                 jc      resize_err              ; Unlikely error
  913.  
  914. check_alloc:    cmp     ax, wptr [di].block_seg ; Is it the same as the original
  915.                                                 ;  segment address?
  916.                 jnz     resize_err              ; Nope.  We could do some fancy
  917.                                                 ;  tricks here, but for the most
  918.                                                 ;  part it's not necessary.
  919.  
  920.                 add     di, size dos_block      ; Point to next entry
  921.                 loop    alloc_extra_loop        ; Keep going through extra
  922.                                                 ;  blocks
  923.  
  924.                 pop     es
  925. ENDIF
  926.                 jmp     short restore_prog
  927.  
  928. resize_err:     call    near ptr err_exit       ; Can't return, exit to DOS
  929.  
  930. restore_prog:   call    wptr restore_proc       ; Restore program from disk
  931.                 jc      resize_err              ; Carry set if error
  932.                                                 ; If no error, it returns
  933.                                                 ;  down to restored code
  934.                 ret
  935. do_exec         endp
  936.  
  937. ; *****************************************************************************
  938. ; *****************************************************************************
  939. ALIGN
  940. ;ALIGN 10h       ; Aligns next code item on paragraph boundary
  941.                 ; para_align is a proc instead of just a data
  942.                 ;  item because the ALIGN directive in MASM only
  943.                 ;  applies to code items, not data items!
  944. para_align      proc    near
  945. new_mcb         db      16 dup (0)          ; DOS will put MCB of released
  946.                                             ;  memory here
  947. para_align      endp
  948. ; *****************************************************************************
  949. ; *****************************************************************************
  950.  
  951. ; *****************************************************************************
  952. ; Everything after here is only needed BEFORE we change our allocation size.
  953. ;  Everything below this line will be (temporarily) swapped out of memory,
  954. ;  and thus cannot be used once we shrink our memory allocation.
  955. ; *****************************************************************************
  956.  
  957. ; *****************************************************************************
  958. ;   swap        The routine that does it all
  959. ;
  960. ;   Callable by a C program, takes these parameters (regardless
  961. ;     of which swap options chosen at assembly time, because
  962. ;     C calling conventions let us ignore parameters to the
  963. ;     right if we want to):
  964. ;
  965. ;   swap_both:
  966. ;       prog        Full path name of program to execute
  967. ;       cmdline     Command-line parameters for program to execute
  968. ;       return      Pointer to byte for return code of exec'd program
  969. ;       save_file   Full path name of file in which to save program image (if
  970. ;                   disk is to be used)
  971. ;
  972. ;   Depending on the memory model used, the pointers to the
  973. ;   parameters each occupy 2 bytes or 4 bytes on the stack.
  974. ;   If there is only one data segment (Small and Medium), each
  975. ;   value is a 2-byte near pointer, with DS assumed as the segment
  976. ;   register.  If there are multiple data segments (Compact and
  977. ;   Large), each value is a 4-byte far pointer, with segment and
  978. ;   offset values each pushed on the stack.
  979. ;
  980. ;   The function is declared with 4 parameters, regardless of whether
  981. ;   disk swapping is being included.  This is because the file name
  982. ;   parameter is the last on the parameter list, which C lets us
  983. ;   ignore if we want.
  984. ;
  985. ;   The swap() routine does not check the program name or command
  986. ;   line to verify that a legal command has been requested--that's
  987. ;   the caller's responsibility!
  988. ;
  989. ; *****************************************************************************
  990.  
  991.                 public  swap
  992.  
  993. swap            proc    prog:PTR, cmdline:PTR, return:PTR, save_file:PTR
  994.  
  995.                 push    si                      ; Save registers needed
  996.                 push    di                      ;  by the caller
  997.                 push    es
  998.                 push    ds
  999.  
  1000. point_segs:     mov     ax, cs                  ; Point ES to our segment
  1001.                 mov     es, ax                  ;  for copying of parameters
  1002.  
  1003. ; *****************************************************************************
  1004. get_name:       ; Copy program name to our variable, all versions
  1005.  
  1006. ; If multiple data segments, load DS:SI from stack.  Else, just load SI
  1007. IF @datasize
  1008.                 push    ds                      ; Save segment register
  1009.                 lds     si, dptr prog           ; Load 32-bit far pointer
  1010. ELSE
  1011.                 mov     si, wptr prog           ; Load 16-bit near pointer
  1012. ENDIF                                           ; DS:SI -> program name from
  1013.                                                 ;  caller
  1014.  
  1015.                 mov     di, offset @code:prog_name  ; ES:DI -> our storage area
  1016.  
  1017. name_loop:      lodsb                           ; Fetch next byte
  1018.                 stosb                           ; Save next byte
  1019.                 or      al, al                  ; Was it 0 (end of string)?
  1020.                 jnz     name_loop               ; No, get next one
  1021.  
  1022. IF @datasize
  1023.                 pop     ds                      ; Pop DS if it was pushed above
  1024. ENDIF
  1025. ; *****************************************************************************
  1026.  
  1027. ; *****************************************************************************
  1028. get_cmd:        ; Copy command line to our variable, all versions
  1029.  
  1030. ; If multiple data segments, load DS:SI from stack.  Else, just load SI
  1031. IF @datasize
  1032.                 push    ds                      ; Save segment register
  1033.                 lds     si, dptr cmdline        ; Load 32-bit far pointer
  1034. ELSE
  1035.                 mov     si, wptr cmdline        ; Load 16-bit near pointer
  1036. ENDIF                                           ; DS:SI -> command line from
  1037.                                                 ;  caller
  1038.                 
  1039.                 mov     di, offset @code:cmd_line   ; ES:DI -> our storage area
  1040.                 xor     cl, cl                  ; Keep track of length in cl
  1041.  
  1042. cmd_loop:       lodsb                           ; Fetch next byte from DS:SI
  1043.                 or      al, al                  ; Was it 0 (end of string)?
  1044.                 jz      cmd_end                 ; Yes, we're done
  1045.                 stosb                           ; No, store byte
  1046.                 inc     cl                      ; Increment length
  1047.                 cmp     cl, MAX_DOS_CMD         ; Are we at maximum cmd length?
  1048.                 jnz     cmd_loop                ; Nope, keep going
  1049.  
  1050. cmd_end:        mov     bptr es:[di], 0dh       ; Put CR at end of cmd line
  1051.                 mov     bptr cs:cmd_len, cl     ; Store command-line length
  1052.  
  1053. IF @datasize
  1054.                 pop     ds                      ; Pop DS if it was pushed above
  1055. ENDIF
  1056. ; *****************************************************************************
  1057. ; Set up the default FCBs at 5Ch and 6Ch in the PSP
  1058. ;  Code provided by David E. Jenkins
  1059.                 push    ds                      ; Save caller's DS
  1060.  
  1061.                 mov     ax, cs                  ; Point DS to our
  1062.                 mov     ds, ax                  ;  variables
  1063.  
  1064.                 assume  ds:@code                ; Tell MASM that DS points to
  1065.                                                 ;  our variables
  1066. ;
  1067. ;   Locate the first two command line arguments
  1068. ;
  1069.                 push    ds                      ; Copy ds into es
  1070.                 pop     es                      ;  "   "   "   "
  1071.                 mov     di, offset @code:cmd_line   ; Point to command line in
  1072.                                                     ;  CS
  1073.                 mov     al, bptr cmd_len        ; load the command line length
  1074.                 xor     ah, ah
  1075.                 inc     ax                      ; Include the CR in the length
  1076.                 mov     wptr c_l_length, ax     ; Save the command line length
  1077.                 add     ax, di                  ; Point to end of command line
  1078.                 mov     wptr si_5c, ax          ; default to just after command
  1079.                                                 ;  line
  1080.                 mov     wptr si_6c, ax          ;    "    "   "     "      "
  1081.                 cmp     bptr cmd_len, 0         ; Is there anything to parse?
  1082.                 jz      args_located            ; if not then args have been
  1083.                                                 ;  located
  1084.  
  1085.                 mov     cx, wptr c_l_length     ; Load the command line length
  1086.                 mov     al, ' '                 ; We must find the first non-
  1087.                                                 ;  blank
  1088.                 repe    scasb                   ; Go until we find it or run out
  1089.                 or      cx, cx                  ; Did we run out (CX = 0)?
  1090.                 jz      args_located            ; Yes--then args have been
  1091.                                                 ;  located
  1092.  
  1093.                 dec     di                      ; Move back to the right one
  1094.                 inc     cx                      ;  "    "   "   "    "    "
  1095.                 mov     wptr si_5c, di          ; Save the location of arg 1
  1096.                 repne   scasb                   ; Find the next space (between
  1097.                                                 ;  arg1,2)
  1098.                 or      cx, cx                  ; Did we run out
  1099.                 jz      args_located            ; If so then args have been
  1100.                                                 ;  located
  1101.  
  1102.                 dec     di                      ; Move back to the left one
  1103.                 inc     cx                      ;  "    "   "   "    "   "
  1104.                 repe    scasb                   ; Now find next non-blank
  1105.                                                 ;  (arg 2)
  1106.                 or      cx, cx                  ; Did we run out
  1107.                 jz      args_located            ; If so then args have been
  1108.                                                 ;  located
  1109.  
  1110.                 dec     di                      ; Move back to the right one
  1111.                 inc     cx                      ;  "    "   "   "    "    "
  1112.                 mov     wptr si_6c,di           ; Save location of arg 2
  1113.  
  1114. args_located:
  1115. ; parse the first argument into the first FCB
  1116.  
  1117.                 mov     si, wptr si_5c                  ; Point to the first
  1118.                                                         ;  argument
  1119.                 mov     di, offset @code:fcb5C_drive    ; Point to the unopened
  1120.                                                         ;  FCB
  1121.                 mov     ah, 29h                 ; Parse file name function
  1122.                 mov     al, 00h                 ; Do it like COMMAND.COM does
  1123.                 int     21h                     ; go for it
  1124.  
  1125. ; parse the second argument into the second FCB
  1126.                 mov     si, wptr si_6c          ; Point to the second argument
  1127.                 mov     di, offset @code:fcb6c_drive    ; point to the unopened
  1128.                                                         ;  FCB
  1129.                 mov     ah, 29h                 ; Parse file name function
  1130.                 mov     al, 00h                 ; Do it like COMMAND.COM does
  1131.                 int     21h                     ; go for it
  1132.  
  1133.                 pop     ds                      ; Restore caller's DS
  1134.  
  1135. ; *****************************************************************************
  1136. ; Get the file name from the command line, if this version needs it
  1137. IFDEF USE_DISK
  1138. get_file:
  1139.  
  1140. ; If multiple data segments, load DS:SI, else just load SI
  1141. IF @datasize
  1142.                 push    ds                      ; Save segment register
  1143.                 lds     si, dptr save_file      ; Load 32-bit pointer
  1144. ELSE
  1145.                 mov     si, save_file           ; Load 16-bit pointer
  1146. ENDIF                                           ; DS:SI -> swap file name from
  1147.                                                 ;  caller
  1148.  
  1149.                 mov     di, offset @code:fname  ; ES:DI -> our storage area
  1150.  
  1151. resolve:        mov     ah, 60h                 ; DOS INTERNAL function to
  1152.                                                 ;  resolve file name to full
  1153.                                                 ;  path name
  1154.                 int     21h                     ; Stores complete path at ES:DI
  1155.                                                 ;  we need it after EXEC in case
  1156.                                                 ;  current drive or directory
  1157.                                                 ;  have changed
  1158.                                                 ; Ignore file name error here--
  1159.                                                 ;  it will be caught in
  1160.                                                 ;  save_disk if need be
  1161.  
  1162. IF @datasize
  1163.                 pop     ds                      ; Pop DS if it was pushed above
  1164. ENDIF
  1165.  
  1166. ENDIF           ; IFDEF disk
  1167. ; *****************************************************************************
  1168. ; We have the parameters--let's go
  1169. ; *****************************************************************************
  1170.  
  1171.                 mov     wptr cs:ret_code, 0     ; Initialize swap's return code
  1172.                 mov     cs:exec_ret, 0          ; Initialize exec's return code
  1173.  
  1174. save_stack:     mov     ax, ss
  1175.                 mov     wptr cs:old_ss, ax      ; Save current SS
  1176.                 mov     ax, sp
  1177.                 mov     wptr cs:old_sp, ax      ; Save current SP
  1178.  
  1179. our_stack:      mov     ax, cs                  ; Our stack is in our CS
  1180.                 cli                             ; Disable interrupts
  1181.                 mov     ss, ax
  1182.                 mov     sp, offset @code:new_sp ; Set new stack
  1183.                 sti                             ; Re-enable interrupts
  1184.  
  1185. save_regs:      push    es                      ; Save needed registers
  1186.                 push    ds                      ; This is the caller's DS!
  1187.                 push    bp
  1188.  
  1189.                 mov     ax, cs
  1190.                 mov     ds, ax                  ; Point DS to our data
  1191.  
  1192.                 assume  ds:@code                ; Tell MASM that DS points to
  1193.                                                 ;  our variables
  1194.  
  1195. save_info:      mov     ah, 51h                 ; DOS function 51h, get PSP
  1196.                 int     21h                     ; Call DOS
  1197.                 mov     ax, bx                  ; ax = PSP
  1198.                 mov     wptr my_psp, ax         ; Save in cs: addressable
  1199.                                                 ;  location
  1200.                 dec     ax                      ; PSP-1 = MCB for this mem block
  1201.                 mov     es, ax
  1202.                 mov     ax, es:[0001h]          ; Get PSP address--should be
  1203.                                                 ;  same!
  1204.                 cmp     ax, wptr my_psp         ; All kosher?
  1205.                 jz      psp_ok                  ; Yes
  1206.  
  1207. psp_error:      mov     wptr ret_code, 1        ; No, pass return code
  1208.                 jmp     short exit_swap         ; Exit
  1209.  
  1210. psp_ok:         call    near ptr calc_size      ; Calc size to keep, save
  1211.  
  1212. try_save:       call    near ptr save_program   ; Write program to disk
  1213.                 jnc     shrink_mem              ; Carry flag set on error
  1214.  
  1215. no_save:        mov     wptr ret_code, 2        ; Error--set return code
  1216.                 jmp     short exit_swap         ; Exit routine on error
  1217.  
  1218. shrink_mem:     mov     ah, 4Ah                 ; DOS 4Ah--modify memory
  1219.                                                 ;  allocation
  1220.                 mov     es, wptr my_psp         ; Point to PSP again
  1221.                 mov     bx, wptr new_size       ; new_size was figured in
  1222.                                                 ;  calc_size
  1223.                 int     21h                     ; Call DOS to shrink size
  1224.                 jc      no_shrink               ; Carry set = error
  1225.  
  1226. IFNDEF NOFRAG
  1227. ; If necessary, free all extra DOS memory blocks our program owns
  1228.  
  1229.                 mov     cx, wptr extra_count    ; CX=number of extra DOS blocks
  1230.                 jcxz    exec_prog               ; If zero, don't bother
  1231.                 mov     di, offset dos_blocks   ; DI -> array of addresses/sizes
  1232.  
  1233.                 push    es
  1234.  
  1235. free_extra_loop:
  1236.                 mov     ax, wptr [di].block_seg
  1237.                 mov     es, ax                  ; ES=DOS memory segment to free
  1238.                 mov     ah, 49h                 ; DOS function to free memory
  1239.                                                 ;  block
  1240.                 push    cx
  1241.                 push    di
  1242.                 int     21h
  1243.                 pop     di
  1244.                 pop     cx
  1245.                 jc      no_shrink               ; Unlikely error
  1246.                 add     di, size dos_block      ; Point to next entry
  1247.                 loop    free_extra_loop         ; Keep going through extra
  1248.                                                 ;  blocks
  1249.  
  1250.                 pop     es
  1251. ENDIF
  1252.  
  1253.                 jmp     short exec_prog
  1254.  
  1255. ; *****************************************************************************
  1256. ; Any routine called or data referred to after this point MUST be located
  1257. ;  in this source file BEFORE the variable new_mcb below!
  1258. ; *****************************************************************************
  1259.  
  1260. no_shrink:      mov     wptr ret_code, 1        ; Carry = couldn't shrink block
  1261.                 jmp     short exit_swap         ; Should delete file here!
  1262.  
  1263. exec_prog:      call    do_exec                 ; This code is resident, and
  1264.                                                 ;  can be found above the
  1265.                                                 ;  resident line
  1266.  
  1267. ; do_exec execute the routine AND restores the program!
  1268.  
  1269. exit_swap:      pop     bp                      ; Restore saved registers
  1270.                 pop     ds                      ; This is the caller's DS!
  1271.                 pop     es
  1272.  
  1273.                 assume  ds:nothing              ; Tell MASM DS doesn't point to
  1274.                                                 ;  our variables
  1275.  
  1276. prev_stack:     mov     ax, wptr cs:old_ss      ; Restore original stack
  1277.                 cli
  1278.                 mov     ss, ax
  1279.                 mov     sp, wptr cs:old_sp
  1280.                 sti
  1281.  
  1282. ; Giving user exec's return code.  It could be a 16- or 32-bit pointer
  1283. IF @datasize
  1284.                 push    ds
  1285.                 lds     si, dptr return         ; Load 32-bit pointer
  1286. ELSE
  1287.                 mov     si, wptr return         ; Load 16-bit pointer
  1288. ENDIF                                           ; DS:SI -> return code variable
  1289.                 
  1290.                 mov     al, bptr cs:exec_ret    ; Store exec's return code
  1291.                 mov     bptr [si], al           ;  at address specified by
  1292.                                                 ;  caller
  1293.  
  1294. IF @datasize
  1295.                 pop     ds                      ; Pop DS if pushed above
  1296. ENDIF
  1297.  
  1298.                 pop     ds
  1299.                 pop     es
  1300.                 pop     di
  1301.                 pop     si
  1302.                 mov     ax, wptr cs:ret_code    ; Give return code
  1303.                 ret
  1304. swap            endp
  1305.  
  1306. ; *****************************************************************************
  1307. ; *****************************************************************************
  1308. ; calc_size     Calculates the total size (in paragraphs) of all DOS blocks
  1309. ;               owned by this program plus the amount of the initial program
  1310. ;               allocation block we can swap out.
  1311. ;
  1312. ; Entry:        DS points to our variables
  1313. ;               ES points to DOS Memory Control Block for our program
  1314. ;
  1315. ; Return:       old_size, start_seg, new_size, total_paras, extra_count
  1316. ;               initialized
  1317. ; *****************************************************************************
  1318. calc_size       proc    near                    ; Called only from inside our
  1319.                                                 ;  segment
  1320.  
  1321.                 push    es
  1322.  
  1323.                 assume  ds:@code                ; Tell MASM that DS points to
  1324.                                                 ;  our variables
  1325.  
  1326.                 mov     ax, es:[0003h]          ; Get # paragraphs allocated
  1327.                                                 ;  in this memory block
  1328.                 mov     wptr old_size, ax       ; Save old size of program
  1329.                 mov     bx, cs                  ; BX = segment of our code
  1330.                 mov     ax, offset @code:new_mcb; Last address to keep
  1331.                 mov     cl, 4                   ; new_mcb is para aligned
  1332.                 shr     ax, cl                  ; AX = ofs new_mcb / 16
  1333.                 inc     ax
  1334.                 add     bx, ax
  1335.                 mov     wptr start_seg, bx      ; Segment of released memory
  1336.                 sub     bx, wptr my_psp         ; BX=size to keep in paragraphs
  1337.                 mov     wptr new_size, bx       ; Save new, smaller size
  1338.                 mov     ax, wptr old_size
  1339.                 sub     ax, bx
  1340.                 mov     wptr prog_size, ax      ; ax = size of program block to
  1341.                                                 ;  swap out
  1342.                 mov     wptr total_paras, ax    ; ax = total paragraphs
  1343.  
  1344. IFNDEF NOFRAG
  1345. ; Now loop through all subsequent MCBs looking for blocks that we own (if
  1346. ;  the MCB's "owner" (PSP) matches us (our PSP).  Right now ES points to
  1347. ;  our MCB.  The MCB has three fields of interest:
  1348. ;
  1349. ;   Offset  Size    Description
  1350. ;   -------------------------------------------------------------------------
  1351. ;   0000h   Byte    Chain flag: 'M' (4Dh) if not last, 'Z' (5Ah) if last block
  1352. ;                   in chain
  1353. ;   0001h   Word    PSP segment of owner, 0000h if free memory
  1354. ;   0003h   Word    Size of memory block in paragraphs, NOT including this MCB!
  1355.  
  1356. find_extras:    mov     wptr extra_count, 0     ; Initialize count
  1357.                 mov     bx, wptr my_psp         ; Use bx to hold PSP for easy
  1358.                                                 ;  comparisons
  1359.                 mov     di, offset dos_blocks   ; di = pointer to storage area
  1360.  
  1361. check_next_mcb: cmp     bptr es:[0000h], 'Z'    ; Is this the last block?
  1362.                 jz      calc_size_ret           ; Yup
  1363.  
  1364. next_mcb2:      mov     ax, es                  ; ax = this MCB
  1365.                 mov     cx, wptr es:[0003h]     ; cx = size of this mcb
  1366.                 add     ax, cx
  1367.                 inc     ax                      ; ax = addres of next MCB
  1368.                 mov     es, ax                  ; ES -> next MCB
  1369.  
  1370. my_block:       cmp     wptr es:[0001h], bx     ; Does it match my PSP?
  1371.                 jnz     check_next_mcb          ; Nope, move along
  1372.  
  1373. is_my_block:    inc     wptr extra_count        ; One more extra block
  1374.                 cmp     wptr extra_count, MAX_EXTRA
  1375.                 ja      calc_size_ret           ; Too many blocks--just exit
  1376.  
  1377. is_my_block2:   inc     ax                      ; Was MCB, now is address of
  1378.                                                 ;  segment
  1379.                 mov     wptr [di].block_seg, ax ; Store segment address
  1380.                 mov     cx, wptr es:[0003h]     ; Get size in paragraphs
  1381.                 mov     wptr [di].block_size, cx; Store size
  1382.                 add     wptr total_paras, cx    ; Increment total
  1383.                 add     di, size dos_block      ; Next index (move pointer)
  1384.                 jmp     short check_next_mcb
  1385. ENDIF
  1386.  
  1387. calc_size_ret:  pop     es
  1388.                 ret
  1389.  
  1390. calc_size       endp
  1391. ; *****************************************************************************
  1392.  
  1393. ; *****************************************************************************
  1394. ; xms_installed     Checks to see if XMS driver (himem.sys) is loaded
  1395. ;
  1396. ; Entry:            No assumptions--can be called by user
  1397. ; Return:           1 if XMS driver is load, 0 if not
  1398. ; *****************************************************************************
  1399. IFDEF USE_XMS
  1400.                 public  xms_installed
  1401. xms_installed   proc                            ; Called by user also!
  1402.  
  1403.                 push    ds                  ; Save all "important" registers
  1404.                 push    si
  1405.                 push    es
  1406.                 push    di
  1407.  
  1408.                 mov     ax, 4300h           ; Multiplex code for XMS driver,
  1409.                                             ;  load check function
  1410.                 int     2Fh                 ; Call multiplex interrupt
  1411.                 cmp     al, 80h             ; al=80h means XMS driver IS loaded
  1412.                 jnz     no_xms              ; Nope, not there
  1413.  
  1414. yes_xms:        mov     ax, 4310h               ; Get address of entry point
  1415.                 int     2Fh                     ; Returns address in ES:BX
  1416.                 mov     wptr cs:XMS_proc, bx
  1417.                 mov     wptr cs:XMS_proc + 2, es
  1418.                 mov     ax, 1                   ; Return 1, XMS installed
  1419.                 jmp     short xms_ret
  1420.  
  1421. no_xms:         xor     ax, ax              ; Return 0, XMS not installed
  1422.  
  1423. xms_ret:        pop     di
  1424.                 pop     es
  1425.                 pop     si
  1426.                 pop     ds
  1427.                 ret
  1428.  
  1429. xms_installed   endp
  1430. ENDIF
  1431. ; *****************************************************************************
  1432.  
  1433. ; *****************************************************************************
  1434. ; ems4_installed    Checks to see if EMS 4.0 or above driver is loaded
  1435. ;
  1436. ; Entry:            No assumptions--can be called by user
  1437. ; Return:           1 if EMS 4.0 driver is load, 0 if not
  1438. ; *****************************************************************************
  1439. IFDEF USE_EMS
  1440.                 public  ems4_installed
  1441. ems4_installed  proc                            ; Called by user also!
  1442.  
  1443.                 push    ds                      ; Save "important" registers
  1444.                 push    si
  1445.                 push    es
  1446.                 push    di
  1447.  
  1448.  
  1449. get_emm_vector: mov     ah, GET_VECTOR          ; Get EMM interrupt vector
  1450.                 mov     al, 67h                 ; EMM accessed through Int 67h
  1451.                 int     21h                     ; Call DOS to get vector
  1452.                 mov     di, 0ah                 ; vector + di = name
  1453.                 mov     ax, cs
  1454.                 mov     ds, ax                  ; DS:SI-> EMM device driver name
  1455.                 mov     si, offset @code:emm_name   ; Compare with EMM device
  1456.                                                     ;  name
  1457.                 mov     cx, EMM_NAME_LEN
  1458.                 cld
  1459.                 repe    cmpsb                   ; Compare bytes
  1460.                 jnz     ems_no                  ; Same?  If not, EMS installed
  1461.  
  1462. ems_yes:        mov     ah, 46h                 ; Get EMM version number
  1463.                 int     67h                     ; Returns BCD in al
  1464.                 cmp     al, 40h                 ; Look only at high 4 bits
  1465.                 jb      ems_no                  ; Version not high enough --
  1466.                                                 ;  return 0
  1467.  
  1468. ems4_yes:       mov     ax, 1                   ; EMS installed, return 1
  1469.                 jmp     short ems_ret
  1470.  
  1471. ems_no:         xor     ax, ax                  ; EMS not installed, return 0
  1472.  
  1473. ems_ret:        pop     di
  1474.                 pop     es
  1475.                 pop     si
  1476.                 pop     ds
  1477.                 ret
  1478.  
  1479. ems4_installed  endp
  1480. ENDIF
  1481. ; *****************************************************************************
  1482.  
  1483.  
  1484. ; *****************************************************************************
  1485. ; save_program      Try to save in XMS/EMS/disk.
  1486. ;
  1487. ; Entry:            DS points to our variables
  1488. ;
  1489. ; Returns:          Success:  carry flag clear
  1490. ;                   Failure:  carry flag set
  1491. ; *****************************************************************************
  1492. save_program    proc    near            ; Called only from inside our segment
  1493.  
  1494.                 push    si              ; Save registers
  1495.                 push    di
  1496.                 push    ds
  1497.                 push    es
  1498.  
  1499. ; Now figure out which routines to call, based on command-line definitions
  1500. ; To change the order in which swap() attempts to swap, change the order
  1501. ;  of these three conditional blocks.
  1502. IF1
  1503.    %out swap() will attempt to save the program in the following order:
  1504. ENDIF
  1505.    
  1506.  
  1507. ; *****************************************************************************
  1508. IFDEF USE_XMS
  1509. IF1
  1510.    %out -- XMS extended memory
  1511. ENDIF
  1512.                 call    save_xms        ; Try saving to XMS extended memory
  1513.                 jnc     save_ok         ; Carry clear == success, all done
  1514. ENDIF
  1515. ; *****************************************************************************
  1516.  
  1517.  
  1518. ; *****************************************************************************
  1519. IFDEF USE_EMS
  1520. IF1
  1521.    %out -- EMS expanded memory
  1522. ENDIF
  1523.                 call    save_ems        ; Try saving to EMS expanded memory
  1524.                 jnc     save_ok       ; Carry clear == success, all done
  1525. ENDIF
  1526. ; *****************************************************************************
  1527.  
  1528.  
  1529. ; *****************************************************************************
  1530. IFDEF USE_DISK
  1531. IF1
  1532.    %out -- DOS disk file
  1533. ENDIF
  1534.                 call    save_disk       ; Try saving to DOS disk file
  1535.                 jnc     save_ok         ; Carry clear == success, all done
  1536. ENDIF
  1537. ; *****************************************************************************
  1538.  
  1539. save_er:        stc                     ; Couldn't save anywhere, return error
  1540.                 jmp     short save_ret
  1541.  
  1542. save_ok:        clc                     ; Saved successfully, return OK
  1543.  
  1544. save_ret:       pop     es              ; Restore registers
  1545.                 pop     ds
  1546.                 pop     di
  1547.                 pop     si
  1548.  
  1549.                 ret
  1550. save_program    endp
  1551. ; *****************************************************************************
  1552.  
  1553.  
  1554. ; *****************************************************************************
  1555. ; Version-dependent code--only assemble the routine to save the program
  1556. ; to each place if it was requested on the command line
  1557. ; *****************************************************************************
  1558.  
  1559.  
  1560. ; *****************************************************************************
  1561. ; save_xms      Attempts to save program to XMS extended memory
  1562. ;
  1563. ; Entry:        DS points to our variables
  1564. ;
  1565. ; Return:       Carry set on error, carry clear on success
  1566. ;               If successful, updates restore_proc with the address of
  1567. ;               the XMS restore routine
  1568. ; *****************************************************************************
  1569. IFDEF USE_XMS
  1570. save_xms        proc    near
  1571.  
  1572.                 assume  ds:@code                ; Tell MASM DS points to our
  1573.                                                 ;  variables
  1574.  
  1575.                 call    xms_installed           ; Check if XMS installed
  1576.                 or      ax, ax                  ; Returns 0 if not installed
  1577.                 jnz     xms_inst                ; AX != 0, XMS installed
  1578.                 jmp     short save_xms_er       ; AX == 0, XMS not installed
  1579.  
  1580. xms_inst:       mov     dx, wptr total_paras    ; dx = total # of paragraphs to
  1581.                                                 ;  write
  1582.                 mov     cl, 6                   ; Convert Paragraphs to
  1583.                                                 ;  kilobytes
  1584.                 shr     dx, cl                  ; dx = dx / 64
  1585.                 inc     dx                      ; dx = kilobytes needed (plus 1
  1586.                                                 ;  for safety)
  1587.  
  1588. xms_alloc:      mov     ah, 09h                 ; XMS function 09, allocate
  1589.                                                 ;  extended memory block
  1590.                 call    dptr XMS_proc           ; Call XMS entry point directly
  1591.                 cmp     ax, 1                   ; AX = 1 on success
  1592.                 jnz     save_xms_er             ; Allocation unsuccessful, error
  1593.  
  1594. xms_alloc_ok:   mov     wptr handle, dx         ; Save returned handle in DX
  1595.  
  1596. ; First, attempt to save the portion of the program block
  1597. xms_prog_save:  mov     ax, wptr start_seg      ; Released segment address
  1598.                 mov     es, ax
  1599.                 mov     ax, wptr prog_size      ; Size (in paragraphs) of
  1600.                                                 ;  program block to save
  1601.                 xor     bx, bx
  1602.                 mov     wptr XMS_to_addr, bx    ; Initialize XMS destination
  1603.                 mov     wptr XMS_to_addr+2, bx  ;  address (offset into extended
  1604.                                                 ;  memory block)
  1605.  
  1606.                 call    save_xms_seg            ; Attempt to save the program
  1607.                                                 ;  block
  1608.                 jc      write_error             ; Carry set = failure, return
  1609.  
  1610. IFNDEF NOFRAG
  1611. ; Next, save the extra DOS segments
  1612. xms_extra_save: mov     cx, wptr extra_count    ; Number of extra blocks to save
  1613.                 jcxz    save_xms_ok             ; If CX = 0, we exit routine
  1614.  
  1615.                 mov     di, offset dos_blocks   ; DI -> array of segment/size
  1616.                                                 ;  pairs
  1617.  
  1618. xms_extra_save_loop:
  1619.                 mov     ax, wptr [di].block_seg
  1620.                 mov     es, ax                  ; ES = segment to save
  1621.                 mov     ax, wptr [di].block_size; AX = size in paragraphs
  1622.                 push    cx
  1623.                 push    di
  1624.                 call    save_xms_seg            ; Attempt to save this block
  1625.                 pop     di
  1626.                 pop     cx
  1627.                 jc      write_error             ; Carry flag set == error
  1628.                 add     di, size dos_block
  1629.                 loop    xms_extra_save_loop     ; Keep going through all blocks
  1630.  
  1631. ENDIF
  1632.                 jmp     short save_xms_ok
  1633.  
  1634. write_error:    mov     dx, wptr handle             ; Free allocated handle
  1635.                 mov     ah, 0Ah
  1636.                 call    dptr XMS_proc               ; Falls through to failure
  1637.                                                     ;  code
  1638.  
  1639. save_xms_er:    stc
  1640.                 jmp     short save_xms_ret
  1641.  
  1642.                                                     ; Initialize pointer
  1643.                                                     ;  to restore routine
  1644. save_xms_ok:    mov     wptr restore_proc, offset @code:restore_xms
  1645.                 clc
  1646.  
  1647. save_xms_ret:   ret
  1648. save_xms        endp
  1649.  
  1650.  
  1651. ; *****************************************************************************
  1652. ; save_xms_seg  Attempts to save a chunk of RAM to XMS memory
  1653. ;
  1654. ; Entry:        ES points to the segment to save
  1655. ;               AX contains its length (in paragraphs)
  1656. ;               handle holds the XMS handle to write to
  1657. ;               XMS_to_addr contains offset into extended memory for write
  1658. ;
  1659. ; Return:       Carry set on error, carry clear on success
  1660. ;               Updates XMS_to_addr for next write
  1661. ; *****************************************************************************
  1662. save_xms_seg    proc    near
  1663.                 push    ds
  1664.                 push    es
  1665.  
  1666. ; Call the XMS copy memory function to do this; fill in the XMS request block
  1667. xms_write_size: mov     bx, 10h                     ; AX = # of paragraphs
  1668.                 mul     bx                          ; DX:AX = AX * 10h, convert
  1669.                                                     ;  paragraphs to bytes
  1670.                 mov     wptr XMS_size, ax           ; Store # of bytes to write
  1671.                 mov     wptr XMS_size + 2, dx
  1672.  
  1673. xms_write_from: xor     bx, bx
  1674.                 mov     wptr XMS_from, bx           ; 0 means from conventional
  1675.                                                     ;  memory
  1676.                 mov     wptr XMS_from_addr, bx      ; Offset of source address
  1677.                                                     ;  is 0
  1678.                 mov     ax, es                      ; Segment of source address
  1679.                                                     ;  is ES
  1680.                 mov     wptr XMS_from_addr + 2, ax
  1681.  
  1682. xms_write_to:   mov     ax, wptr handle             ; Destination XMS handle
  1683.                 mov     wptr XMS_to, ax             ; XMS_to_addr already
  1684.                                                     ;  filled in
  1685.  
  1686. do_xms_write:   mov     si, offset @code:XMS_struc  ; DS:SI -> XMS request
  1687.                                                     ;  structure
  1688.                 mov     ah, 0Bh                     ; Function B, copy memory
  1689.                 call    dptr XMS_proc               ; Do the memory copy move
  1690.                 cmp     ax, 1                       ; AX = 1 means success
  1691.                 jnz     save_xms_seg_er             ; Success, all done!
  1692.  
  1693. save_xms_seg_ok:mov     ax, wptr XMS_size           ; Retrieve length
  1694.                 mov     dx, wptr XMS_size + 2       ;  (32 bits)
  1695.                 add     wptr XMS_to_addr, ax        ; Add two 32-bit values
  1696.                 adc     wptr XMS_to_addr + 2, dx    ; Update XMS write pointer
  1697.                 clc                                 ; Signal success
  1698.                 jmp     short save_xms_seg_ret
  1699.  
  1700. save_xms_seg_er:stc
  1701.  
  1702. save_xms_seg_ret:
  1703.                 pop     es
  1704.                 pop     ds
  1705.                 ret
  1706. save_xms_seg    endp
  1707.  
  1708. ENDIF
  1709. ; *****************************************************************************
  1710.  
  1711.  
  1712. ; *****************************************************************************
  1713. ; save_ems      Attempts to save program to EMS 4.0 expanded memory
  1714. ;
  1715. ; Entry:        DS points to our variables
  1716. ;
  1717. ; Return:       Carry set on error, carry clear on success
  1718. ;               If successful, updates restore_proc with the address of
  1719. ;               the EMS restore routine
  1720. ; *****************************************************************************
  1721. IFDEF USE_EMS
  1722. save_ems        proc    near
  1723.  
  1724.                 assume  ds:@code                ; Tell MASM DS points to our
  1725.                                                 ;  variables
  1726.  
  1727.                 call    ems4_installed          ; Check if EMS 4.0 installed
  1728.                 or      ax, ax                  ; AX = 0 if not installed
  1729.                 jnz     ems_inst                ; AX != 0, ems installed
  1730.                 jmp     short save_ems_er       ; AX = 0, no EMS, error!
  1731.  
  1732. ems_inst:       mov     bx, wptr total_paras    ; Total # of paragraphs we need
  1733.                 mov     cl, 10                  ; Convert Paragraphs to 16K
  1734.                                                 ;  pages
  1735.                 shr     bx, cl
  1736.                 inc     bx                      ; BX = pages needed
  1737.                 mov     bptr pages_used, bl     ; Save for later use
  1738.  
  1739.                 mov     ah, 43h                 ; EMM function 43h, allocate
  1740.                 int     67h
  1741.                 or      ah, ah                  ; OK return code?
  1742.                 jz      ems_alloc_ok            ; Yes, skip ahead
  1743.                 jmp     short save_ems_er       ; No, not enough EMS
  1744.  
  1745. ems_alloc_ok:   mov     wptr handle, dx         ; Returned handle in DX
  1746.  
  1747. ; First, attempt to save the portion of the program block
  1748. ems_prog_save:  mov     ax, wptr start_seg      ; Released segment address
  1749.                 mov     es, ax
  1750.                 mov     ax, wptr prog_size      ; Size (in paragraphs) of
  1751.                                                 ;  program block to save
  1752.  
  1753.                 xor     bx, bx
  1754.                 mov     wptr ems_offset, bx     ; Maintain absolute byte offset
  1755.                 mov     wptr ems_offset + 2, bx ;  pointer into handle
  1756.  
  1757.                 call    save_ems_seg            ; Attempt to save the program
  1758.                                                 ;  block
  1759.  
  1760.                 jc      save_ems_fail           ; Carry set = failure, return
  1761.  
  1762. IFNDEF NOFRAG
  1763. ; Next, save the extra DOS segments
  1764. ems_extra_save: mov     cx, wptr extra_count    ; Number of extra blocks to save
  1765.                 jcxz    save_ems_ok             ; If CX = 0, we exit routine
  1766.  
  1767.                 mov     di, offset dos_blocks   ; DI -> array of segment/size
  1768.                                                 ;  pairs
  1769.  
  1770. ems_extra_save_loop:
  1771.                 mov     ax, wptr [di].block_seg
  1772.                 mov     es, ax                  ; ES = segment to save
  1773.                 mov     ax, wptr [di].block_size; AX = size in paragraphs
  1774.                 push    cx
  1775.                 push    di
  1776.                 call    save_ems_seg            ; Attempt to save this block
  1777.                 pop     di
  1778.                 pop     cx
  1779.                 jc      save_ems_fail           ; Carry flag set == error
  1780.                 add     di, size dos_block
  1781.                 loop    ems_extra_save_loop     ; Keep going through all blocks
  1782. ENDIF
  1783.                 jmp     short save_ems_ok
  1784.  
  1785. save_ems_fail:  mov     dx, wptr handle         ; Failure--free handle
  1786.                 mov     ah, 45h
  1787.                 int     67h                     ; Falls through to failure code
  1788.  
  1789.                                                 ; Initialize pointer
  1790.                                                 ;  to restore routine
  1791. save_ems_ok:    mov     wptr restore_proc, offset @code:restore_ems
  1792.                 clc
  1793.                 jmp     short save_ems_ret
  1794.  
  1795. save_ems_er:    stc
  1796.  
  1797. save_ems_ret:   ret
  1798. save_ems        endp
  1799.  
  1800. ; *****************************************************************************
  1801. ; save_ems_seg  Attempts to save a chunk of RAM to EMS memory
  1802. ;
  1803. ; Entry:        ES points to the segment to save
  1804. ;               AX contains its length (in paragraphs)
  1805. ;               handle holds the EMS handle to write to
  1806. ;               ems_offset holds the 32-bit absolute offset in expanded
  1807. ;                memory to write this block to
  1808. ;
  1809. ; Return:       Carry set on error, carry clear on success
  1810. ;               Updates ems_offset with proper offset for next write
  1811. ; *****************************************************************************
  1812. save_ems_seg    proc    near
  1813.                 push    ds
  1814.                 push    es
  1815.  
  1816.                 assume  ds:@code                ; Tell MASM DS points to our
  1817.                                                 ;  variables
  1818.  
  1819. ; Call the EMS copy memory function to do this; fill in the eMS request block
  1820. ems_write_size: mov     bx, 10h                     ; AX = # of paragraphs
  1821.                 mul     bx                          ; DX:AX = AX * 10h, convert
  1822.                                                     ;  paragraphs to bytes
  1823.                 mov     wptr EMS_size, ax           ; Store # of bytes to write
  1824.                 mov     wptr EMS_size + 2, dx
  1825.  
  1826. ems_write_from: xor     bx, bx
  1827.                 mov     bptr EMS_from, bl           ; Copying from conventional
  1828.                                                     ;  memory (0)
  1829.                 mov     wptr EMS_from_h, bx         ; Source handle is 0
  1830.                                                     ;  (conventional memory)
  1831.                 mov     wptr EMS_from_o, bx         ; Source offset is 0
  1832.                 mov     ax, es                      ; Segment of source address
  1833.                                                     ;  is ES
  1834.                 mov     wptr EMS_from_s, ax
  1835.  
  1836. ems_write_to:   mov     bptr EMS_to, 1              ; Copying to expanded memory
  1837.                 mov     ax, wptr handle
  1838.                 mov     wptr EMS_to_h, ax           ; Specify EMS handle
  1839.  
  1840.                 ; 32-bit absolute offset for copy is in ems_offset
  1841.                 ;  convert to EMS page:offset (16K pages) values
  1842.                 mov     ax, wptr ems_offset         ; Load 32-byte offset
  1843.                 mov     dx, wptr ems_offset + 2
  1844.                 mov     bx, ax                      ; Save a copy of ax (low 16
  1845.                                                     ;  bits)
  1846.                 and     ax, 0011111111111111b       ; Get (ax & (16K - 1)), this
  1847.                                                     ;  is the offset (14 bits)
  1848.                 mov     wptr EMS_to_o, ax           ; Save page offset
  1849.                 mov     cl, 14
  1850.                 shr     bx, cl                      ; Move low 2 bits of page
  1851.                                                     ;  into low 2 bits of bx
  1852.                 mov     cl, 2
  1853.                 shl     dx, cl                      ; Move hi ? bits of page
  1854.                                                     ;  into dx shl 2
  1855.                 or      dx, bx                      ; DX = page number (combine
  1856.                                                     ;  two values)
  1857.                 mov     wptr EMS_to_s, dx           ; Save
  1858.  
  1859.                 mov     ax, wptr EMS_size           ; Retrieve size of copy
  1860.                 mov     dx, wptr EMS_size + 2
  1861.                 add     wptr ems_offset, ax         ; Update EMS copy pointer
  1862.                 adc     wptr ems_offset + 2, dx     ;  for next EMS write
  1863.  
  1864. do_ems_write:   mov     si, offset @code:EMS_struc  ; DS:SI -> EMS request
  1865.                                                     ;  structure
  1866.                 mov     ax, 5700h                   ; Function 57 (copy/exchange
  1867.                                                     ;  memory), sub 0, copy
  1868.                                                     ;  memory
  1869.                 int     67h                         ; Call EMS manager
  1870.                 or      ah, ah                      ; AH = 0 means success
  1871.                 jnz     save_ems_seg_er             ; Not 0 means error
  1872.  
  1873. save_ems_seg_ok:clc                                 ; Signal success
  1874.                 jmp     short save_ems_seg_ret
  1875.  
  1876. save_ems_seg_er:stc
  1877.  
  1878. save_ems_seg_ret:
  1879.                 pop     es
  1880.                 pop     ds
  1881.                 ret
  1882. save_ems_seg    endp
  1883. ENDIF
  1884. ; *****************************************************************************
  1885.  
  1886.  
  1887. ; *****************************************************************************
  1888. ; save_disk     Attempts to save program to DOS disk file
  1889. ;
  1890. ; Entry:        DS points to our variables
  1891. ;
  1892. ; Return:       Carry set on error, carry clear on success
  1893. ;               If successful, updates restore_proc with the address of
  1894. ;               the disk restore routine
  1895. ; *****************************************************************************
  1896. IFDEF USE_DISK
  1897. save_disk       proc    near
  1898.                 push    es
  1899.  
  1900.                 assume  ds:@code                ; Tell MASM DS points to our
  1901.                                                 ;  variables
  1902.  
  1903. creat_file:     mov     dx, offset @code:fname  ; DS:DX -> file name
  1904.                 mov     ah, 3Ch                 ; Create/truncate file
  1905.                 mov     cx, 02h                 ; Create a hidden file
  1906.                 int     21h                     ; Call DOS
  1907.                 jc      save_disk_er            ; Carry set, couldn't create
  1908.                                                 ;  file
  1909.  
  1910. creat_ok:       mov     wptr handle, ax         ; Save handle returned by DOS
  1911.  
  1912. ; First, attempt to save the portion of the program block
  1913. disk_prog_save: mov     ax, wptr start_seg      ; Released segment address
  1914.                 mov     es, ax
  1915.                 mov     ax, wptr prog_size      ; Size (in paragraphs) of
  1916.                                                 ;  program block
  1917.                 call    save_disk_seg           ; Attempt to save the program
  1918.                                                 ;  block
  1919.                 jc      disk_write_er           ; Carry flag set == error
  1920.  
  1921. IFNDEF NOFRAG
  1922. ; Next, save the extra DOS segments
  1923. disk_extra_save:
  1924.                 mov     cx, wptr extra_count    ; Number of extra blocks to save
  1925.                 jcxz    save_disk_ok            ; If CX = 0, we exit routine
  1926.  
  1927.                 mov     di, offset dos_blocks   ; DI -> array of segment/size
  1928.                                                 ; pairs
  1929.  
  1930. disk_extra_save_loop:
  1931.                 mov     ax, wptr [di].block_seg
  1932.                 mov     es, ax                  ; ES = segment to save
  1933.                 mov     ax, wptr [di].block_size; AX = size in paragraphs
  1934.                 push    cx
  1935.                 push    di
  1936.                 call    save_disk_seg           ; Attempt to save this block
  1937.                 pop     di
  1938.                 pop     cx
  1939.                 jc      disk_write_er           ; Carry flag set == error
  1940.                 add     di, size dos_block
  1941.                 loop    disk_extra_save_loop    ; Keep going through all blocks
  1942.  
  1943. ENDIF
  1944.                 jmp     short save_disk_ok
  1945.  
  1946.  
  1947. disk_write_er:  mov     ah, 3Eh                 ; Close file first
  1948.                 mov     bx, wptr handle
  1949.                 int     21h
  1950.                 stc
  1951.                 jmp     short save_disk_ret
  1952.  
  1953.  
  1954. save_disk_ok:   mov     ah, 3Eh                 ; 3eh = close file
  1955.                 mov     bx, wptr handle
  1956.                 int     21h
  1957.  
  1958.                                                 ; Initialize pointer
  1959.                                                 ;  to restore routine
  1960.                 mov     wptr restore_proc, offset @code:restore_disk
  1961.                 clc
  1962.                 jmp     short save_disk_ret
  1963.  
  1964. save_disk_er:   stc
  1965.  
  1966. save_disk_ret:  pop     es
  1967.                 ret
  1968. save_disk       endp
  1969.  
  1970.  
  1971. ; *****************************************************************************
  1972. ; save_disk_seg Attempts to save a chunk of RAM to DOS disk file
  1973. ;
  1974. ; Entry:        ES points to the segment to save
  1975. ;               AX contains its length (in paragraphs)
  1976. ;               handle holds the file handle to write to
  1977. ;
  1978. ;
  1979. ; Return:       Carry set on error, carry clear on success
  1980. ; *****************************************************************************
  1981. save_disk_seg   proc    near
  1982.                 push    ds
  1983.                 push    es
  1984.                 push    di
  1985.  
  1986.                 assume  ds:@code
  1987.  
  1988.                 mov     wptr paras_left, ax     ; Used to count paras written
  1989.                 mov     bx, es
  1990.                 mov     ds, bx                  ; DS -> segment to write
  1991.  
  1992.                 assume  ds:nothing
  1993.  
  1994. disk_write_32k: cmp     ax, 0800h               ; paras_left less than 32K?
  1995.                 jb      finish_disk_write       ; Yes, exit
  1996.                 sub     wptr cs:paras_left, 800h; We will write 32K bytes now
  1997.  
  1998.                 mov     ah, 40h                 ; DOS function to write to file
  1999.                 mov     bx, wptr cs:handle      ; BX = file handle to write to
  2000.                 mov     cx, 8000h               ; Write 32K bytes
  2001.                 xor     dx, dx                  ; DS:DX is buffer to write
  2002.                 int     21h                     ; Write data to file
  2003.                 jc      save_disk_seg_er        ; This write failed--escape
  2004.  
  2005. disk_write_ok:  mov     ax, ds                  ; Move write pointer in memory
  2006.                 add     ax, 800h                ; We just wrote 1K paragraphs
  2007.                 mov     ds, ax
  2008.                 mov     ax, wptr cs:paras_left  ; AX checked above
  2009.                 jmp     short disk_write_32k    ; Loop on next 32K
  2010.  
  2011. finish_disk_write:
  2012.                 mov     cl, 4                   ; AX= # paragraphs left to write
  2013.                 shl     ax, cl                  ; Paragraphs to bytes
  2014.                 mov     cx, ax
  2015.                 mov     ah, 40h                 ; 40h = write to file
  2016.                 mov     bx, wptr cs:handle      ; BX = file handle to write to
  2017.                 xor     dx, dx                  ; DS:DX = buffer
  2018.                 int     21h                     ; Call DOS
  2019.                 jc      save_disk_seg_er        ; Carry set, error (close file
  2020.                                                 ;  first)
  2021. ;
  2022. ; The next two lines added to trap disk full error
  2023. ; A. Warnock, ST Systems Corp.
  2024. ; Goddard Space Flight Center
  2025. ; Greenbelt, MD 21044
  2026. ; Jan. 4, 1991
  2027. ;
  2028.                 cmp     ax,cx                   ; Was write complete?
  2029.                 jne     save_disk_seg_er        ; No - disk must be full
  2030.  
  2031. save_disk_seg_ok:
  2032.  
  2033.                 clc
  2034.                 jmp     short save_disk_seg_ret
  2035.  
  2036. save_disk_seg_er:
  2037.                 stc
  2038.  
  2039. save_disk_seg_ret:
  2040.                 pop     di
  2041.                 pop     es
  2042.                 pop     ds
  2043.  
  2044.                 ret
  2045. save_disk_seg   endp
  2046.  
  2047.  
  2048.  
  2049. ENDIF
  2050. ; *****************************************************************************
  2051.  
  2052. END
  2053.  
  2054.  
  2055.  
  2056.