home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 September / Simtel20_Sept92.cdr / msdos / sysutl / swap.arc / SWAP.ASM < prev    next >
Assembly Source File  |  1989-03-24  |  31KB  |  1,104 lines

  1. ; SWAP - (c) copyright 1988 Nico Mak and Mansfield Software Group
  2. ; All rights reserved
  3. ;
  4. ; To rebuild SWAP.COM use the following instructions:
  5. ;   masm swap;
  6. ;   link swap;
  7. ;   exe2bin swap swap.com
  8. ;
  9. cr    equ    13
  10. lf    equ    10
  11.  
  12. error    macro    message            ;; macro to display an error message
  13.     local    around, msg, msglen    ;; and to jump to error_exit routine
  14.     jmp    around
  15. msg    db    &message,cr,lf        ;; define error message
  16. msglen    equ    $-msg
  17. around:
  18.     mov    dx, offset msg        ;; get address of error message
  19.     mov    cx, msglen        ;; get length
  20.     jmp    error_exit        ;; jump to error exit routine
  21.     endm
  22.  
  23. ; --------------------------------------------------
  24. ; the following is copied over the swappee
  25. ; --------------------------------------------------
  26. code    segment    'code'
  27.     assume    cs:code,ds:code
  28.     org    100h            ; origin past psp
  29.  
  30. swap    proc    near
  31.     jmp    begin
  32.     
  33.     db    20 dup('STACK')
  34. stack    equ    $
  35.  
  36. flag        db    0        ; option flag
  37. flag_copy    equ    80h        ; copy stdout to 'con'
  38. flag_force    equ    40h        ; swap even if vector points to swappee
  39. flag_quiet    equ    20h        ; don't print hello message
  40. flag_disk    equ    10h        ; swap to disk
  41.  
  42. emsg_ems    db    "SWAP EMS Error"
  43. ems_rc        db    'xx function '
  44. ems_func    db    'xx',cr,lf
  45. emsg_ems_len    equ    $-emsg_ems
  46.  
  47. my_psp        dw    ?        ; segment of SWAP's original psp
  48. swappee_psp    dw    ?        ; segment of swappee's psp
  49.  
  50. ; variables used when swapping to expanded memory
  51. ems_handle    dw    ?        ; emm handle
  52. swap_pages    dw    ?        ; number of pages for ems_handle
  53. ems_frame    dw    ?        ; ems page frame
  54. last_ems_func    db    ?        ; last emm function issued by SWAP
  55.  
  56. ; variables used when swapping to disk
  57. swap_fid    db    "c:\swap.dat",0    ; asciiz string to open swap file
  58. swap_handle    dw    ?        ; handle while swap file is open
  59.  
  60. ; fields for int 21 function 4b (exec)
  61. commandcom_addr    dd    ?        ; address of program to exec (command.com)
  62. exec_sp        dw    ?        ; save area for reg clobbered by exec function
  63. command_line    db    ?,"/c"        ; command line for command.com
  64. command_text    db    130 dup (0)    ; command line continued
  65. blank_fcb    db    36 dup (0)    ; dummy fcb for exec function
  66. exec_parm_block    equ    $        ; exec parameter block
  67. exec_env    dw    ?        ; segment address of environment
  68. cmdline_addr    dw    offset command_line    ; address of command_line
  69. cmdline_seg    dw    ?
  70.         dw    offset blank_fcb    ; address of fcb
  71. fcb1_seg    dw    ?
  72.         dw    offset blank_fcb    ; address of fcb
  73. fcb2_seg    dw    ?
  74.  
  75. ; fields used by int 21 handler
  76. save_pid    dw    ?        ; pid at time int 21 handler received control
  77. int21_vector    dd    ?        ; original int 21 vector owner
  78. con        db    "con",0        ; asciiz string to open console
  79. handle        dw    ?        ; handle while "con" is open
  80. char_buf    db    ?        ; buffer for int 21 function 2 and 6 handlers
  81. save_ax        dw    ?        ; register save areas for int 21 handler
  82. save_bx        dw    ?
  83. save_cx        dw    ?
  84. save_dx        dw    ?
  85. save_ds        dw    ?
  86.  
  87. ; --------------------------------------------------
  88. ; run_command - the following code is copied over the swappee
  89. ; --------------------------------------------------
  90. run_command:
  91.     call    copy_start        ; start copying stdout to the console
  92.     call    exec_user_cmd        ; execute the user's command
  93.     call    copy_stop        ; stopy copying stdout to the console
  94.     call    swap_in            ; swap in all but first 16k
  95.     retf
  96.     
  97. ; --------------------------------------------------
  98. ; subroutines for run_command follow
  99. ; --------------------------------------------------
  100.  
  101. ; -----
  102. ; copy_start - if -c option specified, open handle for console and hook int 21
  103. ; -----
  104. copy_start:
  105.     test    flag,flag_copy        ; will we copy stdout to display?
  106.     jz    copy_start_ret        ; no
  107. ; ----- open a handle that points to "con"
  108.     mov    dx, offset con        ; address of asciiz file name
  109.     mov    ax, 3d01h        ; open handle for writing
  110.     int    21h
  111.     mov    handle, ax        ; remember handle
  112.     jnc    open_worked        ; did open succeed?
  113.     and    flag, 255-flag_copy    ; no, then we won't copy stdout...
  114.     jmp    short copy_start_ret    ; ... and won't hook int 21
  115. open_worked:
  116. ; ----- hook int 21 vector
  117.     mov    ax, 3521h        ; get interrupt vector
  118.     int    21h
  119.     mov    word ptr int21_vector,bx    ; save offset
  120.     mov    word ptr int21_vector[2],es    ; save segment
  121.     mov    dx, offset int21_handler    ; address of out int 21
  122.     mov    ax, 2521h            ; set interrupt vector
  123.     int    21h
  124. ; ----- ensure that standard error is redirected and copied
  125.     mov    al, cs:[19h]        ; get stdout file handle array entry
  126.     mov    cs:[1ah], al        ; use stdout entry for stderr entry
  127. copy_start_ret:
  128.     ret
  129.  
  130. ; -----
  131. ; exec_user_cmd - set up and issue the int 21 function 4b (exec)
  132. ; -----
  133. exec_user_cmd:
  134.     mov    cs:exec_sp,sp        ; save register
  135.     mov    ax, cs:[2ch]        ; pass address of our environment
  136.     mov    exec_env, ax        ; to exec function
  137.     mov    word ptr cmdline_seg, ds    ; address of command line
  138.     mov    word ptr fcb1_seg, ds        ; fill in segments for fcb's
  139.     mov    word ptr fcb2_seg, ds
  140.     push    cs
  141.     pop    es
  142.     mov    bx, offset exec_parm_block    ; bx = exec parameter block
  143.     lds    dx, commandcom_addr    ; es:bx = asciiz string of command.com
  144.     mov    ax, 4b00h        ; load and execute a program
  145.     int    21h
  146.     mov    ds, cs:swappee_psp    ; restore ds addressability
  147.     cli                ; turn off interrupts
  148.     mov    ss, swappee_psp
  149.     mov    sp, exec_sp
  150.     sti                ; allow interrupts
  151.     ret
  152.  
  153. ; -----
  154. ; copy_stop - close handle and restore original int 21 vector
  155. ; -----
  156. copy_stop:
  157.     test    cs:flag, flag_copy    ; did we copy stdout to display?
  158.     jz    copy_stop_ret        ; no
  159. ; ----- close handle for console
  160.     mov    bx, handle        ; handle for 'con'
  161.     mov    ah, 3eh            ; dos 'close handle' function
  162.     int    21h
  163. ; ----- restore original int 21 vector
  164.     push    ds            ; ds gets clobbered, so save it
  165.     lds    dx, int21_vector    ; get address of old vector
  166.     mov    ax, 2521h        ; set interrupt vector
  167.     int    21h
  168.     pop    ds
  169. copy_stop_ret:
  170.     ret
  171.     
  172. ; -----
  173. ; swap_in - swap in all but the first page of swappee
  174. ; -----
  175. swap_in:
  176.     mov    bx, cs            ; bx = swappee's psp
  177.     add    bx, 3ffh        ; first page to swap in over
  178.     mov    es, bx
  179.     test    flag, flag_disk
  180.     jnz    swap_in_disk
  181. ; ----- swap in from expanded memory
  182.     mov    cx, 1            ; start with second logical page
  183.     cld
  184. swap_in_page:                ; loop to swap 16K
  185.     mov    bx, cx            ; logical page
  186.     call    map_page
  187.     push    ds            ; save ds
  188.     mov    ds, ems_frame        ; ds = where to swap from
  189.     mov    si, 0
  190.     mov    di, 0
  191.     push    cx
  192.     mov    cx, 4000h        ; copy 16K
  193.     rep    movsb
  194.     pop    cx
  195.     pop    ds            ; restore ds
  196.     mov    bx, es
  197.     add    bx, 400h
  198.     mov    es, bx            ; es = next place to swap to
  199.     inc    cx
  200.     cmp    cx, swap_pages
  201.     jl    swap_in_page
  202.     ret
  203. ; ----- swap in from disk
  204. swap_in_disk:                ; es = first page to swap over
  205.     call    open_swap_file        ; open the swap file
  206.     mov    cx, 0            ; high order part of offset
  207.     mov    dx, 4000h        ; file pointer to start + 16K
  208.     mov    bx, swap_handle        ; get swap file handle
  209.     mov    ax, 4201h        ; code to lseek from current position
  210.     int    21h            ; let dos lseep to 2nd page
  211.     jnc    lseek_done
  212.     error    "LSEEK on swap file failed"
  213. lseek_done:
  214.     mov    cx, 1            ; start with second logical page
  215. swap_in_disk_page:            ; loop to swap 16K
  216.     call    read_swap_file        ; read 16K from swap file
  217.     mov    bx, es
  218.     add    bx, 400h
  219.     mov    es, bx            ; es = next place to swap to
  220.     inc    cx
  221.     cmp    cx, swap_pages
  222.     jl    swap_in_disk_page
  223.     call    close_swap_file
  224.     ret
  225.  
  226. ; --------------------------------------------------
  227. ; int_21_handler and its subroutines
  228. ; --------------------------------------------------
  229.     assume    ds:nothing
  230. int21_handler:
  231. ; ----- decide whether we will front-end this int 21 function
  232.     cmp    ah, 02h
  233.     je    func02
  234.     cmp    ah, 06h
  235.     je    func06
  236.     cmp    ah, 09h
  237.     je    func09
  238.     cmp    ah, 40h
  239.     je    func40
  240. ; ----- call the original int 21 vector owner
  241. do_real_thing:
  242.     jmp    cs:int21_vector
  243.  
  244. ; -----
  245. ; handle int 21 function 9 (print $ delimited string)
  246. ; -----
  247. func09:
  248.     call    front_start
  249.     push    di
  250.     push    es
  251.     mov    di, dx
  252.     mov    es, save_ds        ; address of string at es:di
  253.     mov    al, '$'            ; scan for $
  254.     mov    cx, -1            ; max bytes to scan
  255.     cld                ; scan in forward direction
  256.     repne    scasb            ; find the $
  257.     sub    di, dx
  258.     mov    cx, di            ; length to write
  259.     dec    cx            ; don't write the $
  260.     pop    es
  261.     pop    di
  262.     mov    ds, save_ds        ; ds addressability is blown
  263.     call    write_to_con        ; write buffer to display
  264.     mov    ds, cs:swappee_psp    ; restore ds addressability
  265.     jmp    front_done
  266.  
  267. ; -----
  268. ; handle int 21 function 06 (direct console i/o)
  269. ; -----
  270. func06:
  271.     cmp    dl, 0ffh        ; get input characters?
  272.     je    do_real_thing        ; yes, then there is no output to copy
  273.  
  274. ; -----
  275. ; handle int 21 function 02 (display character in dl register)
  276. ; -----
  277. func02:
  278.     call    front_start
  279.     mov    char_buf, dl        ; put character in write buffer
  280.     mov    dx, offset char_buf    ; get address of buffer
  281.     mov    cx, 1            ; get length
  282.     call    write_to_con        ; write buffer to display
  283.     jmp    front_done
  284.  
  285. ; -----
  286. ; handle int 21 function 40 (write to file handle)
  287. ; -----
  288. func40:
  289.     call    front_start
  290. ; ----- verify that file handle array entry for this handle == stdout entry
  291.     push    di
  292.     push    es
  293.     mov    bx, save_bx        ; get caller's handle
  294.     mov    es, save_pid        ; psp for process issuing int 21
  295.     les    di, es:34h        ; address of caller's file handle array
  296.     mov    ah, es:[di+1]        ; file handle array entry for stdout
  297.     cmp    ah, es:[di+bx]        ; does handle entry == stdout entry?
  298.     pop    es
  299.     pop    di
  300.     jne    func40_done        ; no, don't copy to console
  301. ; ----- call real int 21 handler with handle opened for 'con'
  302.     mov    ds, save_ds        ; ds addressability blown
  303.     call    write_to_con        ; write buffer to display
  304.     mov    ds, cs:swappee_psp    ; restore ds addressability
  305. func40_done:
  306.     jmp    front_done
  307.  
  308. ; -----
  309. ; front_start - start front_ending int 21
  310. ; -----
  311. front_start:
  312.     assume    ds:nothing
  313. ; ----- establish ds addressability and save registers
  314.     mov    save_ds, ds
  315.     mov    ds, cs:swappee_psp    ; establish ds addressability
  316.     assume    ds:code            ; tell assembler
  317.     mov    save_ax, ax        ; save registers
  318.     mov    save_bx, bx
  319.     mov    save_cx, cx
  320.     mov    save_dx, dx
  321. ; ----- remember caller's pid
  322.     mov    ah, 51h            ; 51h = get pid
  323.     int    21h
  324.     mov    save_pid, bx        ; remember pid
  325. ; ----- set pid so our file handle array is used
  326.     mov    bx, cs            ; pid = my cs register
  327.     mov    ah, 50h            ; 50h = set pid
  328.     int    21h
  329.     ret
  330.  
  331. ; -----
  332. ; write_to_con - call original int 21 handler to write buffer to display
  333. ; -----
  334. write_to_con:
  335.     assume    ds:nothing
  336.     mov    bx, cs:handle        ; handle opened for 'con'
  337.     mov    ah, 40h            ; 40h = write to handle
  338.     pushf
  339.     call    dword ptr cs:int21_vector    ; call dos
  340.     ret
  341.  
  342. ; -----
  343. ; front_done - almost done front_ending int 21
  344. ; -----
  345. front_done:
  346.     assume    ds:code
  347. ; ----- restore caller's pid
  348.     mov    bx, save_pid        ; get pid of process that issued int 21
  349.     mov    ah, 50h            ; 50 = set pid
  350.     int    21h
  351. ; ----- restore registers & go jump to previous int 21 handler
  352.     mov    ax, save_ax
  353.     mov    bx, save_bx
  354.     mov    cx, save_cx
  355.     mov    dx, save_dx
  356.     mov    ds, save_ds        ; ds addressability blown
  357.     jmp    do_real_thing
  358.     
  359. ; --------------------------------------------------
  360. ; the following routines are used by both parts of the program
  361. ; --------------------------------------------------
  362.  
  363. ; -----
  364. ; emm - remember emm function in case of error and issue int 67
  365. ; -----
  366. emm:
  367.     mov    last_ems_func, ah
  368.     int    67h            ; call expanded memory manager
  369.     or    ah, ah
  370.     ret
  371.     
  372. ; -----
  373. ; ems_error - handle ems errors
  374. ; -----
  375. ems_error:
  376.     mov    di, offset ems_rc
  377.     call    hex_to_ascii        ; make ems error code printable
  378.     mov    ah, last_ems_func
  379.     mov    di, offset ems_func
  380.     call    hex_to_ascii        ; make last ems function printable
  381.     mov    cx, emsg_ems_len
  382.     mov    dx, offset emsg_ems
  383.     jmp    error_exit        ; go display error message and exit
  384.     
  385. ; -----
  386. ; hex_to_ascii - convert ah register to ascii hexidecimal at ds:di
  387. ; -----
  388. hex_to_ascii:
  389.     mov    dl, ah
  390.     mov    cx, 2
  391. hex_char:
  392.     push    cx
  393.     mov    cl, 4
  394.     rol    dl, cl
  395.     mov    al, dl
  396.     and    al, 00fh
  397.     daa
  398.     add    al, 0f0h
  399.     adc    al, 040h
  400.     mov    [di], al
  401.     inc    di
  402.     pop    cx
  403.     loop    hex_char
  404.     ret
  405.     
  406. ; -----
  407. ; error_exit - display error message and exit
  408. ; ds:di point to error message, cx has the length
  409. ; -----
  410. error_exit:
  411.     push    cx
  412.     push    dx
  413.     mov    dx, offset emsg_start
  414.     mov    cx, emsg_start_len
  415.     mov    bx, 2            ; handle for stderr
  416.     mov    ah, 40h            ; 40h = dos handle write
  417.     int    21h
  418.     pop    dx
  419.     pop    cx
  420.     mov    bx, 2
  421.     mov    ah, 40h
  422.     int    21h
  423.     jmp    return
  424.     
  425. ; -----
  426. ; routines to open, read from, and close the swap file
  427. ; -----
  428. open_swap_file:
  429.     mov    dx, offset swap_fid    ; address of fileid to open
  430.     mov    ax, 3d00h        ; open file in read-only mode
  431.     int    21h
  432.     jnc    open_exit
  433.     error    "Could not open swap file"
  434. open_exit:
  435.     mov    swap_handle, ax        ; save swap file handle
  436.     ret
  437.     
  438. ; read_swap_file -- read 16K from swap file to address in es:0
  439. ; saves cs
  440. read_swap_file:
  441.     push    cx
  442.     mov    bx, swap_handle        ; get swap file handle
  443.     mov    cx, 4000h        ; read 16K
  444.     mov    dx, 0            ; buffer offset
  445.     push    ds
  446.     push    es
  447.     pop    ds            ; buffer segment
  448.     mov    ah, 3fh            ; 3f = dos read (handle)
  449.     int    21h
  450.     pop    ds
  451.     pop    cx
  452.     jnc    read_exit
  453.     error    "Error reading swap file"
  454. read_exit:
  455.     ret
  456.     
  457. close_swap_file:
  458.     mov    bx, swap_handle        ; get swap file handle
  459.     mov    ah, 3eh            ; 3e = dos close file
  460.     int    21h
  461.     ret
  462.  
  463. ; -----
  464. ; return - return to DOS
  465. ; -----
  466. return:
  467.     mov    ax, 4c00h        ; dos terminate function
  468.     int    21h
  469.     
  470. ; -----
  471. ; map_page - map EMS logical page in bx into physical page 0
  472. ; -----
  473. map_page:
  474.     mov    al, 0            ; physical page
  475.     mov    dx, ems_handle        ; ems handle
  476.     mov    ah, 44h            ; map handle page
  477.     call    emm
  478.     jz    map_page_exit
  479.     jmp    ems_error
  480. map_page_exit:
  481.     ret
  482.  
  483. lowend    equ    $            ; end of code copied to lower memory
  484.  
  485. ; --------------------------------------------------
  486. ; the following is *not* copied on top of the swappee
  487. ; --------------------------------------------------
  488.  
  489. hello        db    "SWAP Version 1.0 (c) Copyright 1988 Nico Mak"
  490.         db    " and Mansfield Software Group", cr, lf
  491. hello_len    equ    $-hello
  492. emsg_start    db    "SWAP Error: "
  493. emsg_start_len    equ    $-emsg_start
  494. run_addr    dw    offset run_command    ; offset of run_command
  495. run_seg        dw    ?            ; segment of run_command
  496. swappee_mcb    dw    ?        ; segment of mcb for swappee psp
  497. swappee_end    dw    ?        ; segment of mcb after swappee
  498. my_mcb_size    dw    ?
  499. next_mcb    dw    ?        ; address of next mcb
  500. next_code    db    ?        ; M/Z code in next mcb
  501. next_owner    dw    ?        ; etc
  502. next_size    dw    ?
  503. ems_device_name    db    "EMMXXXX0",0    ; expanded memory manager signature
  504. comspec        db    'COMSPEC='    ; environment variable name
  505. comspec_len    equ    $-comspec
  506.  
  507. mcb_info    struc            ; important memory control block info
  508. addr        dw    ?        ; address of mcb
  509. owner        dw    ?        ; psp of owner
  510. len        dw    ?        ; length of mcb
  511. mcb_info    ends
  512.  
  513. max_mcbs    equ    100
  514. mcbs        mcb_info    <>
  515. mcb_length    equ    $-mcbs
  516.         db    (max_mcbs-1)*mcb_length dup (?)
  517.  
  518. ; --------------------------------------------------
  519. ; mainline code run from system prompt
  520. ; --------------------------------------------------
  521. begin:
  522.     assume    ds:code,es:code
  523.     mov    sp, offset stack    ; set up new stack pointer
  524.     call    process_cmdline        ; check options, set up 'exec' cmdline
  525.     call    say_hello        ; print copyright message
  526.     call    check_dos_version    ; ensure we have DOS 3.0 or later
  527.     call    find_comspec        ; find 'comspec=' in environment
  528.     call    shrink_ourself        ; free unneeded memory
  529.     call    get_mcb_info        ; get relavent info about mcbs
  530.     call    check_mcbs        ; ensure mcbs are in expected order
  531.     call    vector_check        ; ensure swappee has not hooked vectors
  532.     call    figure_pages        ; determine how many ems pages we need
  533.     call    init_ems        ; ems initialization, allocation, etc
  534.     call    swap_out        ; swap out swappee, command.com, and us
  535.     call    muck_with_memory    ; copy swap over swappee & set up mcbs
  536.     mov    ss, swappee_psp        ; switch to stack in low memory
  537.     call    run_user_command    ; go call run_command rtn in low memory
  538.     mov    ss, my_psp        ; switch back to original stack
  539.     call    swap_first        ; swap in first 16K
  540.     call    clean_up        ; restore original environment
  541. exit:
  542.     jmp    return            ; leave SWAP
  543.  
  544. ; --------------------------------------------------
  545. ; subroutines for code that is not copied to low memory follow
  546. ; --------------------------------------------------
  547.  
  548. ; -----
  549. ; process_cmdline - process options, set up command line for exec function
  550. ; -----
  551. process_cmdline:
  552.     mov    bx, 80h
  553. option_check:
  554.     inc    bx
  555.     cmp    byte ptr [bx], cr    ; carriage return?
  556.     jne    option_check2        ; no
  557.     error    "No command to execute"
  558. option_check2:
  559.     cmp    byte ptr [bx], ' '    ; blank?
  560.     je    option_check
  561.     cmp    byte ptr [bx], '/'    ; option signal?
  562.     je    got_option
  563.     cmp    byte ptr [bx], '-'    ; option signal?
  564.     jne    copy_command_line
  565. got_option:
  566.     mov    byte ptr [bx], ' '    ; blank out character on command line
  567.     inc    bx            ; point at option
  568.     mov    al, byte ptr [bx]    ; get option
  569.     mov    byte ptr [bx], ' '    ; blank out character on command line
  570.     or    al, ' '            ; convert option to lower case
  571.     cmp    al, 'c'            ; option 'c'?
  572.     jne    check_option_q
  573.     or    flag, flag_copy
  574.     jmp    option_check
  575. check_option_q:
  576.     cmp    al, 'q'            ; option 'q'?
  577.     jne    check_option_f
  578.     or    flag, flag_quiet
  579.     jmp    option_check
  580. check_option_f:
  581.     cmp    al, 'f'            ; option 'f'?
  582.     jne    check_option_d
  583.     or    flag, flag_force
  584.     jmp    option_check
  585. check_option_d:
  586.     cmp    al, 'd'            ; option 'd'?
  587.     jne    bad_option
  588.     or    flag, flag_disk
  589.     jmp    option_check
  590. bad_option:
  591.     error    "Invalid option"
  592. ; ----- copy remainder of our command line to commmand line for command.com
  593. copy_command_line:
  594.     mov    cl, ds:[80h]        ; length of my command line
  595.     inc    cl            ; add one for cr
  596.     mov    si, 81h            ; address of my command line
  597.     mov    di, offset command_text    ; address of where to put it
  598.     xor    ch, ch            ; zero uninitialized part of count
  599.     cld                ; scan in forward direction
  600.     rep    movsb            ; copy command line
  601. ; ----- set length of new command line
  602.     mov    cl, ds:[80h]        ; length of my command line
  603.     add    cl, 2            ; add 2 for "/c"
  604.     mov    command_line,cl        ; save new length
  605.     ret
  606.  
  607. ; -----
  608. ; say_hello - print hello message
  609. ; -----
  610. say_hello:
  611.     test    flag, flag_quiet    ; was -q option used?
  612.     jnz    say_hello_exit        ; yes, skip this
  613.     mov    dx, offset hello    ; get address of message
  614.     mov    cx, hello_len        ; get length of address
  615.     mov    bx, 2            ; handle for stderr
  616.     mov    ah, 40h            ; 40h = dos write to handle
  617.     int    21h
  618. say_hello_exit:
  619.     ret
  620.  
  621. ; -----
  622. ; check_dos_version - be sure this is dos 3.0 or higher
  623. ; -----
  624. check_dos_version:
  625.     mov    ah, 30h            ; 30h = dos get version
  626.     int    21h
  627.     cmp    al, 3            ; ok?
  628.     jae    dos_version_ret
  629.     error    "DOS version must be 3.0 or higher"
  630. dos_version_ret:
  631.     ret
  632.  
  633. ; -----
  634. ; find_comspec - find fileid for exec function
  635. ; -----
  636. find_comspec:
  637.     mov    es, es:2ch        ; es = environment segment
  638.     xor    di, di            ; point to start of env in es:di
  639.     cld                ; scan in forward direction
  640. ; ----- loop through environment strings one by one, beginning here
  641. find_string:
  642.     test    byte ptr es:[di], -1    ; end of environment?
  643.     jnz    check_string        ; nope, continue
  644.     error    "Could not find COMSPEC= in environment"    ; very unlikely
  645. ; ----- compare current env string to 'COMSPEC='
  646. check_string:
  647.     mov    si, offset comspec    ; point to 'COMSPEC=' string
  648.     mov    bx, di            ; save ptr to start of env string
  649.     mov    cx, comspec_len        ; length of 'COMSPEC='
  650.     repe    cmpsb            ; compare
  651.     je    found_comspec        ; found it!
  652.     mov    di, bx            ; restore ptr to start of env string
  653.     xor    al, al            ; scan for end of string
  654.     mov    cx, -1
  655.     repne    scasb
  656.     jmp    find_string        ; go back for next string
  657. ; ----- found COMSPEC=
  658. found_comspec:
  659.     mov    word ptr commandcom_addr[0], di    ; remember address of ...
  660.     mov    word ptr commandcom_addr[2], es ; ... asciiz "command.com"
  661.     ret
  662.  
  663. ; -----
  664. ; shrink_ourself - release unneeded memory
  665. ; -----
  666. shrink_ourself:
  667.     push    cs
  668.     pop    es            ; address of start of SWAP memory
  669.     mov    bx, offset endcode+15    ; address of end of SWAP code
  670.     mov    cl, 4
  671.     shr    bx, cl            ; convert to paragraphs
  672.     mov    ah, 4ah            ; 4a = dos set block
  673.     int    21h
  674.     ret
  675.  
  676. ; -----
  677. ; get_mcb_info - get relavent info from mcb chain
  678. ; -----
  679. get_mcb_info:
  680.     mov    my_psp, cs        ; remember address of our psp
  681.     mov    ah, 52h            ; undocumented function
  682.     int    21h            ; get base of memory chain
  683.     mov    es, es:[bx]-2        ; this is it
  684.     mov    bx, offset mcbs
  685.     mov    dx, 0            ; count of mcbs
  686. mem_loop:
  687.     mov    [bx].addr, es
  688.     mov    cx, word ptr es:1    ; owner of mcb
  689.     mov    [bx].owner, cx
  690.     mov    cx, word ptr es:3    ; length of mcb
  691.     mov    [bx].len, cx
  692.     inc    dx            ; increment count of mcbs
  693.     cmp    dx, max_mcbs
  694.     jle    mem_loop1
  695.     error    "Over 100 Memory Control Blocks in system"
  696. mem_loop1:
  697.     cmp    byte ptr es:0,'Z'    ; last memory block?
  698.     jne    mem_next
  699.     error    "Could not find SWAP's PSP"
  700. mem_next:
  701.     mov    cx, es            ; copy seg addr of mcb
  702.     inc    cx            ; next paragraph
  703.     cmp    cx, my_psp        ; is this our psp?
  704.     je    found_our_psp        ; yes!
  705.     add    cx, [bx].len        ; add length of this mcb
  706.     mov    es, cx            ; this is next memory block
  707.     add    bx, mcb_length        ; where next mcb goes
  708.     jmp    mem_loop        ; proceed
  709. found_our_psp:
  710.     mov    dx, [bx].len
  711.     mov    my_mcb_size, dx        ; remember length of our mcb
  712.     add    cx, [bx].len        ; add length to memory
  713.     mov    next_mcb, cx        ; this is next memory block
  714. ; ----- remember information about the next mcb
  715.     mov    es, cx
  716.     mov    dl, es:0
  717.     mov    next_code, dl
  718.     mov    dx, es:1
  719.     mov    next_owner, dx
  720.     mov    dx, es:3
  721.     mov    next_size, dx
  722.     ret
  723.  
  724. ; -----
  725. ; check_mcbs - ensure mcbs are in expected order
  726. ; verify that our parent is command.com, find swappee psp, etc.
  727. ; -----
  728. check_mcbs:
  729.     mov    cx, cs:16h        ; our parent's address
  730.     mov    es, cx
  731.     mov    ax, es:16h        ; and our grandparent's address
  732.     cmp    ax, cx            ; better be equal
  733.     jne    unknown_parent
  734.     mov    ax, cs:10h        ; our ctrl-break handler
  735.     cmp    ax, cx            ; better equal our parent's address
  736.     je    skip_our_env
  737. unknown_parent:
  738.     error    "SWAP not directly run from COMMAND.COM"
  739. ; ----- back up to find swappee's mcb.  bx still points at entry for our mcb
  740. skip_our_env:
  741.     mov    cx, cs
  742.     call    prev_mcb
  743.     cmp    [bx].owner, cx        ; is this mcb our environment?
  744.     jne    skip_command
  745.     call    prev_mcb
  746. ; ----- back up over all mcb's owned by command.com (es = command.com psp)
  747. skip_command:
  748.     mov    cx, es            ; address of command.com psp
  749.     cmp    [bx].owner, cx        ; is this mcb owned by command.com?
  750.     je    command_loop        ; yes
  751.     error    "COMMAND.COM must immediately precede SWAP in memory"
  752. command_loop:
  753.     mov    dx, [bx].addr        ; remember address of mcb in case
  754.     mov    swappee_end, dx        ;   it is the one above swappee
  755.     call    prev_mcb        ; back up one mcb
  756.     cmp    [bx].owner, cx        ; is this mcb owned by command.com?
  757.     je    command_loop        ; yes, skip it
  758. ; ----- assume we have one of swappee's mcbs
  759. ;    back up over all it's mcb's till we reach psp
  760.     mov    cx, [bx].owner        ; cx = swappee's psp
  761. find_swappee_psp:
  762.     mov    dx, [bx].addr        ; address of this mcb
  763.     inc    dx            ; address of memory
  764.     cmp    dx, cx            ; is this swappee's psp?
  765.     je    found_swappee_psp    ; yes
  766.     call    prev_mcb        ; check previous psp
  767.     cmp    [bx].owner, cx        ; still owned by swappee?
  768.     je    find_swappee_psp    ; yes, continue
  769.     error    "Unexpected MCB while looking for PSP of swappee"
  770. ; ----- we've found swappee's psp - bx points to mcb entry for swappee
  771. found_swappee_psp:
  772.     mov    es, [bx].owner        ; es = swappee's psp
  773.     mov    swappee_psp, es        ; remember swappee's psp
  774.     cmp    word ptr es:2ch, 0    ; swappee must have an environment
  775.     jne    check_mcbs_ret
  776.     error    "Swappee does not have an environment"
  777. check_mcbs_ret:
  778.     ret
  779.  
  780. ; -----
  781. ; unless the -f option was specified, check whether vectors point at swappee
  782. ; Note:  only interrupts 1-79h (inclusive) are checked
  783. ; -----
  784. vector_check:
  785.     test    flag, flag_force
  786.     jnz    vector_check_ret
  787.     mov    cx, 0            ; start at the beginning
  788. next_vector:
  789.     inc    cx            ; next vector
  790.     cmp    cx, 80h            ; all done?
  791.     jae    vector_check_ret    ; yes, no vectors hooked
  792.     mov    ah, 35h            ; 35h = dos get vector
  793.     mov    al, cl            ; vector number
  794.     int    21h
  795.     mov    dx, es            ; get segment addr
  796.     push    cx
  797.     mov    cl, 4            ; shift count
  798.     add    bx, 15            ; round up
  799.     shr    bx, cl            ; divide offset by 16
  800.     pop    cx
  801.     add    dx, bx            ; compute segment
  802.     cmp    swappee_psp, dx        ; compare to start of swappee
  803.     jae    next_vector        ; no problem, keep looking
  804.     cmp    dx, swappee_end        ; compare to end of swappee
  805.     jae    next_vector        ; no problem either
  806.     error    "Swappee has hooked an interrupt vector"
  807. vector_check_ret:
  808.     ret
  809.  
  810. ; -----
  811. ; figure_pages - figure how many 16K pages of EMS we need
  812. ; -----
  813. figure_pages:
  814.     mov    cx, swappee_psp
  815.     dec    cx            ; cx = swappee's mcb
  816.     mov    swappee_mcb, cx        ; remember address of mcb
  817.     mov    dx, next_mcb        ; dx = mcb after SWAP.COM
  818.     sub    dx, cx            ; dx = difference in paragraphs
  819.     mov    cx, 10
  820.     shr    dx, cl            ; convert paragraphs to 16K pages
  821.     or    dx, dx
  822.     jnz    figure2
  823.     error    "Less than 16K to swap"
  824. figure2:
  825.     inc    dx
  826.     mov    swap_pages, dx
  827.     ret
  828.  
  829. ; -----
  830. ; init_ems - ensure EMS is up to par, allocate pages, and save page map
  831. ; -----
  832. init_ems:
  833.     test    flag, flag_disk
  834.     jz    find_emm
  835.     jmp    init_ems_exit
  836. ; ----- determine whether EMS is installed
  837. find_emm:
  838.     mov    ax, 3567h        ; code to get int 67 handler address
  839.     int    21h
  840.     mov    di, 0ah            ; offset to name string
  841.     mov    si, offset ems_device_name    ; correct ems name
  842.     mov    cx, 8            ; length of name
  843.     cld                ; scan in forward direction
  844.     repe    cmpsb            ; do the compare
  845.     jz    test_status        ; EMS not loaded
  846.     error    "Could not find Expanded Memory Manager"
  847. ; ----- test EMS status
  848. test_status:
  849.     mov    ah, 40h            ; code to test status
  850.     call    emm
  851.     jz    check_ems_version
  852.     jmp    ems_error
  853. ; ----- ensure that we have ems version 3.2 or later
  854. check_ems_version:
  855.     mov    ah, 46h            ; get version
  856.     call    emm
  857.     jz    got_ems_version
  858.     jmp    ems_error
  859. got_ems_version:
  860.     mov    al, 32h
  861.     jnb    get_page_frame
  862.     error    "Expanded Memory Manager version must be 3.2 or higher"
  863. ; ----- get page frame address
  864. get_page_frame:
  865.     mov    ah, 41h            ; code to get page frame addr
  866.     call    emm
  867.     mov    ems_frame, bx        ; where ems memory starts
  868.     jz    alloc_pages
  869.     jmp    ems_error
  870. ; ----- allocate ems pages
  871. alloc_pages:
  872.     mov    ah, 43h
  873.     mov    bx, swap_pages
  874.     call    emm
  875.     mov    ems_handle, dx
  876.     jz    save_page_map
  877.     error    "Not enough free expanded memory"
  878. ; ----- save ems page map
  879. save_page_map:
  880.     mov    ah, 47h            ; save page map
  881.     mov    dx, ems_handle
  882.     call    emm
  883.     jz    init_ems_exit
  884.     jmp    ems_error
  885. init_ems_exit:
  886.     ret
  887.  
  888. ; -----
  889. ; swap_out - swap out swappee, command.com, and ourself
  890. ; -----
  891. swap_out:
  892.     mov    es, swappee_mcb
  893.     test    flag, flag_disk        ; swap to disk?
  894.     jnz    swap_out_disk        ; yes
  895. ; ----- swap out to expanded memory
  896.     mov    cx, 0
  897.     cld
  898. swap_out_page:                ; loop to swap 16K
  899.     mov    bx, cx            ; logical page = loop count
  900.     call    map_page
  901.     mov    bx, ems_frame
  902.     assume    ds:nothing
  903.     push    es
  904.     pop    ds            ; ds = where to swap to
  905.     mov    es, bx            ; es = ems_frame
  906.     mov    si, 0
  907.     mov    di, 0
  908.     push    cx
  909.     mov    cx, 4000h        ; copy 16K
  910.     rep    movsb
  911.     pop    cx
  912.     mov    bx, ds            ; where to swap from
  913.     add    bx, 400h        ; add 16K
  914.     mov    es, bx            ; es = next place to swap from
  915.     push    cs
  916.     pop    ds
  917.     assume    ds:code
  918.     inc    cx
  919.     cmp    cx, swap_pages        ; done swapping?
  920.     jl    swap_out_page        ; no, swap the next page
  921.     ret
  922.  
  923. ; ----- swap out to disk
  924. swap_out_disk:                ; es = swappee's mcb
  925.     mov    cx, 0            ; attribute
  926.     mov    dx, offset swap_fid
  927.     mov    ah, 3ch            ; 3c = dos create file
  928.     int    21h
  929.     jnc    create_done
  930.     error    "Could not create swap file"
  931. create_done:
  932.     mov    swap_handle, ax
  933.     mov    cx, 0            ; number of pages swapped
  934. swap_out_disk_page:            ; loop to swap 16K
  935.     push    cx            ; remember number of pages swapped
  936.     mov    bx, swap_handle        ; handle to write to
  937.     mov    cx, 04000h        ; write 16K
  938.     xor    dx, dx            ; offset to write from
  939.     push    ds
  940.     push    es
  941.     pop    ds            ; segment to write from
  942.     mov    ah, 40h            ; 40h = dos write to handle
  943.     int    21h
  944.     pop    ds
  945.     jnc    write_worked1
  946.     error    "Error writing to swap file"
  947. write_worked1:
  948.     mov    bx, es            ; where to swap from
  949.     add    bx, 400h        ; add 16K
  950.     mov    es, bx            ; es = next place to swap from
  951.     pop    cx            ; remember number of pages swapped
  952.     inc    cx            ; now we've swapped one more
  953.     cmp    cx, swap_pages        ; done swapping?
  954.     jl    swap_out_disk_page    ; no, swap the next page
  955.     call    close_swap_file
  956.     ret
  957.  
  958. ; -----
  959. ; muck_with_memory - copy part of SWAP over swappee's psp, set up mcb's, etc
  960. ; -----
  961. muck_with_memory:
  962.     mov    es, swappee_psp
  963.  
  964. ; ----- copy code over swappee's psp
  965.     cld                ; copy in forward direction
  966.     mov    cx, offset lowend    ; length of code to copy
  967.     mov    si, 100h        ; start copying after psp
  968.     mov    di, 100h        ; where to copy
  969.     rep    movsb            ; copy code over swappee's psp
  970.  
  971. ; ----- copy our file handle array down to swappee's psp
  972.     mov    cx, 20            ; length of file handle table
  973.     mov    si, 18h            ; address of our file handle table
  974.     mov    di, 18h            ; where to put file handle table
  975.     rep    movsb            ; copy file handle table to swappee psp
  976.  
  977. ; ----- set the file handle array size and offset in swappee's psp
  978.     mov    word ptr es:32h, 20    ; length of file handle table
  979.     mov    word ptr es:34h, 18h    ; offset of file handle table
  980.     mov    word ptr es:36h, es    ; segment of file handle table
  981.  
  982. ; ----- now fix up the swappee's mcb (still had an M)
  983.     mov    es, swappee_mcb        ; address of swappee's mcb
  984.     mov    dx, offset lowend+15    ; offset to end of SWAP code
  985.     mov    cx, 4
  986.     shr    dx, cl            ; convert to paragraphs
  987.     mov    word ptr es:3, dx    ; put result in swappee's mcb
  988.  
  989. ; ----- find address of mcb for memory that was freed up
  990.     mov    bx, swappee_psp        ; address of swappee's psp
  991.     add    bx, dx            ; add paragraphs in swappee's mcb
  992.     mov    es, bx            ; this is where mcb for free mem goes
  993.  
  994. ; ----- fill in new mcb
  995.     mov    dx, next_mcb        ; address of mcb after original swap
  996.     sub    dx, bx            ; compute paragraphs of free space
  997.     add    dx, next_size        ; add paragraphs for next mcb
  998.     mov    word ptr es:3, dx    ; fill in size
  999.     mov    dl, next_code        ; get id from next mcb
  1000.     mov    byte ptr es:0, dl    ; copy id (M or Z)
  1001.     mov    word ptr es:1, 0    ; mark next block as free
  1002.     ret
  1003.  
  1004. ; -----
  1005. ; run_user_command - call run_command routine in low memory
  1006. ; -----
  1007. run_user_command:
  1008. ; ----- put swappee segment address into pointer to run_command
  1009.     mov    bx, swappee_psp
  1010.     mov    word ptr run_seg, bx    ; segment of swappee psp
  1011.  
  1012. ; ----- set pid to address of swappee psp
  1013.     mov    ah, 50h            ; 50h = dos set pid
  1014.     int    21h
  1015.  
  1016. ; ----- call run_command in low memory
  1017.     mov    ds, bx
  1018.     assume    ds:nothing
  1019.     call    dword ptr cs:run_addr    ; call run_command
  1020.     mov    ds, cs:my_psp
  1021.     assume    ds:code
  1022.  
  1023. ; ----- restore pid to SWAP's psp
  1024.     mov    bx, cs            ; pid = my cs register
  1025.     mov    ah, 50h            ; 50h = dos set pid
  1026.     int    21h
  1027.     ret
  1028.  
  1029. ; -----
  1030. ; swap_first - swap in first page that was swapped out
  1031. ; -----
  1032. swap_first:
  1033.     mov    es, swappee_mcb
  1034.     test    flag, flag_disk        ; swapping in from disk?
  1035.     jnz    swap_first_disk        ; yes
  1036.  
  1037. ; ----- swap in from expanded memory
  1038.     mov    bx, 0            ; logical page = 0
  1039.     call    map_page
  1040.     push    ds            ; save ds
  1041.     mov    ds, ems_frame        ; ds = where to swap from
  1042.     mov    si, 0
  1043.     mov    di, 0
  1044.     mov    cx, 4000h        ; copy 16K
  1045.     cld
  1046.     rep    movsb
  1047.     pop    ds            ; restore ds
  1048.     ret
  1049.  
  1050. ; ----- swap in from disk
  1051. swap_first_disk:
  1052.     call    open_swap_file
  1053.     call    read_swap_file
  1054.     call    close_swap_file
  1055.     ret
  1056.  
  1057. ; -----
  1058. ; clean_up - restore ems or delete swap file
  1059. ; -----
  1060. clean_up:
  1061.     test    flag, flag_disk
  1062.     jnz    clean_up_disk
  1063.  
  1064. ; ----- restore ems page map
  1065.     mov    ah, 48h            ; restore page map
  1066.     mov    dx, ems_handle
  1067.     call    emm
  1068.     jz    deallocate
  1069.     jmp    ems_error
  1070.  
  1071. ; ----- deallocate the ems pages
  1072. deallocate:
  1073.     mov    ah, 45h            ; deallocate pages
  1074.     mov    dx, ems_handle
  1075.     call    emm
  1076.     jz    clean_up_exit
  1077.     jmp    ems_error
  1078.  
  1079. ; ----- delete swap disk file
  1080. clean_up_disk:
  1081.     mov    dx, offset swap_fid    ; file handle for swap file
  1082.     mov    ah, 41h            ; code to delete a file
  1083.     int    21h
  1084. clean_up_exit:
  1085.     ret
  1086.  
  1087. ; -----
  1088. ; prev_mcb -back up one entry in table of MCBs
  1089. ; -----
  1090. prev_mcb:
  1091.     sub    bx, mcb_length
  1092.     cmp    bx, offset mcbs
  1093.     jae    prev_mcb_ret
  1094.     error    "Memory Control Blocks not in expected order"
  1095. prev_mcb_ret:
  1096.     ret
  1097.  
  1098. endcode    equ    $
  1099.     align    16
  1100.     db    16 dup (0)        ; so that at least one mcb follow swap
  1101. swap    endp
  1102. code    ends
  1103.     end    swap
  1104.