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