Category : Files from Magazines
Archive   : DDJ0489.ZIP
Filename : SWAP.ASC
by Nico Mak
[LISTING ONE]
; 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 ; org 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 addr 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 ; stop 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 ; code to open handle for writing
int 21h ; open the file
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 ; code to get interrupt 21 vector
int 21h ; ask dos for address in vector
mov word ptr int21_vector,bx; save offset
mov word ptr int21_vector[2],es ; save segment
mov dx,offset int21_handler ; address of our int 21 handler
mov ax,2521h ; code to set interrupt 21 address
int 21h ; tell dos to set int 21 vector
; ----- 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 fcbs
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 ; code to load and execute a program
int 21h ; tell dos to execute the user's program
mov ds,cs:swappee_psp ; restore ds addressability
cli ; turn off interrupts
mov ss,swappee_psp ; restore stack
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 ; close handle for 'con'
mov ah,3eh ; dos function = close handle
int 21h ; tell dos to close 'con'
; ----- restore original int 21 vector
push ds ; ds gets clobbered, so save it
lds dx,int21_vector ; get address of old int 21 vector
mov ax,2521h ; code to set interrupt 21 address
int 21h ; tell dos to change it
pop ds ; restore ds addressability
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 location
int 21h ; tell dos to lseek 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 follow
; -------------------------------------------------------------------
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 dollar-sign 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 6 (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 2 (display character in dl register)
; -----
func02:
call front_start
mov char_buf,dl ; put character to write in 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 ; dos function = get pid
int 21h ; tell dos to get pid
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 ; dos function = set pid
int 21h ; tell dos to set pid
ret
; -----
; write_to_con - call original int 21H handler to write buffer to display
; -----
write_to_con:
assume ds:nothing
mov bx,cs:handle ; handle opened for 'con'
mov ah,40h ; dos function = 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 ; dos function = set pid
int 21h ; set pid
; ----- 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 contents to ascii hexadecimal 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:dx 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 ; dos function = handle write
int 21h ; output error message to stderr
pop dx
pop cx
mov bx,2 ; handle for stderr
mov ah,40h ; dos function = handle write
int 21h ; output error message to stderr
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
ret
; read_swap_file - read 16K from swap file to address in es:0
; saves cx
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 ; dos function = handle read
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 ; dos function = close file
int 21h
ret
; -----
; return - return to DOS
; -----
return:
mov ax,4c00h ; dos function to terminate
int 21h ; back to dos
; -----
; 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 relevant 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 command 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 message
mov bx,2 ; handle for stderr
mov ah,40h ; dos function = write to handle
int 21h ; write copyright message
say_hello_exit:
ret
; -----
; check_dos_version - be sure this is dos 3.0 or higher
; -----
check_dos_version:
mov ah,30h ; dos function = get version
int 21h ; get dos version
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 thru 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 ; dos function = SETBLOCK
int 21h ; shrink ourselves
ret
; -----
; get_mcb_info - get relevant 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: ; have found our psp
mov dx,[bx].len
mov my_mcb_size,dx ; remember length of our mcb
add cx,[bx].len ; add length of 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 for 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 at 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 ; get vector function
mov al,cl ; vector number
int 21h ; call dos to get vector address
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 ; get interrupt vector
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:
cmp 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 from
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 ; dos function = create a 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 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 ; dos function = 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 page
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 mcbs, 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 has 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 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 ; dos function = set pid
int 21h ; set process id
; ----- 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 ; code to 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 on mcb follows swap
swap endp
code ends
end swap
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/