BAC	segment para public 'code'
	assume	cs:BAC, ds:BAC, es:BAC, ss:NOTHING
	org	100h		; .COM format
BEGIN:
	jmp	CODE_START	; Jump around data declarations
DECLARE:			; Messages, Storage Areas, Equates
	COPYRIGHT	db	'BACopy (C) 1985, Dickinson Associates Inc.'
			db	13,10,'$'
	PATH_FILE_LEN	equ	77  ;Length = 1, Path = 63, FileName = 12, 0 = 1
	SOURCE_FILE	db	PATH_FILE_LEN dup (0)
	TARGET_PATH	db	PATH_FILE_LEN dup (0)
	SOURCE_END	dw	0
	TARGET_END	dw	0
	SOURCE_HANDLE	dw	0
	TARGET_HANDLE	dw	0
	SOURCE_DTA	db	44 dup(0)
	TARGET_DTA	db	44 dup(0)
	VALID_IN	db	'abcdefghijklmnopqrstuvwxyz,;=',9
	VALID_OUT	db	'ABCDEFGHIJKLMNOPQRSTUVWXYZ',4 dup(32)
	VALID_NUM	equ	$ - VALID_OUT + 1
	BLKSIZE 	dw	0
	LAST_BLOCK	db	0
	EVENT_FLAG	db	0
	ERR_HEAD	db	10,13,'BACopy Error - $'
	NO_PARMS	db	'Correct Syntax is:',13,10,10
	db   'BACopy [d:][source_path]source_filename[.ext] [d:][target_path]$'
	FILE_NOT_FOUND	db	'File Not Found$'
	SOURCE_ERROR	db	'Opening Source File$'
	CREATE_ERROR	db	'Creating Target File$'
	TARGET_FULL	db	'!!',10,10,13,'Target Disk is Full',13,10,10
	db	'Insert New Disk and Press [Enter]',7,'$'
	ERR_TAIL	db	10,10,13,' . . . Aborting',10,13,13,'$'
	CONFIRM_MSG_1	db	' . . $'
	CONFIRM_MSG_2	db	'BACopied to . . $'
	END_LINE	db	10,13,'$'
	NOTHING_TO_DO	db	13,10,'No Files Needed to be BACopied',13,10,'$'
;
CODE_START:	; Parse command line into source & target parameters
	mov	dx,offset COPYRIGHT	; Display copyright notice
	mov	ah,9h
	int	21h
	mov	si,80h			; PSP parameter byte count pointer
	mov	cl,[si] 		; Move byte count to CL
	xor	ch,ch			; Zero CH
	jcxz	NO_PARMS_PASSED 	; If CX is zero, there are no parameters
	mov	dx,cx			; Save byte count in dx
	inc	si			; Point to parameter area
	mov	di,si			; Copy SI to DI for cleanup routine
	cld				; Set direction flag to forward
CLEAN_PARMS:	; Change valid delimiters to blanks, lower to upper case
	lodsb				; Load each character to AL
	push	di			; Save DI on stack
	mov	di,offset VALID_IN	; Point to table of valid inputs
	push	cx			; Save CX on stack
	mov	cx,VALID_NUM		; Set CX to number of inputs to look for
repne	scasb				; See if any are in AL
	jcxz	CLEAN_END		; If not, change nothing
	mov	bx,VALID_NUM		; Set up BX to point to valid output
	sub	bx,cx			; This will leave BX one off
	mov	al,VALID_OUT [bx - 1]	; Load the valid output to AL
CLEAN_END:
	pop	cx			; Restore CX
	pop	di			; Restore DI
	stosb				; Store modified AL back to PSP
loop	CLEAN_PARMS			; Loop until CX is zero
;
	mov	cx,dx			; Restore number of bytes in PSP to CX
	mov	dx,2			; Set DX to look for up to 2 parameters
	mov	bx,offset SOURCE_FILE	; Set BX to address of 1st parameter
	mov	al,' '                  ; Set up to scan for first non-blank
	mov	di,81h			; Set DI to PC-DOS parameter pointer
FIND_PARMS:	; Start looking for parameters, load to program storage
repe	scasb				; Scan while blanks
	mov	si,di			; Set SI to second non-blank byte
	dec	si			; Adjust it to first non-blank byte
	inc	cx			; Adjust CX to compensate
	jcxz	PARMS_LOADED		; If CX is zero, no parameters left
	mov	di,bx			; Set DI to parameter hold area
	mov	ax,cx			; Store CX to first byte of hold area
	stosb				; DI is adjusted to second byte here
STORE:	lodsb				; Load each byte to AL
	cmp	al,' '                  ; Is it a blank?
	jz	END_STORE		; Yes, end of this parameter
	stosb				; No, store the byte to hold area
END_STORE:
	loopnz	STORE			; Keep looking
	sub	[bx],cx 		; Store number of bytes in each
	jcxz	PARMS_LOADED		; If CX is zero, no more parameters
	dec	byte ptr [bx]		; parameter to first byte of hold area
	mov	di,si			; Set up to scan for next non-blank
	dec	di			; Adjust DI to point to the blank
	inc	cx			; Adjust CX to compensate
	dec	dx			; Decrement DX counter
	cmp	dx,0			; Is DX zero?
	jz	PARMS_LOADED		; Yes, all expected parameters loaded
	add	bx,PATH_FILE_LEN	; No, point to next part of hold area
	jmp	FIND_PARMS		; Go back and look for more
PARMS_LOADED:				; All parameters are loaded
	cmp	SOURCE_FILE[0],0	; If there are no bytes in the
	ja	FIX_UP			; SOURCE_FILE, no parameters present
NO_PARMS_PASSED:			; Exit with an error if there
	mov	dx,offset NO_PARMS	; are no parameters passed
	jmp	ERROR_EXIT
FIX_UP: 				; Fix SOURCE_FILE and TARGET_PATH
	mov	si,offset SOURCE_FILE	; For Search calls
	lodsb				; Get Number of bytes
	xor	ah,ah			; Zero high byte of AX
	mov	di,si			; Move SI to DI for scan
	add	di,ax			; Start scan at end of parameter
	dec	di			; Adjust DI
	mov	cx,ax			; Set CX to number of bytes
	mov	al,'\'                  ; Scan for the last '\'
	std				; Set direction flag to reverse
repnz	scasb				; Scan while not '\'
	jnz	NO_SOURCE_DIR		; If Zero Flag not set, '\' not found
	add	di,2			; Add 2 to DI to point to file name
	jmp	SOURCE_FIXED		; position
NO_SOURCE_DIR:				; No source directory was specified
	add	di,1			; Adjust DI
	cmp	SOURCE_FILE[2],':'      ; Check for specified disk drive
	jne	SOURCE_FIXED		; None present, we're done
	mov	di,offset SOURCE_FILE[3]; Yes, set DI to point to first byte
SOURCE_FIXED:				; after ':'
	mov	SOURCE_END,di		; Move DI to SOURCE_END pointer
;
	cld				; Set direction flag to forward
	mov	si,offset TARGET_PATH	; Set up to look for '\' present
	lodsb				; Get number of bytes
	cmp	al,0			; If it's zero, no target specified
	je	NO_TARGET
	xor	ah,ah			; Zero high byte of AX
	add	si,ax			; Add it to SI to point to end
	dec	si			; Decrement SI to adjust
	lodsb				; Look at last byte
	mov	di,si			; Copy SI to DI
	cmp	al,'\'                  ; Is last byte a '\'?
	je	TARGET_FIXED		; Yes, everything's fine
	cmp	TARGET_PATH[0],2	; If TARGET_PATH is 2 bytes long and
	jne	STORE_SLASH		; is a disk drive specification,
	cmp	TARGET_PATH[2],':'      ; let it default to the current
	je	TARGET_FIXED		; directory.
STORE_SLASH:				; Place a '\' at the end of
	mov	al,'\'                  ; TARGET_PATH if user did
	stosb				; not
TARGET_FIXED:
	mov	TARGET_END,di		; Move DI to TARGET_END pointer
	jmp	BUFFER_SIZE
NO_TARGET:				; Set up to allow target path default
	mov	TARGET_END,offset TARGET_PATH + 1      ; to current path
BUFFER_SIZE:				; Compute size of file buffer
	mov	ax,0fdffh		; Leave plenty of room in segment
	mov	dx,offset FILE_BUFFER	; for stack & set DX to end of code
	sub	ax,dx			; Subtract
	mov	BLKSIZE,ax		; Save result in BLKSIZE
FIND_FILE:				; Find first source file
	xor	ax,ax			; Request to use SOURCE_DTA
	mov	ah,1ah			; to house FCB for SOURCE_FILE
	mov	dx,offset SOURCE_DTA
	int	21h			; Call PC-DOS
	mov	dx,offset SOURCE_FILE + 1	; DX points to SOURCE_FILE
	mov	ah,4eh			; Request function 4EH (find 1st file)
	mov	cx,0			; Set CX to zero for normal files only
	int	21h			; Call PC-DOS
	jnc	FOUND_FILE		; If no error, first file found
	mov	dx,offset FILE_NOT_FOUND; If no files found, exit
	jmp	ERROR_EXIT		; program with error message
FOUND_FILE:
	mov	LAST_BLOCK,0		; Initalize last block read flag
	mov	si,offset SOURCE_DTA+30 ; SI points to source file name in DTA
	mov	di,SOURCE_END		; DI points to end of source path
	push	si			; Save pointer to source file name
	mov	cx,13			; DTA will have 13 bytes
rep	movsb				; Move name bytes to SOURCE_FILE
	mov	di,TARGET_END		; DI points to end of target path
	pop	si			; Recover pointer to source file name
	mov	cx,13			; DTA will have 13 bytes
rep	movsb				; Move file name bytes to TARGET_PATH
FIND_TARGET:				; Find matching target file
	mov	ah,1ah			; Request to use TARGET_DTA
	xor	al,al			; to house FCB for TARGET_PATH
	mov	dx,offset TARGET_DTA
	int	21h			; Call PC-DOS
	mov	ah,4eh			; Request find 1st file for target
	mov	dx,offset TARGET_PATH+1
	mov	cx,0			; Set CX to zero for normal files only
	int	21h			; Call PC-DOS
	jc	OPEN_SOURCE		; If not found, bypass date & time check
CHECK_TIME_DATE:			; Check time & date stamps in DTAs
	mov	si,offset SOURCE_DTA+24 ; Load source file date stamp to AX
	lodsw
	mov	dx,ax			; Save in DX
	mov	si,offset TARGET_DTA+24 ; Load target file date stamp to AX
	lodsw
	cmp	dx,ax			; If Source file newer, jump
	ja	OPEN_SOURCE		; to OPEN_SOURCE
	jne	DONT_COPY		; If Source file older, don't copy it
	mov	si,offset SOURCE_DTA+22 ; Otherwise,
	lodsw				; load source time stamp to AX
	mov	dx,ax			; Save in DX
	mov	si,offset TARGET_DTA+22 ; Load target time stamp to AX
	lodsw
	cmp	dx,ax			; If Source file newer, jump
	ja	OPEN_SOURCE		; to OPEN_SOURCE
	jmp	DONT_COPY
DONT_COPY:				; Otherwise,
	call	CLOSE_ALL		; Close all files
	jmp	NEXT_FILE		; Check for next file
OPEN_SOURCE:
	mov	ah,3dh			; Request Open Source File
	mov	dx,offset SOURCE_FILE+1 ; DX points to source file path name
	mov	al,0			; with read permission only
	int	21h			; Call PC-DOS
	mov	SOURCE_HANDLE,ax	; Save handle in memory
	jnc	CREATE_TARGET		; If no carry, open was good
	mov	dx,offset SOURCE_ERROR	; Otherwise, exit with error
	mov	SOURCE_HANDLE,0 	; Make sure CLOSE_ALL ignores handle
	jmp	ERROR_EXIT
CREATE_TARGET:
	xor	ax,ax
	mov	ah,3ch			; Request create & open a file
	mov	dx,offset TARGET_PATH+1 ; named the target file
	xor	cx,cx			; with normal attribute
	int	21h			; Call PC-DOS
	mov	TARGET_HANDLE,ax	; Save target handle
	jnc	PROCEED_TO_COPY 	; If no carry, create / open is ok
	mov	dx,offset CREATE_ERROR	; Otherwise, exit with an error
	mov	TARGET_HANDLE,0 	; Make sure CLOSE_ALL ignores target
	jmp	ERROR_EXIT
PROCEED_TO_COPY:			; The heart of the matter
	mov	si,offset SOURCE_FILE+1 ; Point to source file
START1: lodsb				; Load each byte to AL
	cmp	al,0			; If ASCII 0, end of field
	je	DOTS
	mov	dl,al			; Copy byte to DL for funciton 2H
	mov	ah,2h			; Request function 2H
	int	21h			; Call PC-DOS
	jmp	START1			; Get next character
DOTS:	mov	ah,9h			; Confirm start of task
	mov	dx,offset CONFIRM_MSG_1
	int	21h
KEEP_COPYING:
	mov	ah,3fh			; Request read block of data
	mov	cx,BLKSIZE		; BLKSIZE bytes long
	mov	bx,SOURCE_HANDLE	; from source file
	mov	dx,offset FILE_BUFFER	; into buffer
	int	21h			; Call PC-DOS
	cmp	ax,0			; If AX is 0, no bytes were
	je	FINISH			; read, and we're done
	mov	cx,ax			; Move AX to CX for write call (below)
	cmp	cx,BLKSIZE		; Check number of bytes read against
	je	MORE_TO_COME		; request.  If equal, we got them all,
	mov	LAST_BLOCK,1		; otherwise, it's the last block of file
MORE_TO_COME:				;
	push	cx			; Save requested write count on stack
	mov	ah,40h			; Request write block of data
	mov	bx,TARGET_HANDLE	; to target file
	mov	dx,offset FILE_BUFFER	; from file buffer
	int	21h			; Call PC-DOS
	pop	cx			; Recover requested write count
	cmp	ax,cx			; If CX equals AX,
	je	WRITE_OK		; write was successful,
DISK_FULL:
	call	CLOSE_ALL		; Otherwise disk is full -- close files
	mov	ah,41h			; Request erase file
	mov	dx,offset TARGET_PATH+1 ; for incomplete target.
	int	21h			; Call PC-DOS
	mov	dx,offset TARGET_FULL
	mov	ah,9h
	int	21h
READ_KEYBOARD:				; Prompt requested [Enter] key
	mov	ah,8h			; Make sure [Ctrl]-[Break] is detected
	int	21h			; Call PC-DOS for key
	cmp	al,13			; Check for [Enter]
	jne	READ_KEYBOARD		; (no extended codes are 13)
	mov	cx,2
END_FULL:
	mov	dx,offset END_LINE	; Send a new line to screen
	mov	ah,9h
	int	21h
	loop	END_FULL
	jmp	FOUND_FILE		; Re-start from FOUND_FILE:
WRITE_OK:
	cmp	LAST_BLOCK,1		; If this is the last block,
	je	FINISH			; we're done
	jmp	KEEP_COPYING		; Otherwise, keep going.
FINISH: 				; Force target time & date stamps
	mov	ah,57h			; to equal source, close files
	mov	al,0			; Request get time and date stamos
	mov	bx,SOURCE_HANDLE	; for source file
	int	21h			; DX & CX contain data
	mov	ah,57h			; Request set date and time
	mov	al,1			; to force target file to
	mov	bx,TARGET_HANDLE	; source stamp
	int	21h			; Call PC-DOS
	call	CLOSE_ALL		; Go close all files
	mov	dx,offset CONFIRM_MSG_2 ; Confirm completion of task
	mov	ah,9h			; Request function 9H
	int	21h			; Call PC-DOS
	mov	si,offset TARGET_PATH+1 ; Point to source file
START2: lodsb				; Load each byte to AL
	cmp	al,0			; If ASCII 0, end of field
	je	CR_LF
	mov	dl,al			; Copy byte to DL for funciton 2H
	mov	ah,2h			; Request function 2H
	int	21h			; Call PC-DOS
	jmp	START2			; Get next character
CR_LF:	mov	dx,offset END_LINE	; Terminate display line
	mov	ah,9h			; Request function 9H
	int	21h
	mov	EVENT_FLAG,1		; Set flag to indicate file was copied
NEXT_FILE:				; Go Look for next file
	xor	ax,ax
	mov	ah,1ah			; Request to use SOURCE_DTA
	mov	dx,offset SOURCE_DTA	; to house FCB for SOURCE_FILE
	int	21h			; Call PC-DOS
	mov	ah,4fh			; Request find next source file
	mov	cx,0			; Normal files only
	int	21h			; Call PC-DOS
	jnc	FOUND_ANOTHER		; No error, another file was found
	jmp	END_OK			; Error, we're done finding files
FOUND_ANOTHER:
	jmp	FOUND_FILE		; Go process next file
END_OK: cmp	EVENT_FLAG,1		; Did anything happen?
	je	EXIT			; Yes, just exit
	mov	dx,offset NOTHING_TO_DO ; No, tell user that nothing happened
	mov	ah,9h
	int	21h
EXIT:	int	20h			; Exit to PC-DOS
ERROR_EXIT:				; Print Error Message and Exit
	push	dx			; Save error message pointer on stack
	mov	ah,9			; Display error header
	mov	dx,offset ERR_HEAD
	int	21h
	mov	ah,9			; Display error message
	pop	dx
	int	21h
	mov	ah,9			; Display error tail
	mov	dx,offset ERR_TAIL
	call	CLOSE_ALL
	int	21h
	int	20h			; Exit to PC-DOS


CLOSE_ALL	proc
	cmp	SOURCE_HANDLE,0 	; Check for valid SOURCE_HANDLE
	je	CLOSE_TARGET		; None, then go close target
	mov	ah,3eh			; Request close file
	mov	bx,SOURCE_HANDLE	; for source handle
	int	21h			; Call PC-DOS
	mov	SOURCE_HANDLE,0 	; Refresh handle
CLOSE_TARGET:
	cmp	TARGET_HANDLE,0 	; Check for valid TARGET_HANDLE
	je	CLOSE_RETURN		; None, then return
	mov	bx,TARGET_HANDLE	; Request close file
	mov	ah,3eh			; for target handle
	int	21h			; Call PC-DOS
	mov	TARGET_HANDLE,0 	; Refresh handle
CLOSE_RETURN:
	ret
CLOSE_ALL	endp
FILE_BUFFER	label	word
BAC	ends
	end	BEGIN