home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c017 / 34.ddi / CSWAPXXX.ZIP / SWAP.ASM < prev    next >
Encoding:
Assembly Source File  |  1990-09-06  |  56.6 KB  |  1,262 lines

  1. page 60, 132
  2.  
  3. ;   SWAP.ASM        Version 2.0     September 6, 1990
  4. ;
  5. ;   Contains code and data needed to swap most of the current program out
  6. ;   to extended memory, expanded memory, or disk; execute another program;
  7. ;   and re-load the original program back into memory.
  8. ;
  9. ;   Copyright (C) 1990 by Marty Del Vecchio
  10. ;   Released to the public domain for free use by all
  11. ;   Product is supplied as is and author disclaims all warranties,
  12. ;   explicit or implied, about the functionality of the source code
  13. ;   or object modules supplied, and shall not be held responsible
  14. ;   for any damages caused by use of product.
  15. ;
  16. ;   Contributions not solicited.  Just appreciate the fact that somebody
  17. ;   took the time to write and comment this code.  If you have any
  18. ;   questions, please contact me at:
  19. ;
  20. ;   Marty Del Vecchio                   Channel 1 BBS
  21. ;   99 Marlboro Road                    Boston, MA
  22. ;   Southborough, MA  01772             (617) 354-8873
  23. ;   (508) 485-9718
  24. ;
  25. ;   internet:  marty@bsn.mceo.dg.com
  26. ;
  27. ;   For information about the contents of this file, see the accompanying
  28. ;   file SWAP.DOC.
  29. ;
  30.  
  31.  
  32. ; 'DOSSEG' gives us support for Microsoft C and Turbo C segment naming
  33. ; and ordering schemes
  34. DOSSEG
  35.  
  36. ; Figure out which memory model we're assembling for.  Specified on
  37. ;  MASM command line with /D followed by either Small, Compact, Medium,
  38. ;  or Large.  If none specified, Small is assumed.
  39. ; Once the model is defined, MASM provides two definitions, @codesize
  40. ;  and @datasize, to determine the size of code and data pointers.  If
  41. ;  @codesize is 0 (Small and Compact), there is one code segment, and
  42. ;  code addresses are 16 bits (offset only).  If @codesize is 1 (Medium
  43. ;  and Large), there are multiple code segments, and code addresses are
  44. ;  32 bits (segment and offset).  Similarly, @datasize of 0 means one
  45. ;  data segment (16-bit pointers), and @datasize of 1 means multiple
  46. ;  data segments (32-bit pointers).
  47.  
  48. IFDEF large
  49.    .MODEL Large, C
  50.    IF1
  51.       %out Assembling for C, Large memory model
  52.    ENDIF
  53. ELSE
  54.    IFDEF compact
  55.       .MODEL Compact, C
  56.       IF1
  57.          %out Assembling for C, Compact memory model
  58.       ENDIF
  59.    ELSE
  60.       IFDEF medium
  61.          .MODEL Medium, C
  62.          IF1
  63.             %out Assembling for C, Medium memory model
  64.          ENDIF
  65.       ELSE
  66.          .MODEL Small, C
  67.          IF1
  68.             %out Assembling for C, Small memory model
  69.          ENDIF
  70.       ENDIF
  71.    ENDIF
  72. ENDIF
  73.  
  74.  
  75. ; Figure out which save method we are using--EMS, XMS, disk, or what
  76. ;  Specified on MASM command line with /D followed by a combination
  77. ;  of "ems", "xms", and "disk" (or "all").  For example, to create a swap()
  78. ;  that will try using XMS and EMS, you would use "masm swap.asm /Dems /Dxms"
  79. ;  If none specified, it will use all.  To change the order in which swap()
  80. ;  attempts to save the program to different places, see the function
  81. ;  save_program below.
  82.  
  83. ; First, see if they want all of them...
  84. IFDEF all
  85.    USE_DISK  EQU 1
  86.    USE_XMS   EQU 1
  87.    USE_EMS   EQU 1
  88. ELSE
  89.    ; /Dall not specified--try each individually...
  90.    IFDEF disk
  91.       USE_DISK  EQU 1
  92.    ENDIF
  93.  
  94.    IFDEF xms
  95.       USE_XMS   EQU 1
  96.     ENDIF
  97.  
  98.    IFDEF ems
  99.       USE_EMS   EQU 1
  100.    ENDIF
  101.  
  102. ENDIF
  103.  
  104. ; Now see if they declared anything--if not, it will use them all
  105. IFNDEF USE_DISK
  106.    IFNDEF USE_EMS
  107.       IFNDEF USE_XMS
  108.          USE_DISK  EQU 1
  109.          USE_XMS   EQU 1
  110.          USE_EMS   EQU 1
  111.       ENDIF
  112.    ENDIF
  113. ENDIF
  114.  
  115. ; Constant definitions for easier reading
  116. STDERR          equ     2           ; Standard DOS file handle for error output
  117. GET_VECTOR      equ     35h         ; DOS function to get interrupt vector
  118. EMM_INT         equ     67h         ; EMS interrupt vector
  119. EMM_NAME_LEN    equ     8           ; Length of EMS device driver name
  120. MAX_DOS_CMD     equ     127         ; Maximum DOS command-line length
  121.  
  122. bptr            equ     byte ptr    ; Means we're loading/storing 8 bits
  123. wptr            equ     word ptr    ; Means we're loading/storing 16 bits
  124. dptr            equ     dword ptr   ; Means we're loading/storing 32 bits
  125.  
  126.  
  127. ; All code and data must be in the code segment, which is the first segment
  128. ;  in all Turbo C, Turbo C++, and Microsoft C memory models.
  129. ; If we are in the Medium or Large models, there are multiple code segments.
  130. ;  If this is the case, our default code segment name will be "SWAP_TEXT".
  131. ;  However, in order for us to be loaded as early as possible in the
  132. ;  executable file, we have to call our segment "_TEXT", which is what it
  133. ;  is called in the Small and Compact models.  This is the segment where
  134. ;  the C compiler puts all of its code.
  135. ; This could be a problem if you are trying to keep certain routines
  136. ;  resident while swap() is executing your program!!!!!!!!
  137. IF @codesize
  138. .CODE   _TEXT
  139. ELSE
  140. .CODE
  141. ENDIF
  142.  
  143. ; *****************************************************************************
  144. ; Our resident data declarations--this data will be needed after the swap
  145. ;  has occurred, and thus must be above the resident line
  146. ; *****************************************************************************
  147.  
  148. ; *****************************************************************************
  149. ; First, all variables that will be used by all versions assembled from
  150. ; this source file, regardless of what save options are selected
  151. ; *****************************************************************************
  152. ret_code    dw      0           ; Return code (to C caller) of this swap routine
  153.                                 ;   0 = success
  154.                                 ;   1 = unable to shrink DOS memory allocation
  155.                                 ;   2 = unable to save program to EMS
  156.                                 ;   3 = unable to execute requested program
  157.  
  158. ; *****************************************************************************
  159. ; Variables that deal with DOS' memory allocation blocks
  160. old_size    dw      0           ; The old size (in paragraphs) of this program
  161. new_size    dw      0           ; The new "resident" size, doesn't include code/data swapped to disk
  162. my_psp      dw      0           ; This program's Program Segment Prefix (PSP)
  163. mcb_psp     dw      0           ; The PSP address in this program's memory block
  164. start_seg   dw      0           ; Segment address of released memory
  165. ; *****************************************************************************
  166.  
  167. ; *****************************************************************************
  168. ; Variables used during the save/restore process
  169. handle      dw      0           ; Expanded memory/disk file handle
  170. saved_paras dw      0           ; Number of paragraphs (16 bytes) saved to ems/disk
  171. paras_left  dw      0           ; temporary counter
  172. ; *****************************************************************************
  173.  
  174. ; *****************************************************************************
  175. ; A temporary stack in our code segment, and associated variables
  176. old_sp      dw      0               ; Place to save this program's stack
  177. old_ss      dw      0               ;  information while executing new program
  178. ; XMS driver needs a large stack (at least 256 bytes free)
  179. IFDEF USE_XMS
  180. new_stack   db      320 dup ('?')   ; Temporary stack we can address after swap
  181. ELSE
  182. new_stack   db      64 dup ('?')    ; Temporary stack we can address after swap
  183. ENDIF
  184. new_sp      label   word            ; Point SP to "top" of stack
  185. ; *****************************************************************************
  186.  
  187. ; *****************************************************************************
  188. ; Variables that deal with the execution of the new program
  189. prog_name   db      128 dup (0)     ; Storage for name of program to execute
  190. cmd_len     db      0               ; Storage for length of command line parameters
  191. cmd_line    db      128 dup (0)     ; Storage for command line parameters
  192.  
  193. param_blk   label   byte            ; Program Parameter Block--pass to DOS on exec call
  194. env_seg     dw      0               ; Environment address, 0 means copy of ours
  195. cmd_ofs     dw      offset @code:cmd_len    ; Offset address of command line
  196. cmd_seg     dw      seg cmd_line    ; Segment address of command line
  197. fcb1        dd      0ffffffffh      ; Default FCB's, -1 = none
  198. fcb2        dd      0ffffffffh
  199. ; *****************************************************************************
  200.  
  201. exec_ret    db      0               ; Return code from executed program
  202. restore_proc dw     0               ; Address of appropriate restore routine
  203.  
  204. ; *****************************************************************************
  205. ; Message to display to screen when we can't reload program
  206. abort_msg   db      0dh, 0ah, 'SWAP: Unable to reload program.', 0dh, 0ah
  207. abort_len   dw      $ - offset @code:abort_msg
  208. ; *****************************************************************************
  209.  
  210. ; *****************************************************************************
  211. ; Next, the variables needed only for certain versions of the routine,
  212. ;  depending on which save/restore options are chosen
  213. ; *****************************************************************************
  214.  
  215. ; *****************************************************************************
  216. ; Variables needed only when swapping to XMS
  217. IFDEF USE_XMS
  218. XMS_proc    dd      0               ; Address of XMS entry point
  219.  
  220. XMS_struc       label   byte        ; Structure needed to move memory with XMS
  221. XMS_size        dd      0           ; # of bytes to move (must be even)
  222. XMS_from        dw      0           ; Handle of source, 0=conventional memory
  223. XMS_from_addr   dd      0           ; Address of source memory
  224. XMS_to          dw      0           ; Handle of destionation, 0=conventional memory
  225. XMS_to_addr     dd      0           ; Address of destination memory
  226. ENDIF
  227. ; *****************************************************************************
  228.  
  229. ; *****************************************************************************
  230. ; Variables needed only when swapping to EMS
  231. IFDEF USE_EMS
  232. pages_used  db      0           ; # of pages of EMS used
  233. next_page   dw      0           ; Temporary storage for EMS page number
  234. emm_name    db      'EMMXXXX0'  ; Name of EMS device driver
  235. ems_seg     dw      0           ; Segment address of EMS page frame
  236. ENDIF
  237. ; *****************************************************************************
  238.  
  239. ; *****************************************************************************
  240. ; Variables needed only when swapping to disk
  241. IFDEF USE_DISK
  242. fname       db      80 dup (0)  ; Name of the file data is saved to/read from
  243. ENDIF
  244. ; *****************************************************************************
  245.  
  246.  
  247.  
  248. ; *****************************************************************************
  249. ; Version-dependent code--only assemble the routine to restore the program
  250. ; from each media (XMS, EMS, disk) if it was specified on the command line
  251. ; *****************************************************************************
  252.  
  253.  
  254. ; *****************************************************************************
  255. ; restore_xms   Attempts to restore program from XMS extended memory
  256. ;
  257. ; Entry:        DS points to our variables
  258. ;               Program was saved to XMS extended memory (handle)
  259. ; Return:       Carry set on error, carry clear on success
  260. ; *****************************************************************************
  261. IFDEF USE_XMS
  262. restore_xms     proc    near
  263.  
  264.                 assume  ds:@code                    ; Tell MASM that DS points to our variables
  265.  
  266. ; All we need to do is copy the old program from XMS to conventional memory
  267. ; Call the XMS copy memory function to do this; so fill in request block
  268. xms_read_size:  mov     ax, wptr saved_paras        ; AX = # of paragraphs to read
  269.                 mov     bx, 10h
  270.                 mul     bx                          ; DX:AX = AX * 10h, # of bytes to read
  271.                 mov     wptr XMS_size, ax           ; Store # of bytes to read
  272.                 mov     wptr XMS_size + 2, dx
  273.  
  274. xms_read_from:  mov     ax, wptr handle             ; Source XMS handle
  275.                 mov     wptr XMS_from, ax
  276.                 xor     bx, bx
  277.                 mov     wptr XMS_from_addr, bx      ; Offset in extended memory
  278.                 mov     wptr XMS_from_addr + 2, bx  ;  block is 0000:0000
  279.  
  280. xms_read_to:    mov     wptr XMS_to, bx             ; Read into conventional memory
  281.                 mov     wptr XMS_to_addr, bx        ; Offset of dest address
  282.                 mov     ax, wptr start_seg          ; Segment of dest address
  283.                 mov     wptr XMS_to_addr + 2, ax
  284.  
  285. do_xms_read:    mov     si, offset @code:XMS_struc  ; DS:SI -> XMS structure
  286.                 mov     ah, 0Bh
  287.                 call    dptr XMS_proc               ; Do the move
  288.  
  289. xms_dealloc:    push    ax                          ; Save that return code!
  290.  
  291.                 mov     dx, wptr handle             ; First, free handle
  292.                 mov     ah, 0Ah
  293.                 call    dptr XMS_proc
  294.  
  295.                 pop     ax                          ; Retrieve copy return code
  296.                 cmp     ax, 1                       ; AX = 1 means success
  297.                 jnz     restore_xms_er              ; Error
  298.  
  299. restore_xms_ok: clc                                 ; Signal no error
  300.                 jmp     short restore_xms_ret
  301.  
  302. restore_xms_er: stc                                 ; Signal error
  303.  
  304. restore_xms_ret:ret
  305. restore_xms     endp
  306. ENDIF
  307. ; *****************************************************************************
  308.  
  309.  
  310. ; *****************************************************************************
  311. ; restore_ems   Attempts to restore program from EMS expanded memory
  312. ;
  313. ; Entry:        DS points to our code segment
  314. ;               Program was saved to EMS expanded memory (handle)
  315. ; Return:       Carry set on error, carry clear on success
  316. ; *****************************************************************************
  317. IFDEF USE_EMS
  318. restore_ems     proc    near
  319.  
  320.                 push    ds                      ; Save original DS
  321.                 cld                             ; All memory copies increase SI and DI
  322.  
  323.                 assume  ds:@code                ; Tell MASM that DS points to our variables
  324.  
  325.                 mov     bx, wptr saved_paras    ; Get # of paragraphs saved
  326.                 mov     wptr paras_left, bx     ; Used to count paras restored
  327.                 mov     ax, wptr ems_seg
  328.                 mov     ds, ax                  ; DS:0 = EMS source
  329.  
  330.                 ; DS no longer points to our segment!  All references to
  331.                 ;  our variables must have cs: override until the end of
  332.                 ;  this routine.
  333.  
  334.                 assume  ds:nothing              ; Tell MASM that DS doesn't point to our variables
  335.  
  336.                 mov     ax, wptr cs:start_seg
  337.                 mov     es, ax                  ; ES:0 = program destination
  338.                 mov     wptr cs:next_page, 0    ; Start with page 0
  339.                 mov     dx, wptr cs:handle      ; Retrieve EMS handle
  340.  
  341.                 mov     ah, 47h                 ; Save page map first
  342.                 int     67h
  343.  
  344.                 or      ah, ah                  ; check EMS return code (ah = 0, OK)
  345.                 jz      ems_read_16K            ; ah was 0, OK
  346.  
  347. ems_read_fail:  mov     ah, 48h                 ; Some kind of EMS failure--
  348.                 int     67h                     ;  first restore page map
  349.                 mov     ah, 45h                 ; Deallocate my handle
  350.                 int     67h                     ;  (passed in dx)
  351.  
  352. jmp     short restore_ems_er    ; Signal error
  353.  
  354. ems_read_16k:   mov     ah, 44h                 ; Map page
  355.                 mov     bx, wptr cs:next_page   ; Next logical page to map
  356.                 mov     al, 0
  357.                 int     67h                     ; Map the next page
  358.                 or      ah, ah
  359.                 jnz     ems_read_fail           ; ah != 0 means failure
  360.  
  361.                 cmp     wptr cs:paras_left, 400h    ; Less than 16K left?
  362.                 jb      last_ems_read               ; Yes, do last EMS read
  363.                 sub     wptr cs:paras_left, 400h    ; 16K less to read
  364.                 xor     si, si                      ; Set SI and DI to 0
  365.                 xor     di, di
  366.                 mov     cx, 2000h               ; Write 8K words = 16K bytes
  367.                                                 ; DS:SI = expanded memory page (source)
  368.                                                 ; ES:DI = location to restore to (destination)
  369.                 rep     movsw                   ; Move the data from EMS
  370.  
  371.                 mov     ax, es                  ; Get old restore pointer
  372.                 add     ax, 400h                ; Move restore pointer up 16K
  373.                 mov     es, ax                  ; Set new restore pointer
  374.                 inc     wptr cs:next_page       ; Look at next EMS page
  375.                 jmp     short ems_read_16k      ; Read next 16K from EMS
  376.  
  377. last_ems_read:  mov     ax, wptr cs:paras_left  ; Paragraphs left to restore
  378.                 mov     cl, 4                   ; Convert to bytes
  379.                 shl     ax, cl
  380.                 mov     cx, ax                  ; CX = remaining bytes to restore
  381.                 xor     si, si
  382.                 xor     di, di
  383.                 rep     movsb                   ; Restore the remaining bytes
  384.  
  385. restore_ems_ok: mov     ah, 48h                 ; Restore EMS page map
  386.                 int     67h
  387.  
  388. ems_dealloc:    mov     ah, 45h                 ; Deallocate pages
  389.                 int     67h
  390.  
  391.                 clc                             ; Signal no error
  392.                 jmp     short restore_ems_ret
  393.  
  394. restore_ems_er: stc                             ; Signal error
  395.  
  396. restore_ems_ret:pop     ds
  397.                 ret
  398. restore_ems     endp
  399. ENDIF
  400. ; *****************************************************************************
  401.  
  402.  
  403. ; *****************************************************************************
  404. ; restore_disk  Attempts to restore program from DOS disk file
  405. ;
  406. ; Entry:        DS points to our code segment
  407. ;               Program was saved to DOS disk file (fname)
  408. ; Return:       Carry set on error, carry clear on success
  409. ; *****************************************************************************
  410. IFDEF USE_DISK
  411. restore_disk    proc    near
  412.  
  413.                 assume  ds:@code                ; Tell MASM that DS points to our variables
  414.  
  415. open_file:      mov     dx, offset @code:fname  ; DS:DX -> file name
  416.                 mov     ax, 3D42h               ; DOS function 3Dh, open file
  417.                                                 ;  al = open for read only, deny none
  418.                 int     21h                     ; Call DOS
  419.                 jnc     open_ok                 ; Carry clear = all OK
  420.                 jmp     restore_disk_er         ; Carry set, error
  421.  
  422. open_ok:        mov     wptr handle, ax         ; File handle returned from DOS
  423.                 mov     bx, ax                  ; Must specify in BX later
  424.                 mov     ax, wptr start_seg      ; First segment to restore to
  425.                 mov     ds, ax
  426.                 mov     ax, wptr saved_paras    ; # of paragraphs in file to read
  427.                 mov     wptr paras_left, ax     ; Keep count in this variable
  428.  
  429. disk_read_16k:  cmp     ax, 0400h               ; Less than 16K left?
  430.                 jb      last_disk_read          ; Yes, do last read
  431.                 sub     wptr cs:paras_left, 0400h   ; 16K left to read
  432.                 mov     ah, 3Fh                 ; DOS function 3fh, read file
  433.                 mov     cx, 4000h               ; Read 16K bytes
  434.                 xor     dx, dx                  ; DS:DX -> buffer to read to
  435.                 int     21h                     ; Call DOS
  436.                 jc      disk_read_err           ; Carry set = error
  437.  
  438. disk_read_ok:   mov     ax, ds                  ; Address next read location
  439.                 add     ax, 0400h               ; It's 400h paragraphs ahead
  440.                 mov     ds, ax                  ; DS -> new restore location
  441.                 mov     ax, paras_left          ; Expecting this above
  442.                 jmp     short disk_read_16k     ; Read next 16K
  443.  
  444. disk_read_err:  mov     ah, 3eh                 ; Error, close file first
  445.                 int     21h                     ; Call DOS
  446.                 jmp     restore_disk_er         ; Return with error code
  447.  
  448. last_disk_read: mov     cx, 4                   ; Convert paragraphs to bytes
  449.                 shl     ax, cl
  450.                 mov     cx, ax                  ; # of bytes left in cx
  451.                 mov     ah, 3fh                 ; Read last bytes
  452.                 xor     dx, dx                  ; DS:DX -> buffer to restore to
  453.                 int     21h                     ; Call DOS
  454.                 jc      disk_read_err           ; Error reading!  Close file first
  455.  
  456. close_read:     mov     ah, 3eh                 ; Close file
  457.                 int     21h                     ; Call DOS
  458.  
  459. restore_disk_ok:clc                             ; Signal success
  460.                 jmp     short restore_disk_ret  ;  and Exit
  461.  
  462. restore_disk_er:stc                             ; Signal failure
  463.  
  464. restore_disk_ret:
  465.                 rcl     bl, 1                   ; Save carry flag in low bit of bl
  466.  
  467.                 mov     dx, offset @code:fname  ; DS:DX -> file name
  468.                 mov     ah, 41h                 ; DOS function 41h, delete file
  469.                 int     21h                     ; Call DOS
  470.  
  471.                 rcr     bl, 1                   ; Restore carry flag from low bit of bl
  472.  
  473.                 ret
  474. restore_disk    endp
  475. ENDIF
  476. ; *****************************************************************************
  477.  
  478.  
  479.                 
  480. ; *****************************************************************************
  481. ; execute_program   Execute the program specified
  482. ;
  483. ; Entry:            param_blk has been initialized
  484. ;                   DS points to our data
  485. ; Return:           puts return code in cs:exec_ret
  486. ; *****************************************************************************
  487. execute_program proc    near                    ; Called only from inside our segment
  488.  
  489.                 push    ds                      ; These are destroyed by the
  490.                 push    es                      ;  DOS EXEC call
  491.  
  492.                 assume  ds:@code                ; Tell MASM that DS points to our variables
  493.  
  494. exec_program:   mov     ax, ds                  ; Our path name is in CS (point DS to our segment)
  495.                 mov     es, ax                  ; Our parameter block is in CS (point ES to our segment)
  496.                 mov     ax, 4b00h               ; Load and execute program
  497.                 mov     bx, offset @code:param_blk
  498.                 mov     dx, offset @code:prog_name
  499.                 int     21h                     ; Sets carry flag if error
  500.                                                 ; All registers destroyed
  501.                                                 ;  except CS:IP!
  502.  
  503.                 assume  ds:nothing              ; Tell MASM that DS doesn't point to our variables
  504.  
  505.                 mov     bptr cs:exec_ret, al    ; Store EXEC code
  506.                 jc      exec_err                ; Ooops
  507.  
  508. get_return:     mov     ah, 4dh                 ; DOS function to get ret code
  509.                 int     21h                     ; All registers destroyed
  510.                 mov     bptr cs:exec_ret, al    ; Store EXEC code
  511.                 jmp     short exec_exit
  512.  
  513. exec_err:       mov     wptr cs:ret_code, 3     ; Signal error on executing
  514.  
  515. exec_exit:      pop     es
  516.                 pop     ds
  517.  
  518.                 ret
  519.  
  520. execute_program endp
  521.  
  522.  
  523. ; *****************************************************************************
  524. ; err_exit          Prints error message and terminates program
  525. ;
  526. ; Entry:            Nothing.
  527. ; Returns:          Doesn't return--calls DOS terminate function.
  528. ;                   Naturally, we can't use the C runtime routines,
  529. ;                   since they are swapped out.
  530. ; *****************************************************************************
  531. err_exit        proc    near                    ; Called only from inside our segment
  532.  
  533.                 mov     ax, cs
  534.                 mov     ds, ax                  ; Point DS to our data
  535.  
  536.                 assume  ds:@code                ; Tell MASM that DS points to our data
  537.  
  538.                 mov     ah, 40h                 ; DOS function to write to file
  539.                 mov     bx, STDERR              ; Write to standard error handle
  540.                 mov     cx, wptr abort_len      ; CX = length of message
  541.                 mov     dx, offset @code:abort_msg  ; DS:DX = message
  542.                 int     21h
  543.  
  544.                 mov     ax, 4CFFh           ; Exit, return code 255 decimal
  545.                 int     21h                 ; Exit to DOS, no return
  546.  
  547. err_exit        endp
  548.  
  549.  
  550. ; *****************************************************************************
  551. ;   swap        The routine that does it all
  552. ;
  553. ;   Callable by a C program, takes these parameters (regardless
  554. ;     of which swap options chosen at assembly time, because
  555. ;     C calling conventions let is ignore parameters to the
  556. ;     right if we want to):
  557. ;
  558. ;   swap_both:
  559. ;       prog        Full path name of program to execute
  560. ;       cmdline     Command-line parameters for program to execute
  561. ;       return      Pointer to byte for return code of exec'd program
  562. ;       save_file   Full path name of file in which to save program image (if disk is to be used)
  563. ;
  564. ;   Depending on the memory model used, the pointers to the
  565. ;   parameters each occupy 2 bytes or 4 bytes on the stack.
  566. ;   If there is only one data segment (Small and Medium), each
  567. ;   value is a 2-byte near pointer, with DS assumed as the segment
  568. ;   register.  If there are multiple data segments (Compact and
  569. ;   Large), each value is a 4-byte far pointer, with segment and
  570. ;   offset values each pushed on the stack.
  571. ;
  572. ;   The function is declared with 4 parameters, regardless of whether
  573. ;   disk swapping is being included.  This is because the file name
  574. ;   parameter is the last on the parameter list, which C lets us
  575. ;   ignore if we want.
  576. ;
  577. ;   The swap() routine does not check the program name or command
  578. ;   line to verify that a legal command has been requested--that's
  579. ;   the caller's responsibility!
  580. ;
  581. ; *****************************************************************************
  582.  
  583.                 public  swap
  584.  
  585. swap            proc    prog:PTR, cmdline:PTR, return:PTR, save_file:PTR
  586.  
  587.                 push    si                      ; Save registers needed
  588.                 push    di                      ;  by the caller
  589.                 push    es
  590.                 push    ds
  591.  
  592. point_segs:     mov     ax, cs                  ; Point ES to our segment
  593.                 mov     es, ax                  ;  for copying of parameters
  594.  
  595. ; *****************************************************************************
  596. get_name:       ; Copy program name to our variable, all versions
  597.  
  598. ; If multiple data segments, load DS:SI from stack.  Else, just load SI
  599. IF @datasize
  600.                 push    ds                      ; Save segment register
  601.                 lds     si, dptr prog           ; Load 32-bit far pointer
  602. ELSE
  603.                 mov     si, wptr prog           ; Load 16-bit near pointer
  604. ENDIF                                           ; DS:SI -> program name from caller
  605.  
  606.                 mov     di, offset @code:prog_name  ; ES:DI -> our storage area
  607.  
  608. name_loop:      lodsb                           ; Fetch next byte
  609.                 stosb                           ; Save next byte
  610.                 or      al, al                  ; Was it 0 (end of string)?
  611.                 jnz     name_loop               ; No, get next one
  612.  
  613. IF @datasize
  614.                 pop     ds                      ; Pop DS if it was pushed above
  615. ENDIF
  616. ; *****************************************************************************
  617.  
  618. ; *****************************************************************************
  619. get_cmd:        ; Copy command line to our variable, all versions
  620.  
  621. ; If multiple data segments, load DS:SI from stack.  Else, just load SI
  622. IF @datasize
  623.                 push    ds                      ; Save segment register
  624.                 lds     si, dptr cmdline        ; Load 32-bit far pointer
  625. ELSE
  626.                 mov     si, wptr cmdline        ; Load 16-bit near pointer
  627. ENDIF                                           ; DS:SI -> command line from caller
  628.                 
  629.                 mov     di, offset @code:cmd_line   ; ES:DI -> our storage area
  630.                 xor     cl, cl                  ; Keep track of length in cl
  631.  
  632. cmd_loop:       lodsb                           ; Fetch next byte from DS:SI
  633.                 or      al, al                  ; Was it 0 (end of string)?
  634.                 jz      cmd_end                 ; Yes, we're done
  635.                 stosb                           ; No, store byte
  636.                 inc     cl                      ; Increment length
  637.                 cmp     cl, MAX_DOS_CMD         ; Are we at maximum cmd length?
  638.                 jnz     cmd_loop                ; Nope, keep going
  639.  
  640. cmd_end:        mov     bptr es:[di], 0dh       ; Put CR at end of cmd line
  641.                 mov     bptr cs:cmd_len, cl     ; Store command-line length
  642.  
  643. IF @datasize
  644.                 pop     ds                      ; Pop DS if it was pushed above
  645. ENDIF
  646. ; *****************************************************************************
  647.  
  648. ; *****************************************************************************
  649. ; Get the file name from the command line, if this version needs it
  650. IFDEF USE_DISK
  651. get_file:
  652.  
  653. ; If multiple data segments, load DS:SI, else just load SI
  654. IF @datasize
  655.                 push    ds                      ; Save segment register
  656.                 lds     si, dptr save_file      ; Load 32-bit pointer
  657. ELSE
  658.                 mov     si, save_file           ; Load 16-bit pointer
  659. ENDIF                                           ; DS:SI -> swap file name from caller
  660.  
  661.                 mov     di, offset @code:fname  ; ES:DI -> our storage area
  662.  
  663. resolve:        mov     ah, 60h                 ; DOS INTERNAL function to resolve file name to full path name
  664.                 int     21h                     ; Stores complete path at ES:DI--we need it after EXEC in case
  665.                                                 ;  current drive or directory have changed
  666.                                                 ; Ignore file name error here--it
  667.                                                 ;  will be caught in save_disk if need be
  668.  
  669. IF @datasize
  670.                 pop     ds                      ; Pop DS if it was pushed above
  671. ENDIF
  672.  
  673. ENDIF           ; IFDEF disk
  674. ; *****************************************************************************
  675. ; We have the parameters--let's go
  676. ; *****************************************************************************
  677.  
  678.                 mov     wptr cs:ret_code, 0     ; Initialize swap's return code
  679.                 mov     cs:exec_ret, 0          ; Initialize exec's return code
  680.  
  681. save_stack:     mov     ax, ss
  682.                 mov     wptr cs:old_ss, ax      ; Save current SS
  683.                 mov     ax, sp
  684.                 mov     wptr cs:old_sp, ax      ; Save current SP
  685.  
  686. our_stack:      mov     ax, cs                  ; Our stack is in our CS
  687.                 cli                             ; Disable interrupts
  688.                 mov     ss, ax
  689.                 mov     sp, offset @code:new_sp ; Set new stack
  690.                 sti                             ; Re-enable interrupts
  691.  
  692. save_regs:      push    es                      ; Save needed registers
  693.                 push    ds                      ; This is the caller's DS!
  694.                 push    bp
  695.  
  696.                 mov     ax, cs
  697.                 mov     ds, ax                  ; Point DS to our data
  698.  
  699.                 assume  ds:@code                ; Tell MASM that DS points to our variables
  700.  
  701. save_info:      mov     ah, 51h                 ; DOS function 51h, get PSP
  702.                 int     21h                     ; Call DOS
  703.                 mov     ax, bx                  ; ax = PSP
  704.                 mov     wptr my_psp, ax         ; Save in cs: addressable location
  705.                 dec     ax                      ; PSP-1 = MCB for this mem block
  706.                 mov     es, ax
  707.                 mov     ax, es:[0001h]          ; Get PSP address--should be same!
  708.                 cmp     ax, wptr my_psp         ; All kosher?
  709.                 jz      psp_ok                  ; Yes
  710.  
  711. psp_error:      mov     wptr ret_code, 1        ; No, pass return code
  712.                 jmp     short exit_swap         ; Exit
  713.  
  714. psp_ok:         call    near ptr calc_size      ; Calc size to keep, save
  715.  
  716. try_save:       call    near ptr save_program   ; Write program to disk
  717.                 jnc     shrink_mem              ; Carry flag set on error
  718.  
  719. no_save:        mov     wptr ret_code, 2        ; Error--set return code
  720.                 jmp     short exit_swap         ; Exit routine on error
  721.  
  722. shrink_mem:     mov     ah, 4ah                 ; DOS 4Ah--modify memory allocation
  723.                 mov     es, wptr my_psp         ; Point to PSP again
  724.                 mov     bx, wptr new_size       ; new_size was figured in calc_size
  725.                 int     21h                     ; Call DOS to shrink size
  726.  
  727. ; *****************************************************************************
  728. ; Any routine called or data referred to after this point MUST be located
  729. ;  in this source file BEFORE the variable new_mcb below!
  730. ; *****************************************************************************
  731.  
  732.                 jnc     exec_prog               ; No carry = success
  733.  
  734. no_shrink:      mov     wptr ret_code, 1        ; Carry = couldn't shrink block
  735.                 jmp     short exit_swap         ; Should delete file here!
  736.  
  737. exec_prog:      call    near ptr execute_program    ; Execute the specified program
  738.                 jnc     re_size                     ; No carry, OK
  739.  
  740. exec_er:        mov     wptr ret_code, 3        ; Signal error
  741.  
  742. re_size:        mov     es, wptr my_psp         ; Get our PSP address
  743.                 mov     bx, wptr old_size       ; Increase back to old size
  744.                 mov     ah, 4ah                 ; DOS function 4Ah = resize
  745.                 int     21h
  746.                 jnc     restore_prog            ; Carry clear = all OK
  747.  
  748. resize_err:     call    near ptr err_exit       ; Can't return, exit to DOS
  749.  
  750. restore_prog:   call    wptr restore_proc       ; Restore program from disk
  751.                 jc      resize_err              ; Carry set if error
  752.  
  753. read_ok:
  754. exit_swap:      pop     bp                      ; Restore saved registers
  755.                 pop     ds                      ; This is the caller's DS!
  756.                 pop     es
  757.  
  758.                 assume  ds:nothing              ; Tell MASM DS doesn't point to our variables
  759.  
  760. prev_stack:     mov     ax, wptr cs:old_ss      ; Restore original stack
  761.                 cli
  762.                 mov     ss, ax
  763.                 mov     sp, wptr cs:old_sp
  764.                 sti
  765.  
  766. ; Giving user exec's return code.  It could be a 16- or 32-bit pointer
  767. IF @datasize
  768.                 push    ds
  769.                 lds     si, dptr return         ; Load 32-bit pointer
  770. ELSE
  771.                 mov     si, wptr return         ; Load 16-bit pointer
  772. ENDIF                                           ; DS:SI -> return code variable
  773.                 
  774.                 mov     al, bptr cs:exec_ret    ; Store exec's return code
  775.                 mov     bptr [si], al           ;  at address specified by caller
  776.  
  777. IF @datasize
  778.                 pop     ds                      ; Pop DS if pushed above
  779. ENDIF
  780.  
  781.                 pop     ds
  782.                 pop     es
  783.                 pop     di
  784.                 pop     si
  785.                 mov     ax, wptr cs:ret_code    ; Give return code
  786.                 ret
  787. swap            endp
  788. ; *****************************************************************************
  789.  
  790.  
  791. ; *****************************************************************************
  792. ; *****************************************************************************
  793. ALIGN 10h       ; Aligns next code item on paragraph boundary
  794. para_align      proc    near
  795. new_mcb         db      32 dup (0)          ; DOS will put MCB of released memory here
  796. para_align      endp
  797. ; *****************************************************************************
  798. ; *****************************************************************************
  799.  
  800. ; *****************************************************************************
  801. ; Everything after here is only needed BEFORE we change our allocation size.
  802. ;  Everything below this line will be (temporarily) swapped out of memory,
  803. ;  and thus cannot be used once we shrink our memory allocation.
  804. ; *****************************************************************************
  805.  
  806. ; *****************************************************************************
  807. ; calc_size     Calculates the size of the program to keep and
  808. ;               how much of it to swap out.
  809. ;
  810. ; Entry:        DS points to our variables
  811. ;               ES points to DOS Memory Control Block for our program
  812. ; Return:       old_size, start_seg, new_size initialized
  813. ; *****************************************************************************
  814. calc_size       proc    near                    ; Called only from inside our segment
  815.  
  816.                 assume  ds:@code                ; Tell MASM that DS points to our variables
  817.  
  818.                 mov     ax, es:[0003h]          ; Get # paragraphs allocated
  819.                                                 ;  in this memory block
  820.                 mov     wptr old_size, ax       ; Save old size of program
  821.                 mov     bx, cs                  ; BX = segment of our code
  822.                 mov     ax, offset @code:new_mcb; Last address to keep
  823.                 mov     cl, 4                   ; new_mcb is para aligned
  824.                 shr     ax, cl                  ; AX = ofs new_mcb / 16
  825.                 inc     ax
  826.                 add     bx, ax
  827.                 mov     wptr start_seg, bx      ; Segment of released memory
  828.                 sub     bx, wptr my_psp         ; BX = size to keep in paragraphs
  829.                 mov     wptr new_size, bx       ; Save new, smaller size
  830.                 ret
  831.  
  832. calc_size       endp
  833. ; *****************************************************************************
  834.  
  835. ; *****************************************************************************
  836. ; xms_installed     Checks to see if XMS driver (himem.sys) is loaded
  837. ;
  838. ; Entry:            No assumptions--can be called by user
  839. ; Return:           1 if XMS driver is load, 0 if not
  840. ; *****************************************************************************
  841. IFDEF USE_XMS
  842.                 public  xms_installed
  843. xms_installed   proc                            ; Called by user also!
  844.  
  845.                 push    ds                  ; Save all "important" registers
  846.                 push    si
  847.                 push    es
  848.                 push    di
  849.  
  850.                 mov     ax, 4300h           ; Multiplex code for XMS driver, load check function
  851.                 int     2Fh                 ; Call multiplex interrupt
  852.                 cmp     al, 80h             ; al = 80h means XMS driver IS loaded
  853.                 jnz     no_xms              ; Nope, not there
  854.  
  855. yes_xms:        mov     ax, 4310h               ; Get address of entry point
  856.                 int     2Fh                     ; Returns address in ES:BX
  857.                 mov     wptr cs:XMS_proc, bx
  858.                 mov     wptr cs:XMS_proc + 2, es
  859.                 mov     ax, 1                   ; Return 1, XMS installed
  860.                 jmp     short xms_ret
  861.  
  862. no_xms:         xor     ax, ax              ; Return 0, XMS not installed
  863.  
  864. xms_ret:        pop     di
  865.                 pop     es
  866.                 pop     si
  867.                 pop     ds
  868.                 ret
  869.  
  870. xms_installed   endp
  871. ENDIF
  872. ; *****************************************************************************
  873.  
  874. ; *****************************************************************************
  875. ; ems_installed     Checks to see if EMS driver is loaded
  876. ;
  877. ; Entry:            No assumptions--can be called by user
  878. ; Return:           1 if EMS driver is load, 0 if not
  879. ; *****************************************************************************
  880. IFDEF USE_EMS
  881.                 public  ems_installed
  882. ems_installed   proc                            ; Called by user also!
  883.  
  884.                 push    ds                      ; Save "important" registers
  885.                 push    si
  886.                 push    es
  887.                 push    di
  888.  
  889.  
  890. get_emm_vector: mov     ah, GET_VECTOR          ; Get EMM interrupt vector
  891.                 mov     al, 67h                 ; EMM accessed through Int 67h
  892.                 int     21h                     ; Call DOS to get vector
  893.                 mov     di, 0ah                 ; vector + di = name
  894.                 mov     ax, cs
  895.                 mov     ds, ax                  ; DS:SI -> EMM device driver name
  896.                 mov     si, offset @code:emm_name   ; Compare with EMM device name
  897.                 mov     cx, EMM_NAME_LEN
  898.                 cld
  899.                 repe    cmpsb                   ; Compare bytes
  900.                 jnz     ems_no                  ; Same?  If not, EMS installed
  901.  
  902. ems_yes:        mov     ax, 1                   ; EMS installed, return 1
  903.                 jmp     short ems_ret
  904.  
  905. ems_no:         xor     ax, ax                  ; EMS not installed, return 0
  906.  
  907. ems_ret:        pop     di
  908.                 pop     es
  909.                 pop     si
  910.                 pop     ds
  911.                 ret
  912.  
  913. ems_installed   endp
  914. ENDIF
  915. ; *****************************************************************************
  916.  
  917.  
  918. ; *****************************************************************************
  919. ; save_program      Try to save in XMS/EMS/disk.
  920. ;
  921. ; Entry:            DS points to our variables
  922. ;
  923. ; Returns:          Success:  carry flag clear
  924. ;                   Failure:  carry flag set
  925. ; *****************************************************************************
  926. save_program    proc    near            ; Called only from inside our segment
  927.  
  928.                 push    si              ; Save registers
  929.                 push    di
  930.                 push    ds
  931.                 push    es
  932.  
  933. ; Now figure out which routines to call, based on command-line definitions
  934. ; To change the order in which swap() attempts to swap, change the order
  935. ;  of these three conditional blocks.
  936. IF1
  937.    %out swap() will attempt to save the program in the following order:
  938. ENDIF
  939.    
  940.  
  941. ; *****************************************************************************
  942. IFDEF USE_XMS
  943. IF1
  944.    %out -- XMS extended memory
  945. ENDIF
  946.                 call    save_xms        ; Try saving to XMS extended memory
  947.                 jnc     save_ok         ; Carry clear == success, all done
  948. ENDIF
  949. ; *****************************************************************************
  950.  
  951.  
  952. ; *****************************************************************************
  953. IFDEF USE_EMS
  954. IF1
  955.    %out -- EMS expanded memory
  956. ENDIF
  957.                 call    save_ems        ; Try saving to EMS expanded memory
  958.                 jnc     save_ok       ; Carry clear == success, all done
  959. ENDIF
  960. ; *****************************************************************************
  961.  
  962.  
  963. ; *****************************************************************************
  964. IFDEF USE_DISK
  965. IF1
  966.    %out -- DOS disk file
  967. ENDIF
  968.                 call    save_disk       ; Try saving to DOS disk file
  969.                 jnc     save_ok         ; Carry clear == success, all done
  970. ENDIF
  971. ; *****************************************************************************
  972.  
  973. save_er:        stc                     ; Couldn't save anywhere, return error
  974.                 jmp     short save_ret
  975.  
  976. save_ok:        clc                     ; Saved successfully, return OK
  977.  
  978. save_ret:       pop     es              ; Restore registers
  979.                 pop     ds
  980.                 pop     di
  981.                 pop     si
  982.  
  983.                 ret
  984. save_program    endp
  985. ; *****************************************************************************
  986.  
  987.  
  988. ; *****************************************************************************
  989. ; Version-dependent code--only assemble the routine to save the program
  990. ; to each place if it was requested on the command line
  991. ; *****************************************************************************
  992.  
  993.  
  994. ; *****************************************************************************
  995. ; save_xms      Attempts to save program to XMS extended memory
  996. ;
  997. ; Entry:        DS points to our variables
  998. ;
  999. ; Return:       Carry set on error, carry clear on success
  1000. ;               If successful, updates restore_proc with the address of
  1001. ;               the XMS restore routine
  1002. ; *****************************************************************************
  1003. IFDEF USE_XMS
  1004. save_xms        proc    near
  1005.  
  1006.                 assume  ds:@code                ; Tell MASM DS points to our variables
  1007.  
  1008.                 call    xms_installed           ; Check if XMS installed
  1009.                 or      ax, ax                  ; Returns 0 if not installed
  1010.                 jnz     xms_inst                ; AX != 0, XMS installed
  1011.                 jmp     save_xms_er             ; AX == 0, XMS not installed
  1012.  
  1013. xms_inst:       mov     dx, wptr old_size       ; Old size in paragraphs
  1014.                 sub     dx, wptr new_size       ; DX = size to save to XMS
  1015.                 mov     wptr saved_paras, dx    ; Save # of paragraphs written
  1016.  
  1017.                 mov     cl, 6                   ; Convert Paragraphs to kilobytes
  1018.                 shr     dx, cl                  ; dx = dx / 64
  1019.                 inc     dx                      ; dx = kilobytes needed (plus 1 for safety)
  1020.  
  1021. xms_alloc:      mov     ah, 09h                 ; XMS function 09, allocate extended memory block
  1022.                 call    dptr XMS_proc           ; Call XMS entry point directly
  1023.                 cmp     ax, 1                   ; AX = 1 on success
  1024.                 jnz     save_xms_er             ; Allocation unsuccessful, error
  1025.  
  1026. xms_alloc_ok:   mov     wptr handle, dx         ; Save returned handle in DX
  1027.  
  1028. ; OK, memory is allocated.  Now fill in the XMS request block for memory copy
  1029. xms_write_size: mov     ax, wptr saved_paras        ; AX = # of paragraphs to write
  1030.                 mov     bx, 10h
  1031.                 mul     bx                          ; DX:AX = AX * 10h
  1032.                 mov     wptr XMS_size, ax           ; Store # of bytes to write
  1033.                 mov     wptr XMS_size + 2, dx
  1034.  
  1035. xms_write_from: xor     bx, bx
  1036.                 mov     wptr XMS_from, bx           ; 0 means from conventional memory
  1037.                 mov     wptr XMS_from_addr, bx      ; Offset of source address
  1038.                 mov     ax, wptr start_seg          ; Segment of source address
  1039.                 mov     wptr XMS_from_addr + 2, ax
  1040.  
  1041. xms_write_to:   mov     ax, wptr handle             ; Destination XMS handle
  1042.                 mov     wptr XMS_to, ax
  1043.                 mov     wptr XMS_to_addr, bx        ; Offset in extended memory
  1044.                 mov     wptr XMS_to_addr + 2, bx    ;  block is 0000:0000
  1045.  
  1046. do_write:       mov     si, offset @code:XMS_struc  ; DS:SI -> XMS request structure
  1047.                 mov     ah, 0Bh                     ; Function B, copy memory
  1048.                 call    dptr XMS_proc               ; Do the memory copy move
  1049.                 cmp     ax, 1                       ; AX = 1 means success
  1050.                 jz      save_xms_ok                 ; Success, all done!
  1051.  
  1052. write_error:    mov     dx, wptr handle             ; Free allocated handle
  1053.                 mov     ah, 0Ah
  1054.                 call    dptr XMS_proc               ; Falls through to failure code
  1055.  
  1056. save_xms_er:    stc
  1057.                 jmp     short save_xms_ret
  1058.  
  1059. save_xms_ok:    mov     wptr restore_proc, offset @code:restore_xms
  1060.                 clc
  1061.  
  1062. save_xms_ret:   ret
  1063. save_xms        endp
  1064. ENDIF
  1065. ; *****************************************************************************
  1066.  
  1067.  
  1068. ; *****************************************************************************
  1069. ; save_ems      Attempts to save program to EMS expanded memory
  1070. ;
  1071. ; Entry:        DS points to our variables
  1072. ;
  1073. ; Return:       Carry set on error, carry clear on success
  1074. ;               If successful, updates restore_proc with the address of
  1075. ;               the EMS restore routine
  1076. ; *****************************************************************************
  1077. IFDEF USE_EMS
  1078. save_ems        proc    near
  1079.  
  1080.                 assume  ds:@code                ; Tell MASM DS points to our variables
  1081.  
  1082.                 push    ds                      ; Save DS--lost later
  1083.                 cld                             ; All memory copies increment SI and DI
  1084.  
  1085.                 call    ems_installed           ; Check if EMS installed
  1086.                 or      ax, ax                  ; AX = 0 if not installed
  1087.                 jnz     ems_inst                ; AX != 0, ems installed
  1088.                 jmp     save_ems_er             ; AX = 0, no EMS, error!
  1089.  
  1090. ems_inst:       mov     ah, 41h                 ; Get page frame segment
  1091.                 int     67h
  1092.                 or      ah, ah                  ; ah = 0 on success
  1093.                 jz      save_ems1               ; Zero, all OK
  1094.                 jmp     save_ems_er             ; Non-zero, fatal error
  1095.  
  1096. save_ems1:      mov     wptr ems_seg, bx        ; Store segment address
  1097.  
  1098.                 mov     bx, wptr old_size       ; Segment of released memory
  1099.                 sub     bx, wptr new_size       ; BX = # paragraphs to save
  1100.                 mov     wptr saved_paras, bx    ; Save
  1101.                 mov     wptr paras_left, bx     ; Used to count paras written
  1102.  
  1103.                 mov     cl, 10                  ; Convert Paragraphs to 16K pages
  1104.                 shr     bx, cl
  1105.                 inc     bx                      ; BX = pages needed
  1106.                 mov     bptr pages_used, bl     ; Save for later use
  1107.  
  1108.                 mov     ah, 43h                 ; EMM function 43h, allocate
  1109.                 int     67h
  1110.                 or      ah, ah                  ; OK return code?
  1111.                 jz      ems_alloc_ok            ; Yes, skip ahead
  1112.                 jmp     save_ems_er             ; No, not enough EMS
  1113.  
  1114. ems_alloc_ok:   mov     wptr cs:handle, dx      ; Returned handle in DX
  1115.  
  1116.                 mov     ah, 47h                 ; Save EMS page map first
  1117.                 int     67h
  1118.                 or      ah, ah
  1119.                 jnz     save_ems_fail2          ;  Free allocated handle
  1120.  
  1121.                 mov     wptr next_page, 0       ; Next page to use is 0
  1122.                 mov     ax, wptr start_seg
  1123.                 mov     ds, ax                  ; DS:0 = address to write from
  1124.  
  1125.                 assume  ds:nothing              ; Tell MASM that DS doesn't point to our variables
  1126.  
  1127.                 mov     ax, wptr cs:ems_seg     ; ES:0 = EMS page 0
  1128.                 mov     es, ax
  1129.  
  1130. ems_map_next:   mov     ah, 44h                 ; EMM function 44h, map page
  1131.                 mov     bx, wptr cs:next_page   ; Next page to map
  1132.                 mov     al, 0                   ; Always use physical page 0
  1133.                 int     67h                     ; Tell EMM to map the page
  1134.                 or      ah, ah
  1135.                 jz      save_ems2               ; ah = 0, skip ahead
  1136.  
  1137. save_ems_fail:  mov     ah, 48h                 ; Restore page map first
  1138.                 int     67h
  1139. save_ems_fail2: mov     ah, 45h                 ; And deallocate my handle (in dx)
  1140.                 int     67h
  1141.                 jmp     short save_ems_er       ; Signal error
  1142.  
  1143. save_ems2:      cmp     wptr cs:paras_left, 400h    ; Less than 16K left?
  1144.                 jb      last_ems_write              ; Yes, do last EMS write
  1145.                 sub     wptr cs:paras_left, 400h    ; 16K less to save
  1146.                 xor     si, si
  1147.                 xor     di, di
  1148.                 mov     cx, 2000h               ; Write 8K words
  1149.                 rep     movsw                   ; Move the data to EMS
  1150.  
  1151.                 mov     ax, ds                  ; Move source pointer (DS:0)
  1152.                 add     ax, 400h                ;  up by the 16K we have
  1153.                 mov     ds, ax                  ;  just written
  1154.                 inc     wptr cs:next_page
  1155.                 jmp     ems_map_next
  1156.  
  1157. last_ems_write: mov     ax, wptr cs:paras_left  ; Paragraphs left to save
  1158.                 mov     cl, 4                   ; Convert to bytes
  1159.                 shl     ax, cl
  1160.                 mov     cx, ax                  ; CX = remaining bytes to save
  1161.                 xor     si, si
  1162.                 xor     di, di
  1163.                 rep     movsb                   ; Write the remaining bytes
  1164.  
  1165. save_ems_ok:    mov     dx, wptr cs:handle
  1166.                 mov     ah, 48h                 ; Restore page map first
  1167.                 int     67h
  1168.                 mov     wptr cs:restore_proc, offset @code:restore_ems
  1169.                 clc
  1170.                 jmp     short save_ems_ret
  1171.  
  1172. save_ems_er:    stc
  1173.  
  1174. save_ems_ret:   pop     ds
  1175.                 ret
  1176. save_ems        endp
  1177. ENDIF
  1178. ; *****************************************************************************
  1179.  
  1180.  
  1181. ; *****************************************************************************
  1182. ; save_disk     Attempts to save program to DOS disk file
  1183. ;
  1184. ; Entry:        DS points to our variables
  1185. ;
  1186. ; Return:       Carry set on error, carry clear on success
  1187. ;               If successful, updates restore_proc with the address of
  1188. ;               the disk restore routine
  1189. ; *****************************************************************************
  1190. IFDEF USE_DISK
  1191. save_disk       proc    near
  1192.  
  1193.                 assume  ds:@code                ; Tell MASM DS points to our variables
  1194.  
  1195. creat_file:     mov     dx, offset @code:fname  ; DS:DX -> file name
  1196.                 mov     ah, 3ch                 ; Create/truncate file
  1197.                 mov     cx, 02h                 ; Create a hidden file
  1198.                 int     21h                     ; Call DOS
  1199.                 jc      save_disk_er            ; Carry set, couldn't create file
  1200.  
  1201. creat_ok:       mov     wptr handle, ax         ; Save handle returned by DOS
  1202.                 mov     bx, ax
  1203.                 mov     ax, wptr start_seg      ; Released segment address
  1204.                 mov     ds, ax
  1205.  
  1206.                 assume  ds:nothing              ; Tell MASM DS doesn't point to our variables
  1207.  
  1208.                 mov     ax, wptr cs:old_size    ; Segment of released memory
  1209.                 sub     ax, wptr cs:new_size    ; AX = # paragraphs to save
  1210.                 mov     wptr cs:saved_paras, ax ; Save
  1211.                 mov     wptr cs:paras_left, ax  ; Used to count paras written
  1212.  
  1213. disk_write_32k: cmp     ax, 0800h               ; paras_left less than 32K?
  1214.                 jb      finish_disk_write       ; Yes, exit
  1215.                 sub     wptr cs:paras_left, 800h; We will write 32K bytes now
  1216.  
  1217.                 mov     ah, 40h                 ; DOS function to write to file
  1218.                 mov     cx, 8000h               ; Write 32K bytes
  1219.                 xor     dx, dx                  ; DS:DX is buffer to write
  1220.                 int     21h                     ; Write data to file
  1221.                 jnc     disk_write_ok           ; This write was successful
  1222.  
  1223. disk_write_er:  mov     ah, 3eh                 ; Close file first
  1224.                 int     21h
  1225.                 jmp     short save_disk_er      ; Return error
  1226.  
  1227. disk_write_ok:  mov     ax, ds                  ; Move write pointer in memory
  1228.                 add     ax, 800h                ; We just wrote 1K paragraphs
  1229.                 mov     ds, ax
  1230.                 mov     ax, wptr cs:paras_left  ; AX checked above
  1231.                 jmp     short disk_write_32k    ; Loop on next 32K
  1232.  
  1233. finish_disk_write:
  1234.                 mov     cl, 4                   ; AX = # paragraphs left to write
  1235.                 shl     ax, cl                  ; Paragraphs to bytes
  1236.                 mov     cx, ax
  1237.                 mov     ah, 40h                 ; 40h = write to file
  1238.                 xor     dx, dx                  ; DS:DX = buffer
  1239.                 int     21h                     ; Call DOS
  1240.                 jc      disk_write_er           ; Carry set, error (close file first)
  1241.  
  1242. close_file:     mov     ah, 3eh                 ; 3eh = close file
  1243.                 int     21h
  1244.  
  1245. save_disk_ok:   mov     wptr restore_proc, offset @code:restore_disk
  1246.                 clc
  1247.                 jmp     short save_disk_ret
  1248.  
  1249. save_disk_er:   stc
  1250.  
  1251. save_disk_ret:  ret
  1252. save_disk       endp
  1253. ENDIF
  1254. ; *****************************************************************************
  1255.  
  1256.  
  1257.  
  1258. END
  1259.  
  1260.  
  1261.  
  1262.