home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
sysutl
/
swap.arc
/
SWAP.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-03-24
|
31KB
|
1,104 lines
; SWAP - (c) copyright 1988 Nico Mak and Mansfield Software Group
; All rights reserved
;
; To rebuild SWAP.COM use the following instructions:
; masm swap;
; link swap;
; exe2bin swap swap.com
;
cr equ 13
lf equ 10
error macro message ;; macro to display an error message
local around, msg, msglen ;; and to jump to error_exit routine
jmp around
msg db &message,cr,lf ;; define error message
msglen equ $-msg
around:
mov dx, offset msg ;; get address of error message
mov cx, msglen ;; get length
jmp error_exit ;; jump to error exit routine
endm
; --------------------------------------------------
; the following is copied over the swappee
; --------------------------------------------------
code segment 'code'
assume cs:code,ds:code
org 100h ; origin past psp
swap proc near
jmp begin
db 20 dup('STACK')
stack equ $
flag db 0 ; option flag
flag_copy equ 80h ; copy stdout to 'con'
flag_force equ 40h ; swap even if vector points to swappee
flag_quiet equ 20h ; don't print hello message
flag_disk equ 10h ; swap to disk
emsg_ems db "SWAP EMS Error"
ems_rc db 'xx function '
ems_func db 'xx',cr,lf
emsg_ems_len equ $-emsg_ems
my_psp dw ? ; segment of SWAP's original psp
swappee_psp dw ? ; segment of swappee's psp
; variables used when swapping to expanded memory
ems_handle dw ? ; emm handle
swap_pages dw ? ; number of pages for ems_handle
ems_frame dw ? ; ems page frame
last_ems_func db ? ; last emm function issued by SWAP
; variables used when swapping to disk
swap_fid db "c:\swap.dat",0 ; asciiz string to open swap file
swap_handle dw ? ; handle while swap file is open
; fields for int 21 function 4b (exec)
commandcom_addr dd ? ; address of program to exec (command.com)
exec_sp dw ? ; save area for reg clobbered by exec function
command_line db ?,"/c" ; command line for command.com
command_text db 130 dup (0) ; command line continued
blank_fcb db 36 dup (0) ; dummy fcb for exec function
exec_parm_block equ $ ; exec parameter block
exec_env dw ? ; segment address of environment
cmdline_addr dw offset command_line ; address of command_line
cmdline_seg dw ?
dw offset blank_fcb ; address of fcb
fcb1_seg dw ?
dw offset blank_fcb ; address of fcb
fcb2_seg dw ?
; fields used by int 21 handler
save_pid dw ? ; pid at time int 21 handler received control
int21_vector dd ? ; original int 21 vector owner
con db "con",0 ; asciiz string to open console
handle dw ? ; handle while "con" is open
char_buf db ? ; buffer for int 21 function 2 and 6 handlers
save_ax dw ? ; register save areas for int 21 handler
save_bx dw ?
save_cx dw ?
save_dx dw ?
save_ds dw ?
; --------------------------------------------------
; run_command - the following code is copied over the swappee
; --------------------------------------------------
run_command:
call copy_start ; start copying stdout to the console
call exec_user_cmd ; execute the user's command
call copy_stop ; stopy copying stdout to the console
call swap_in ; swap in all but first 16k
retf
; --------------------------------------------------
; subroutines for run_command follow
; --------------------------------------------------
; -----
; copy_start - if -c option specified, open handle for console and hook int 21
; -----
copy_start:
test flag,flag_copy ; will we copy stdout to display?
jz copy_start_ret ; no
; ----- open a handle that points to "con"
mov dx, offset con ; address of asciiz file name
mov ax, 3d01h ; open handle for writing
int 21h
mov handle, ax ; remember handle
jnc open_worked ; did open succeed?
and flag, 255-flag_copy ; no, then we won't copy stdout...
jmp short copy_start_ret ; ... and won't hook int 21
open_worked:
; ----- hook int 21 vector
mov ax, 3521h ; get interrupt vector
int 21h
mov word ptr int21_vector,bx ; save offset
mov word ptr int21_vector[2],es ; save segment
mov dx, offset int21_handler ; address of out int 21
mov ax, 2521h ; set interrupt vector
int 21h
; ----- ensure that standard error is redirected and copied
mov al, cs:[19h] ; get stdout file handle array entry
mov cs:[1ah], al ; use stdout entry for stderr entry
copy_start_ret:
ret
; -----
; exec_user_cmd - set up and issue the int 21 function 4b (exec)
; -----
exec_user_cmd:
mov cs:exec_sp,sp ; save register
mov ax, cs:[2ch] ; pass address of our environment
mov exec_env, ax ; to exec function
mov word ptr cmdline_seg, ds ; address of command line
mov word ptr fcb1_seg, ds ; fill in segments for fcb's
mov word ptr fcb2_seg, ds
push cs
pop es
mov bx, offset exec_parm_block ; bx = exec parameter block
lds dx, commandcom_addr ; es:bx = asciiz string of command.com
mov ax, 4b00h ; load and execute a program
int 21h
mov ds, cs:swappee_psp ; restore ds addressability
cli ; turn off interrupts
mov ss, swappee_psp
mov sp, exec_sp
sti ; allow interrupts
ret
; -----
; copy_stop - close handle and restore original int 21 vector
; -----
copy_stop:
test cs:flag, flag_copy ; did we copy stdout to display?
jz copy_stop_ret ; no
; ----- close handle for console
mov bx, handle ; handle for 'con'
mov ah, 3eh ; dos 'close handle' function
int 21h
; ----- restore original int 21 vector
push ds ; ds gets clobbered, so save it
lds dx, int21_vector ; get address of old vector
mov ax, 2521h ; set interrupt vector
int 21h
pop ds
copy_stop_ret:
ret
; -----
; swap_in - swap in all but the first page of swappee
; -----
swap_in:
mov bx, cs ; bx = swappee's psp
add bx, 3ffh ; first page to swap in over
mov es, bx
test flag, flag_disk
jnz swap_in_disk
; ----- swap in from expanded memory
mov cx, 1 ; start with second logical page
cld
swap_in_page: ; loop to swap 16K
mov bx, cx ; logical page
call map_page
push ds ; save ds
mov ds, ems_frame ; ds = where to swap from
mov si, 0
mov di, 0
push cx
mov cx, 4000h ; copy 16K
rep movsb
pop cx
pop ds ; restore ds
mov bx, es
add bx, 400h
mov es, bx ; es = next place to swap to
inc cx
cmp cx, swap_pages
jl swap_in_page
ret
; ----- swap in from disk
swap_in_disk: ; es = first page to swap over
call open_swap_file ; open the swap file
mov cx, 0 ; high order part of offset
mov dx, 4000h ; file pointer to start + 16K
mov bx, swap_handle ; get swap file handle
mov ax, 4201h ; code to lseek from current position
int 21h ; let dos lseep to 2nd page
jnc lseek_done
error "LSEEK on swap file failed"
lseek_done:
mov cx, 1 ; start with second logical page
swap_in_disk_page: ; loop to swap 16K
call read_swap_file ; read 16K from swap file
mov bx, es
add bx, 400h
mov es, bx ; es = next place to swap to
inc cx
cmp cx, swap_pages
jl swap_in_disk_page
call close_swap_file
ret
; --------------------------------------------------
; int_21_handler and its subroutines
; --------------------------------------------------
assume ds:nothing
int21_handler:
; ----- decide whether we will front-end this int 21 function
cmp ah, 02h
je func02
cmp ah, 06h
je func06
cmp ah, 09h
je func09
cmp ah, 40h
je func40
; ----- call the original int 21 vector owner
do_real_thing:
jmp cs:int21_vector
; -----
; handle int 21 function 9 (print $ delimited string)
; -----
func09:
call front_start
push di
push es
mov di, dx
mov es, save_ds ; address of string at es:di
mov al, '$' ; scan for $
mov cx, -1 ; max bytes to scan
cld ; scan in forward direction
repne scasb ; find the $
sub di, dx
mov cx, di ; length to write
dec cx ; don't write the $
pop es
pop di
mov ds, save_ds ; ds addressability is blown
call write_to_con ; write buffer to display
mov ds, cs:swappee_psp ; restore ds addressability
jmp front_done
; -----
; handle int 21 function 06 (direct console i/o)
; -----
func06:
cmp dl, 0ffh ; get input characters?
je do_real_thing ; yes, then there is no output to copy
; -----
; handle int 21 function 02 (display character in dl register)
; -----
func02:
call front_start
mov char_buf, dl ; put character in write buffer
mov dx, offset char_buf ; get address of buffer
mov cx, 1 ; get length
call write_to_con ; write buffer to display
jmp front_done
; -----
; handle int 21 function 40 (write to file handle)
; -----
func40:
call front_start
; ----- verify that file handle array entry for this handle == stdout entry
push di
push es
mov bx, save_bx ; get caller's handle
mov es, save_pid ; psp for process issuing int 21
les di, es:34h ; address of caller's file handle array
mov ah, es:[di+1] ; file handle array entry for stdout
cmp ah, es:[di+bx] ; does handle entry == stdout entry?
pop es
pop di
jne func40_done ; no, don't copy to console
; ----- call real int 21 handler with handle opened for 'con'
mov ds, save_ds ; ds addressability blown
call write_to_con ; write buffer to display
mov ds, cs:swappee_psp ; restore ds addressability
func40_done:
jmp front_done
; -----
; front_start - start front_ending int 21
; -----
front_start:
assume ds:nothing
; ----- establish ds addressability and save registers
mov save_ds, ds
mov ds, cs:swappee_psp ; establish ds addressability
assume ds:code ; tell assembler
mov save_ax, ax ; save registers
mov save_bx, bx
mov save_cx, cx
mov save_dx, dx
; ----- remember caller's pid
mov ah, 51h ; 51h = get pid
int 21h
mov save_pid, bx ; remember pid
; ----- set pid so our file handle array is used
mov bx, cs ; pid = my cs register
mov ah, 50h ; 50h = set pid
int 21h
ret
; -----
; write_to_con - call original int 21 handler to write buffer to display
; -----
write_to_con:
assume ds:nothing
mov bx, cs:handle ; handle opened for 'con'
mov ah, 40h ; 40h = write to handle
pushf
call dword ptr cs:int21_vector ; call dos
ret
; -----
; front_done - almost done front_ending int 21
; -----
front_done:
assume ds:code
; ----- restore caller's pid
mov bx, save_pid ; get pid of process that issued int 21
mov ah, 50h ; 50 = set pid
int 21h
; ----- restore registers & go jump to previous int 21 handler
mov ax, save_ax
mov bx, save_bx
mov cx, save_cx
mov dx, save_dx
mov ds, save_ds ; ds addressability blown
jmp do_real_thing
; --------------------------------------------------
; the following routines are used by both parts of the program
; --------------------------------------------------
; -----
; emm - remember emm function in case of error and issue int 67
; -----
emm:
mov last_ems_func, ah
int 67h ; call expanded memory manager
or ah, ah
ret
; -----
; ems_error - handle ems errors
; -----
ems_error:
mov di, offset ems_rc
call hex_to_ascii ; make ems error code printable
mov ah, last_ems_func
mov di, offset ems_func
call hex_to_ascii ; make last ems function printable
mov cx, emsg_ems_len
mov dx, offset emsg_ems
jmp error_exit ; go display error message and exit
; -----
; hex_to_ascii - convert ah register to ascii hexidecimal at ds:di
; -----
hex_to_ascii:
mov dl, ah
mov cx, 2
hex_char:
push cx
mov cl, 4
rol dl, cl
mov al, dl
and al, 00fh
daa
add al, 0f0h
adc al, 040h
mov [di], al
inc di
pop cx
loop hex_char
ret
; -----
; error_exit - display error message and exit
; ds:di point to error message, cx has the length
; -----
error_exit:
push cx
push dx
mov dx, offset emsg_start
mov cx, emsg_start_len
mov bx, 2 ; handle for stderr
mov ah, 40h ; 40h = dos handle write
int 21h
pop dx
pop cx
mov bx, 2
mov ah, 40h
int 21h
jmp return
; -----
; routines to open, read from, and close the swap file
; -----
open_swap_file:
mov dx, offset swap_fid ; address of fileid to open
mov ax, 3d00h ; open file in read-only mode
int 21h
jnc open_exit
error "Could not open swap file"
open_exit:
mov swap_handle, ax ; save swap file handle
ret
; read_swap_file -- read 16K from swap file to address in es:0
; saves cs
read_swap_file:
push cx
mov bx, swap_handle ; get swap file handle
mov cx, 4000h ; read 16K
mov dx, 0 ; buffer offset
push ds
push es
pop ds ; buffer segment
mov ah, 3fh ; 3f = dos read (handle)
int 21h
pop ds
pop cx
jnc read_exit
error "Error reading swap file"
read_exit:
ret
close_swap_file:
mov bx, swap_handle ; get swap file handle
mov ah, 3eh ; 3e = dos close file
int 21h
ret
; -----
; return - return to DOS
; -----
return:
mov ax, 4c00h ; dos terminate function
int 21h
; -----
; map_page - map EMS logical page in bx into physical page 0
; -----
map_page:
mov al, 0 ; physical page
mov dx, ems_handle ; ems handle
mov ah, 44h ; map handle page
call emm
jz map_page_exit
jmp ems_error
map_page_exit:
ret
lowend equ $ ; end of code copied to lower memory
; --------------------------------------------------
; the following is *not* copied on top of the swappee
; --------------------------------------------------
hello db "SWAP Version 1.0 (c) Copyright 1988 Nico Mak"
db " and Mansfield Software Group", cr, lf
hello_len equ $-hello
emsg_start db "SWAP Error: "
emsg_start_len equ $-emsg_start
run_addr dw offset run_command ; offset of run_command
run_seg dw ? ; segment of run_command
swappee_mcb dw ? ; segment of mcb for swappee psp
swappee_end dw ? ; segment of mcb after swappee
my_mcb_size dw ?
next_mcb dw ? ; address of next mcb
next_code db ? ; M/Z code in next mcb
next_owner dw ? ; etc
next_size dw ?
ems_device_name db "EMMXXXX0",0 ; expanded memory manager signature
comspec db 'COMSPEC=' ; environment variable name
comspec_len equ $-comspec
mcb_info struc ; important memory control block info
addr dw ? ; address of mcb
owner dw ? ; psp of owner
len dw ? ; length of mcb
mcb_info ends
max_mcbs equ 100
mcbs mcb_info <>
mcb_length equ $-mcbs
db (max_mcbs-1)*mcb_length dup (?)
; --------------------------------------------------
; mainline code run from system prompt
; --------------------------------------------------
begin:
assume ds:code,es:code
mov sp, offset stack ; set up new stack pointer
call process_cmdline ; check options, set up 'exec' cmdline
call say_hello ; print copyright message
call check_dos_version ; ensure we have DOS 3.0 or later
call find_comspec ; find 'comspec=' in environment
call shrink_ourself ; free unneeded memory
call get_mcb_info ; get relavent info about mcbs
call check_mcbs ; ensure mcbs are in expected order
call vector_check ; ensure swappee has not hooked vectors
call figure_pages ; determine how many ems pages we need
call init_ems ; ems initialization, allocation, etc
call swap_out ; swap out swappee, command.com, and us
call muck_with_memory ; copy swap over swappee & set up mcbs
mov ss, swappee_psp ; switch to stack in low memory
call run_user_command ; go call run_command rtn in low memory
mov ss, my_psp ; switch back to original stack
call swap_first ; swap in first 16K
call clean_up ; restore original environment
exit:
jmp return ; leave SWAP
; --------------------------------------------------
; subroutines for code that is not copied to low memory follow
; --------------------------------------------------
; -----
; process_cmdline - process options, set up command line for exec function
; -----
process_cmdline:
mov bx, 80h
option_check:
inc bx
cmp byte ptr [bx], cr ; carriage return?
jne option_check2 ; no
error "No command to execute"
option_check2:
cmp byte ptr [bx], ' ' ; blank?
je option_check
cmp byte ptr [bx], '/' ; option signal?
je got_option
cmp byte ptr [bx], '-' ; option signal?
jne copy_command_line
got_option:
mov byte ptr [bx], ' ' ; blank out character on command line
inc bx ; point at option
mov al, byte ptr [bx] ; get option
mov byte ptr [bx], ' ' ; blank out character on command line
or al, ' ' ; convert option to lower case
cmp al, 'c' ; option 'c'?
jne check_option_q
or flag, flag_copy
jmp option_check
check_option_q:
cmp al, 'q' ; option 'q'?
jne check_option_f
or flag, flag_quiet
jmp option_check
check_option_f:
cmp al, 'f' ; option 'f'?
jne check_option_d
or flag, flag_force
jmp option_check
check_option_d:
cmp al, 'd' ; option 'd'?
jne bad_option
or flag, flag_disk
jmp option_check
bad_option:
error "Invalid option"
; ----- copy remainder of our command line to commmand line for command.com
copy_command_line:
mov cl, ds:[80h] ; length of my command line
inc cl ; add one for cr
mov si, 81h ; address of my command line
mov di, offset command_text ; address of where to put it
xor ch, ch ; zero uninitialized part of count
cld ; scan in forward direction
rep movsb ; copy command line
; ----- set length of new command line
mov cl, ds:[80h] ; length of my command line
add cl, 2 ; add 2 for "/c"
mov command_line,cl ; save new length
ret
; -----
; say_hello - print hello message
; -----
say_hello:
test flag, flag_quiet ; was -q option used?
jnz say_hello_exit ; yes, skip this
mov dx, offset hello ; get address of message
mov cx, hello_len ; get length of address
mov bx, 2 ; handle for stderr
mov ah, 40h ; 40h = dos write to handle
int 21h
say_hello_exit:
ret
; -----
; check_dos_version - be sure this is dos 3.0 or higher
; -----
check_dos_version:
mov ah, 30h ; 30h = dos get version
int 21h
cmp al, 3 ; ok?
jae dos_version_ret
error "DOS version must be 3.0 or higher"
dos_version_ret:
ret
; -----
; find_comspec - find fileid for exec function
; -----
find_comspec:
mov es, es:2ch ; es = environment segment
xor di, di ; point to start of env in es:di
cld ; scan in forward direction
; ----- loop through environment strings one by one, beginning here
find_string:
test byte ptr es:[di], -1 ; end of environment?
jnz check_string ; nope, continue
error "Could not find COMSPEC= in environment" ; very unlikely
; ----- compare current env string to 'COMSPEC='
check_string:
mov si, offset comspec ; point to 'COMSPEC=' string
mov bx, di ; save ptr to start of env string
mov cx, comspec_len ; length of 'COMSPEC='
repe cmpsb ; compare
je found_comspec ; found it!
mov di, bx ; restore ptr to start of env string
xor al, al ; scan for end of string
mov cx, -1
repne scasb
jmp find_string ; go back for next string
; ----- found COMSPEC=
found_comspec:
mov word ptr commandcom_addr[0], di ; remember address of ...
mov word ptr commandcom_addr[2], es ; ... asciiz "command.com"
ret
; -----
; shrink_ourself - release unneeded memory
; -----
shrink_ourself:
push cs
pop es ; address of start of SWAP memory
mov bx, offset endcode+15 ; address of end of SWAP code
mov cl, 4
shr bx, cl ; convert to paragraphs
mov ah, 4ah ; 4a = dos set block
int 21h
ret
; -----
; get_mcb_info - get relavent info from mcb chain
; -----
get_mcb_info:
mov my_psp, cs ; remember address of our psp
mov ah, 52h ; undocumented function
int 21h ; get base of memory chain
mov es, es:[bx]-2 ; this is it
mov bx, offset mcbs
mov dx, 0 ; count of mcbs
mem_loop:
mov [bx].addr, es
mov cx, word ptr es:1 ; owner of mcb
mov [bx].owner, cx
mov cx, word ptr es:3 ; length of mcb
mov [bx].len, cx
inc dx ; increment count of mcbs
cmp dx, max_mcbs
jle mem_loop1
error "Over 100 Memory Control Blocks in system"
mem_loop1:
cmp byte ptr es:0,'Z' ; last memory block?
jne mem_next
error "Could not find SWAP's PSP"
mem_next:
mov cx, es ; copy seg addr of mcb
inc cx ; next paragraph
cmp cx, my_psp ; is this our psp?
je found_our_psp ; yes!
add cx, [bx].len ; add length of this mcb
mov es, cx ; this is next memory block
add bx, mcb_length ; where next mcb goes
jmp mem_loop ; proceed
found_our_psp:
mov dx, [bx].len
mov my_mcb_size, dx ; remember length of our mcb
add cx, [bx].len ; add length to memory
mov next_mcb, cx ; this is next memory block
; ----- remember information about the next mcb
mov es, cx
mov dl, es:0
mov next_code, dl
mov dx, es:1
mov next_owner, dx
mov dx, es:3
mov next_size, dx
ret
; -----
; check_mcbs - ensure mcbs are in expected order
; verify that our parent is command.com, find swappee psp, etc.
; -----
check_mcbs:
mov cx, cs:16h ; our parent's address
mov es, cx
mov ax, es:16h ; and our grandparent's address
cmp ax, cx ; better be equal
jne unknown_parent
mov ax, cs:10h ; our ctrl-break handler
cmp ax, cx ; better equal our parent's address
je skip_our_env
unknown_parent:
error "SWAP not directly run from COMMAND.COM"
; ----- back up to find swappee's mcb. bx still points at entry for our mcb
skip_our_env:
mov cx, cs
call prev_mcb
cmp [bx].owner, cx ; is this mcb our environment?
jne skip_command
call prev_mcb
; ----- back up over all mcb's owned by command.com (es = command.com psp)
skip_command:
mov cx, es ; address of command.com psp
cmp [bx].owner, cx ; is this mcb owned by command.com?
je command_loop ; yes
error "COMMAND.COM must immediately precede SWAP in memory"
command_loop:
mov dx, [bx].addr ; remember address of mcb in case
mov swappee_end, dx ; it is the one above swappee
call prev_mcb ; back up one mcb
cmp [bx].owner, cx ; is this mcb owned by command.com?
je command_loop ; yes, skip it
; ----- assume we have one of swappee's mcbs
; back up over all it's mcb's till we reach psp
mov cx, [bx].owner ; cx = swappee's psp
find_swappee_psp:
mov dx, [bx].addr ; address of this mcb
inc dx ; address of memory
cmp dx, cx ; is this swappee's psp?
je found_swappee_psp ; yes
call prev_mcb ; check previous psp
cmp [bx].owner, cx ; still owned by swappee?
je find_swappee_psp ; yes, continue
error "Unexpected MCB while looking for PSP of swappee"
; ----- we've found swappee's psp - bx points to mcb entry for swappee
found_swappee_psp:
mov es, [bx].owner ; es = swappee's psp
mov swappee_psp, es ; remember swappee's psp
cmp word ptr es:2ch, 0 ; swappee must have an environment
jne check_mcbs_ret
error "Swappee does not have an environment"
check_mcbs_ret:
ret
; -----
; unless the -f option was specified, check whether vectors point at swappee
; Note: only interrupts 1-79h (inclusive) are checked
; -----
vector_check:
test flag, flag_force
jnz vector_check_ret
mov cx, 0 ; start at the beginning
next_vector:
inc cx ; next vector
cmp cx, 80h ; all done?
jae vector_check_ret ; yes, no vectors hooked
mov ah, 35h ; 35h = dos get vector
mov al, cl ; vector number
int 21h
mov dx, es ; get segment addr
push cx
mov cl, 4 ; shift count
add bx, 15 ; round up
shr bx, cl ; divide offset by 16
pop cx
add dx, bx ; compute segment
cmp swappee_psp, dx ; compare to start of swappee
jae next_vector ; no problem, keep looking
cmp dx, swappee_end ; compare to end of swappee
jae next_vector ; no problem either
error "Swappee has hooked an interrupt vector"
vector_check_ret:
ret
; -----
; figure_pages - figure how many 16K pages of EMS we need
; -----
figure_pages:
mov cx, swappee_psp
dec cx ; cx = swappee's mcb
mov swappee_mcb, cx ; remember address of mcb
mov dx, next_mcb ; dx = mcb after SWAP.COM
sub dx, cx ; dx = difference in paragraphs
mov cx, 10
shr dx, cl ; convert paragraphs to 16K pages
or dx, dx
jnz figure2
error "Less than 16K to swap"
figure2:
inc dx
mov swap_pages, dx
ret
; -----
; init_ems - ensure EMS is up to par, allocate pages, and save page map
; -----
init_ems:
test flag, flag_disk
jz find_emm
jmp init_ems_exit
; ----- determine whether EMS is installed
find_emm:
mov ax, 3567h ; code to get int 67 handler address
int 21h
mov di, 0ah ; offset to name string
mov si, offset ems_device_name ; correct ems name
mov cx, 8 ; length of name
cld ; scan in forward direction
repe cmpsb ; do the compare
jz test_status ; EMS not loaded
error "Could not find Expanded Memory Manager"
; ----- test EMS status
test_status:
mov ah, 40h ; code to test status
call emm
jz check_ems_version
jmp ems_error
; ----- ensure that we have ems version 3.2 or later
check_ems_version:
mov ah, 46h ; get version
call emm
jz got_ems_version
jmp ems_error
got_ems_version:
mov al, 32h
jnb get_page_frame
error "Expanded Memory Manager version must be 3.2 or higher"
; ----- get page frame address
get_page_frame:
mov ah, 41h ; code to get page frame addr
call emm
mov ems_frame, bx ; where ems memory starts
jz alloc_pages
jmp ems_error
; ----- allocate ems pages
alloc_pages:
mov ah, 43h
mov bx, swap_pages
call emm
mov ems_handle, dx
jz save_page_map
error "Not enough free expanded memory"
; ----- save ems page map
save_page_map:
mov ah, 47h ; save page map
mov dx, ems_handle
call emm
jz init_ems_exit
jmp ems_error
init_ems_exit:
ret
; -----
; swap_out - swap out swappee, command.com, and ourself
; -----
swap_out:
mov es, swappee_mcb
test flag, flag_disk ; swap to disk?
jnz swap_out_disk ; yes
; ----- swap out to expanded memory
mov cx, 0
cld
swap_out_page: ; loop to swap 16K
mov bx, cx ; logical page = loop count
call map_page
mov bx, ems_frame
assume ds:nothing
push es
pop ds ; ds = where to swap to
mov es, bx ; es = ems_frame
mov si, 0
mov di, 0
push cx
mov cx, 4000h ; copy 16K
rep movsb
pop cx
mov bx, ds ; where to swap from
add bx, 400h ; add 16K
mov es, bx ; es = next place to swap from
push cs
pop ds
assume ds:code
inc cx
cmp cx, swap_pages ; done swapping?
jl swap_out_page ; no, swap the next page
ret
; ----- swap out to disk
swap_out_disk: ; es = swappee's mcb
mov cx, 0 ; attribute
mov dx, offset swap_fid
mov ah, 3ch ; 3c = dos create file
int 21h
jnc create_done
error "Could not create swap file"
create_done:
mov swap_handle, ax
mov cx, 0 ; number of pages swapped
swap_out_disk_page: ; loop to swap 16K
push cx ; remember number of pages swapped
mov bx, swap_handle ; handle to write to
mov cx, 04000h ; write 16K
xor dx, dx ; offset to write from
push ds
push es
pop ds ; segment to write from
mov ah, 40h ; 40h = dos write to handle
int 21h
pop ds
jnc write_worked1
error "Error writing to swap file"
write_worked1:
mov bx, es ; where to swap from
add bx, 400h ; add 16K
mov es, bx ; es = next place to swap from
pop cx ; remember number of pages swapped
inc cx ; now we've swapped one more
cmp cx, swap_pages ; done swapping?
jl swap_out_disk_page ; no, swap the next page
call close_swap_file
ret
; -----
; muck_with_memory - copy part of SWAP over swappee's psp, set up mcb's, etc
; -----
muck_with_memory:
mov es, swappee_psp
; ----- copy code over swappee's psp
cld ; copy in forward direction
mov cx, offset lowend ; length of code to copy
mov si, 100h ; start copying after psp
mov di, 100h ; where to copy
rep movsb ; copy code over swappee's psp
; ----- copy our file handle array down to swappee's psp
mov cx, 20 ; length of file handle table
mov si, 18h ; address of our file handle table
mov di, 18h ; where to put file handle table
rep movsb ; copy file handle table to swappee psp
; ----- set the file handle array size and offset in swappee's psp
mov word ptr es:32h, 20 ; length of file handle table
mov word ptr es:34h, 18h ; offset of file handle table
mov word ptr es:36h, es ; segment of file handle table
; ----- now fix up the swappee's mcb (still had an M)
mov es, swappee_mcb ; address of swappee's mcb
mov dx, offset lowend+15 ; offset to end of SWAP code
mov cx, 4
shr dx, cl ; convert to paragraphs
mov word ptr es:3, dx ; put result in swappee's mcb
; ----- find address of mcb for memory that was freed up
mov bx, swappee_psp ; address of swappee's psp
add bx, dx ; add paragraphs in swappee's mcb
mov es, bx ; this is where mcb for free mem goes
; ----- fill in new mcb
mov dx, next_mcb ; address of mcb after original swap
sub dx, bx ; compute paragraphs of free space
add dx, next_size ; add paragraphs for next mcb
mov word ptr es:3, dx ; fill in size
mov dl, next_code ; get id from next mcb
mov byte ptr es:0, dl ; copy id (M or Z)
mov word ptr es:1, 0 ; mark next block as free
ret
; -----
; run_user_command - call run_command routine in low memory
; -----
run_user_command:
; ----- put swappee segment address into pointer to run_command
mov bx, swappee_psp
mov word ptr run_seg, bx ; segment of swappee psp
; ----- set pid to address of swappee psp
mov ah, 50h ; 50h = dos set pid
int 21h
; ----- call run_command in low memory
mov ds, bx
assume ds:nothing
call dword ptr cs:run_addr ; call run_command
mov ds, cs:my_psp
assume ds:code
; ----- restore pid to SWAP's psp
mov bx, cs ; pid = my cs register
mov ah, 50h ; 50h = dos set pid
int 21h
ret
; -----
; swap_first - swap in first page that was swapped out
; -----
swap_first:
mov es, swappee_mcb
test flag, flag_disk ; swapping in from disk?
jnz swap_first_disk ; yes
; ----- swap in from expanded memory
mov bx, 0 ; logical page = 0
call map_page
push ds ; save ds
mov ds, ems_frame ; ds = where to swap from
mov si, 0
mov di, 0
mov cx, 4000h ; copy 16K
cld
rep movsb
pop ds ; restore ds
ret
; ----- swap in from disk
swap_first_disk:
call open_swap_file
call read_swap_file
call close_swap_file
ret
; -----
; clean_up - restore ems or delete swap file
; -----
clean_up:
test flag, flag_disk
jnz clean_up_disk
; ----- restore ems page map
mov ah, 48h ; restore page map
mov dx, ems_handle
call emm
jz deallocate
jmp ems_error
; ----- deallocate the ems pages
deallocate:
mov ah, 45h ; deallocate pages
mov dx, ems_handle
call emm
jz clean_up_exit
jmp ems_error
; ----- delete swap disk file
clean_up_disk:
mov dx, offset swap_fid ; file handle for swap file
mov ah, 41h ; code to delete a file
int 21h
clean_up_exit:
ret
; -----
; prev_mcb -back up one entry in table of MCBs
; -----
prev_mcb:
sub bx, mcb_length
cmp bx, offset mcbs
jae prev_mcb_ret
error "Memory Control Blocks not in expected order"
prev_mcb_ret:
ret
endcode equ $
align 16
db 16 dup (0) ; so that at least one mcb follow swap
swap endp
code ends
end swap