 	TITLE 'BUFFERED INTER-USER COPY ROUTINE FOR CP/M'
*****************************************************************
*								*
*	A UTILITY PROGRAM TO FACILITATE COPYING FILES		*
*		    BETWEEN USER AREAS				*
*								*
*****************************************************************
;
;	First publication data:
;
;	Electronic publication by :- "Software Tools" RCPM System
;				     Sydney, Australia
;
;	Print publication by      :- Dr Dobbs Journal, Palo Alto, USA
; 	
;	Copyright (c) 1981/82
;
;	Angus Bliss		Bill Bolton
;	P.O. Box 293,		Software Tools
;	Hamilton,		P.O. Box 80,
;	NSW, 2303,		Newport Beach,
;	AUSTRALIA		NSW, 2106,
;				AUSTRALIA
;
;	This program is made available for public distribution
;	for NON-COMMERCIAL use only. All commercial rights
;	retained by the authors.
;
;	Version list (latest version first)
;	-----------------------------------
;	1.6	Fix problem with zero length files writing
;		a file as large as buffer. Fix problem of
;		large files being truncated to 32K in
;		some multifile transfers. Tidy up for
;		publication - Angus Bliss 29/Apr/82
;
;	1.5	Initial release version. Large file transfer
;		bug fixed and other minor internal changes
;		mode - Bill Bolton 3/Feb/82
;
;	1.4	Overwrite options added, user abort added,
;		filename show added and lots more comments
;		added - Bill Bolton 2/Feb/82
;
;	1.3	Transfer to $$$ file first then rename after
;		succesful close (like PIP) added - Bill Bolton
;		1/Feb/82
;
;	1.2	Wildcard file transfer added. - Bill Bolton
;		31/Jan/82
;
;	1.1	Converted to 8080 code for greater portability
;	        (now that the Godbout 8085/8088 card has given
;		8080 code a new lease of life) and presentation-
;		tidied up. - Bill Bolton 30/Jan/82
;
;	1.0	Original code in Xitan Z80 source. - Angus Bliss
;		Aug/82
;
VERSION	EQU	16		;VERSION NUMBER
CNTRLC	EQU	3		;CP/M 'PANIC' CHARACTER
ACR	EQU	0DH
ALF	EQU	0AH
WBOOT	EQU	0		;CP/M WARM BOOT ENTRY
BDOS	EQU	0005H		;CP/M BDOS ENTRY POINT
FCB	EQU	05CH		;CP/M FILE CONTROL BLOCK
FCB0	EQU	06CH
TBUF	EQU	080H		;CP/M COMMAND LINE BUFFER
CI	EQU	1		;BDOS CONSOLE IN
CO	EQU	2		;BDOS CONSOLE OUT
DIRECT	EQU	6		;BDOS DIRECT CONSOLE
B$PRINT	EQU	9		;BDOS CONSOLE MESSAGE
VERS	EQU	12		;BDOS RETURN VERSION NUMBER
B$OPEN	EQU	15		;BDOS OPEN FILE
B$CLOSE	EQU	16		;BDOS CLOSE FILE
SRCH$1ST EQU	17		;BDOS SEARCH FOR FILE
SRCH$NXT EQU	18		;BDOS SEARCH FOR NEXT (AMBIG) FILE
DELET	EQU	19		;BDOS DELETE FILE
B$READ	EQU	20		;BDOS SEQUENTIAL READ
B$WRITE	EQU	21		;BDOS SEQUENTIAL WRITE
MAKE	EQU	22		;BDOS CREATE NEW FILE
REN	EQU	23		;BDOS RENAME FILE
DMA	EQU	26		;BDOS SET NEW DMA
ATTRIB	EQU	30		;BDOS SET FILE ATTRIBUTES
USER	EQU	32		;BDOS SET/GET USER

;
	ORG	100H		;FOR CP/M
;
START:
	LXI	SP,STACK	;SET A STACK
	LXI	D,MSG1
	MVI	C,B$PRINT
	CALL	BDOSE		;ANNOUNCE OURSELF
	MVI	C,VERS		;CHECK VERSION
	CALL	BDOS		;USES HL REGISTER
	MOV	A,L
	CPI	2
	JC	ERROR3		;WRONG CP/M VERSION
	LDA	TBUF		;PARAMETER COUNT
	CPI	0		;NO PARAMETER
	JZ	ERROR1
	LXI	H,FCB-1		;TAKE A COPY OF FCB
	LXI	D,FCB2-1	;@ TBUF
	LXI	B,33		;LENGTH OF A FILENAME
LDIR1:
	INX	H		;ADJUST POINTERS
	INX	D
	MOV	A,M		;GET A BYTE
	STAX	D		;PUT A BYTE
	DCX	B		;ADJUST COUNT
	MOV	A,B
	ORA	C		;ZERO YET
	JNZ	LDIR1		;NO	
	LXI	H,TBUF		;YES
	MVI	B,0
	MOV	C,M		;GET COUNT
	INX	H		;STEP OVER ANY SOURCE
	DCR	C
	INX	H		; DRIVE IDENTIFIER ON
	DCR	C
	INX	H		; FILE NAME
	DCR	C
	MVI	A,':'		;DRIVE DELIMITER
CCIR1:
	INX	H
	CMP	M		;MATCH
	JZ	GO$ON1
	DCR	C
	JZ	NOT$FOUND1	;YES
	JMP	CCIR1
;
GO$ON1:
	DCX	H		;ADJUST PTR
	MOV	A,M
	CPI	'G'+1		; A >= CHAR <= G
	JNC	NOT$FOUND1
	CPI	'A'
	JC	NOT$FOUND1
	ANI	7		;MAKE 0 TO 7
	STA	DEST$DRV
	STA	FCB2
NOT$FOUND1:
	LXI	H,FCB
	MVI	C,11		;GET LENGTH OF FILE NAME
	MVI	A,'?'		;WILDCARD
CCIR2:
	INX	H		;LOOP TO SEARCH FOR WILDCARD
	CMP	M
	JZ	GO$ON2
	DCR	C
	JZ	NOT$FOUND2
	JMP	CCIR2
;
GO$ON2:
	MVI	A,0FFH
	STA	WILD		;SET MULTIFILE FLAG
NOT$FOUND2:
	LXI	H,TBUF		;YES
	MVI	B,0
	MOV	C,M		;GET COUNT
	MVI	A,'-'		;OPTION SPECIFIER
CCIR3:
	INX	H		;LOOP TO SEARCH FOR OPTIONS
	CMP	M
	PUSH	H
	CZ	OPTION
	POP	H
	DCR	C
	JNZ	CCIR3
	JMP	NOT$FOUND3
;
OPTION:
	PUSH	PSW		;FOUND THE FLAG
OPT$1:
	INX	H		;NOW LOOK FOR AN OPTION
	MOV	A,M
	CPI	'W'
	JZ	OVER$WRITE
	CPI	'N'
	JZ	NO$QUERY
	POP	PSW
	RET
;
OVER$WRITE:
	MVI	A,0FFH
	STA	O$W
	JMP	OPT$1		;LOOK FOR ANOTHER ONE
;
NO$QUERY:
	MVI	A,0FFH
	STA	N$Q
	JMP	OPT$1		;LOOK FOR ANOTHER ONE
;
NOT$FOUND3:
	LXI	H,6DH		;FROM USER
	CALL	NSCAN		;GET NUMBER
	JC	ERROR2		;INVALID USER
	STA	FUSER		;FROM USER
	LXI	H,75H		;TO USER
	CALL	NSCAN
	JC	ERROR2
	STA	TUSER		;TO USER
	MVI	E,0FFH
	MVI	C,USER		;GET CURRENT USER
	CALL	BDOSE
	STA	CUSER		;SAVE IT
	LDA	FUSER		;FROM USER
	MOV	E,A		;PUT IN E
	MVI	C,USER
	CALL	BDOSE		;SET THE USER
	LXI	H,FCB
	LDA	WILD
	ORA	A		;SINGLE FILE ONLY ?
	JZ	COPY$FCB	;YES
	LXI	D,FCB
	MVI	C,SRCH$1ST
	CALL	BDOSE
	CPI	0FFH		;FOUND?
	JZ	ERROR4		;NO
DIR$MATCH:
 	ADD	A		;MULTIPLY BY 5
	ADD	A
	ADD	A
	ADD	A
	ADD	A
	LXI	H,TBUF		;POINT TO DIRECTORY BUFFER
	MOV	E,A
	MVI	D,0
	DAD	D		;HL < POINTER TO MATCHED FILE
COPY$FCB:
	PUSH	H
	MVI	C,12
	LXI	D,FCB1
	LDA	FCB
	MOV	M,A		;STUFF SRC DRIVE IDENT INTO FCB
C$FCB$1:
	MOV	A,M		;COPY FCB TO FCB1 (READ FCB)
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	C$FCB$1
	POP	H
C$FCB$2:
	MVI	C,11
	LXI	D,FCB2
	LDA	DEST$DRV
	STAX	D		;STUFF DEST DRIVE IDENT INTO FCB
	INX	H
	INX	D
C$FCB$3:
	MOV	A,M		;COPY FCB TO FCB1 (WRITE FCB)
	ANI	7FH		;RESET ANY FILE ATTRIBUTES
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	C$FCB$3
READ$FILE:
	LXI	H,FCB1
	CALL	SHOW
	LXI	D,FCB1		;CP/M DEFAULT
	MVI	C,B$OPEN
	CALL	BDOSE		;OPEN OUR SOURCE
	CPI	255
	JZ	ERROR4		;OPEN FAILURE
	CALL	BUFSIZ		;CALCULATE BUFFER SIZE
	XRA	A		;INITIATE REGISTERS
	STA	ACOUNT		;SECTOR COUNT
	LDA	COUNT
	MOV	B,A		;SECTOR COUNT
;
READ:	LXI	D,BUFSTART	;B HAS SECTOR CNT
READ1:	MVI	C,DMA
	CALL	BDOSE		;SET DMA ADDRESS
	PUSH	D		;SAVE DMA ADDR.
	LXI	D,FCB1
	MVI	C,B$READ
	CALL	BDOSE		;READ A SECTOR
	POP	D		;RESTORE DMA
	CPI	1
	JZ	FINISH
	CPI	0
	JNZ	ERROR5		;READ ERROR
	MOV	A,E		;BUMP POINTER
	ADI	80H
	MOV	E,A
	MVI	A,0
	ADC	D
	MOV	D,A		;BY 128 BYTES
	LDA	ACOUNT
	INR	A
	STA	ACOUNT		;SECTORS READ
	DCR	B		;ADJUST COUNT
	JNZ	READ1		;NO
	CALL	WRITE		;FULL, SO EMPTY IT
	XRA	A		;RESET REGISTERS
	STA	ACOUNT		;SECTOR COUNT
	LDA	COUNT
	MOV	B,A		;SECTOR COUNT
	JMP	READ
;
FINISH:
	MVI	A,0FFH
	STA	EOF		;FINISHED THIS FILE
	LDA	COUNT
	SUB	B
	MOV	B,A
	CALL	WRITE
	LXI	D,FCB2
	MVI	C,B$CLOSE	;CLOSE DESTINATION
	CALL	BDOSE
	CPI	255
	JZ	ERROR9		;CLOSE FAILURE
	CALL	RENAME		;RENAME $$$ TO TYP
	LDA	WILD
	ORA	A		;MORE FILES?
	JZ	DONE
	LXI	H,BUFSTART
	SHLD	BUFPT		;RESET BUFFER POINTER
	XRA	A
	STA	OPEN		;RESET FILE OPEN FLAG
	STA	EOF		;WON'T BE EOF ON NEXT FILE
	LXI	H,FCB1		;POINT TO INTERNAL FCBS
	MVI	C,64		;LENGTH OF 2 * FCB
	XRA	A
FCB$FILL1:
	MOV	M,A		;RESET MEMORY
	INX	H		;ADJUST POINTER
	DCR	C		;DONE ?
	JNZ	FCB$FILL1	;NO	
	LDA	FUSER
	MOV	E,A		;SAVE DIRECTORY POINTER TIL LATER
	MVI	C,USER
	CALL	BDOSE		;RESET TO SOURCE USER
	LXI	D,TBUF		;RESET DMA
	MVI	C,DMA
	CALL	BDOSE
	LXI	D,FCB
	MVI	C,SRCH$1ST	;START SEARCH FOR NEXT 
	CALL	BDOSE		; WILDCARD MATCH (TEDIOUS)
	LDA	F$COUNT		;NO FILES DONE SO FAR
	INR	A		;JUST DONE ANOTHER ONE
	STA	F$COUNT		;KEEP FOR NEXT TIME
	STA	D$COUNT		;INITIALISE LOOP COUNTER
SEARCH$LOOP:	
	LXI	D,0
	MVI	C,SRCH$NXT	;SEARCH FOR NEXT WILDCARD MATCH
	CALL	BDOSE
	STA	DIR$POINT
	CPI	0FFH		;NO MORE MATCH ?
	JZ	DONE		;YES
	LDA	D$COUNT		;NO, GET LOOP COUNT
	DCR	A		;ONE SEARCH DONE
	STA	D$COUNT	
	JNZ	SEARCH$LOOP	;SEARCH AGAIN
	MVI	C,DIRECT
	MVI	E,0FFH
	CALL	BDOSE
	CPI	CNTRLC		;USER WANTS ABORT ?
	JZ	U$ABORT		;YES
	LDA	DIR$POINT	;NO, A = POINTER INTO DIR SECTOR
	JMP	DIR$MATCH	;FOUND THE ONE WE NEEDED
;
WRITE:
	LDA	TUSER
	MOV	E,A
	MVI	C,USER
	CALL	BDOSE		;SET DESTINATION USER
	LDA	OPEN
	CPI	0		;FILE ALREADY OPEN ?
	JNZ	WRITE2		;YES
	CMA			;NO
	STA	OPEN		;INDICATE FILE OPEN
	LXI	D,FCB2
	MVI	C,B$OPEN
	CALL	BDOSE		;ATTEMPT OPEN
	CPI	255
	JZ	WRITE0		;NOT PRESENT
	LDA	FCB2+9		;PRESENT, CHECK R/O
	ANI	80H		;ISOLATE BIT
	RAL			;PUT IN CARRY
	JNC	NOT$RO		;NOT R/O
	LDA	O$W
	ORA	A		;OVER WRITE R/O FILE ?
	JNZ	REMOVE$RO
	JMP	ERR6A		;IS R/O
;
REMOVE$RO:
	LXI	H,FCB2+12	;FCB2 HAS GROUP 'GARBAGE' 
	XRA	A		; FROM OPEN CALL WHICH
	MVI	C,21		; NEEDS TO BE CLEANED OUT
	CALL	FILL$BLOCK	; FOR ATTRIBUTE CALL
	LXI	H,FCB2
	MVI	C,12
R$RO:
	MOV	A,M		;RESET ATTRIBUTES IN FILE NAME
	ANI	7FH
	MOV	M,A
	INX	H
	DCR	C
	JNZ	R$RO
	LXI	D,FCB2
	MVI	C,ATTRIB
	CALL	BDOSE
	CPI	0FFH		;THIS SHOULD NEVER HAPPEN
	JZ	ERROR11		; BUT JUST IN CASE
NOT$RO:
	LDA	N$Q
	ORA	A		;NO FILE EXISTS QUERY?
	JNZ	WRITE0		;YES
	CALL	ERROR6		;CHECK BEFORE DELETE
	CPI	'Y'
	JZ	WRITE1		;CONTINUE
	CPI	'y'
	JZ	WRITE1		;CONTINUE
	JMP	ABORT		;ANSWER NOT 'Y' OR 'y'
WRITE1:
	CALL	CRLF
WRITE0:
	LXI	H,FCB2+9	;POINT TO SECONDARY FILENAME
	LXI	D,TYPE
	MVI	C,3		;LENGTH OF SECONDARY FILENAME
	MVI	B,'$'		;TEMPORARY FILE TYPE MARKER
FILL$TYPE1:
	MOV	A,M		;GET SECONDARY FILE NAME
	STAX	D		;SAVE IT FOR LATER
	MOV	M,B		;STUFF IN TEMP MARKERS
	INX	H
	INX	D
	DCR	C
	JNZ	FILL$TYPE1	
	LXI	H,FCB2+12	;ZERO FILL REST OF FCB
	MVI	C,24
	XRA	A
	CALL	FILL$BLOCK
	LXI	D,FCB2
	MVI	C,MAKE
	CALL	BDOSE		;CREATE DESTINATION FILE
	CPI	255
	JZ	ERROR7		;DIRECTORY FULL
;
WRITE2:
	LDA	ACOUNT
	ORA	A		;ZERO LENGTH FILE?
	JZ	ZEXIT		;YES, DONT WRITE TO DESTINATION
	MOV	B,A		;ACTUAL SECTOR COUNT
	PUSH	H
	LXI	H,BUFSTART
	SHLD	BUFPT		;SAVE BUFFER POINTER
	POP	H
WRITE3:
	PUSH	H
	LHLD	BUFPT		;GET BUFFER POINTER
	XCHG			;DE <---- BUFFER POINTER
	POP	H
	PUSH	D
	MOV	A,E
	ADI	80H
	MOV	E,A
	MVI	A,0
	ADC	D		;16 BIT ADD OF 1 SECTOR
	MOV	D,A
	PUSH	H
	XCHG
	SHLD	BUFPT		;SAVE NEW BUFFER POINTER
	POP	H
	POP	D
	MVI	C,DMA
	CALL	BDOSE		;CHANGE DMA ADDRESS
	LXI	D,FCB2
	MVI	C,B$WRITE
	CALL	BDOSE		;WRITE A SECTOR
	CPI	0
	JNZ	ERROR8		;WRITE ERROR
	DCR	B		;DONE YET?
	JNZ	WRITE3		;NO
ZEXIT:				;(COME IN HERE IF ZERO LENGTH FILE)
	LDA	EOF
	CPI	0
	RNZ			;END
	LDA	FUSER
	MOV	E,A
	MVI	C,USER		;SET SOURCE USER
	CALL	BDOSE
	RET
;
FILL$BLOCK:
	MOV	M,A		;GENERAL BLOCK FILLER
	INX	H		; WITH A CONSTANT
	DCR	C
	JNZ	FILL$BLOCK
	RET
;
;
RENAME:
	LXI	H,FCB2+9	;START POINT
	MVI	C,27		;LENGTH TO FILL
	XRA	A		;ZERO A
	CALL	FILL$BLOCK
	LXI	H,TYPE		;POINT TO FILE TYPE
	LXI	D,FCB2+9	;SECONDARY FILE NAME
	MVI	C,3		;LENGTH TO MOVE
REN$LOOP1:
	MOV	A,M		;STUF FILE TYPE BACK INTO FCB
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	REN$LOOP1
	LXI	D,FCB2
	MVI	C,DELET
	CALL	BDOSE		;KILL ORIGINAL DESTINATION FILE
	LXI	H,FCB2+9
	MVI	C,27
	XRA	A
	CALL	FILL$BLOCK	;ZERO FILL WRITE FCB YET AGAIN	
	LXI	H,FCB2
	LXI	D,FCB2+16
	MVI	C,9
REN$LOOP2:
	MOV	A,M		;COPY WRITE FCB TO MAKE THE
	STAX	D		; SPECIAL RENAME FORMAT FCB
	INX	H
	INX	D
	DCR	C
	JNZ	REN$LOOP2
	MVI	A,'$'		;HL = POINTER TO FCB2 +9
	MVI	C,3
REN$LOOP3:
	MOV	M,A		;STUFF TEMP FILE MARKERS IN
	INX	H		; THE 'FROM' PART OF FCB
	DCR	C
	JNZ	REN$LOOP3
	LXI	H,TYPE		;DE = FCB2+9+16
	MVI	C,3
REN$LOOP4:
	MOV	A,M		;STUFF FILE TYP IN THE
	STAX	D		; 'TO' PART OF FCB
	INX	H
	INX	D
	DCR	C
	JNZ	REN$LOOP4
	LXI	D,FCB2
	MVI	C,REN		;DO THE RENAME
	CALL	BDOSE
	CPI	0FFH		;AGAIN, THIS SHOULD NEVER HAPPEN
	JZ	ERROR10		; BUT.......
	RET
;
;ERROR AND MESSAGE HANDLING
;
ERROR1:
	LXI	D,MSG3
	CALL	PRINT
	LXI	D,MSG2
	CALL	PRINT
	JMP	ABORT
;
ERROR2:
	LXI	D,MSG4
	CALL	PRINT
	LXI	D,MSG2
	CALL	PRINT
	JMP	ABORT
;
ERROR3:
	LXI	D,MSG5
	CALL	PRINT
	JMP	ABORT
;
ERROR4:
	LXI	D,MSG6
	CALL	PRINT
	JMP	ABORT
;
ERROR5:
	LXI	D,MSG7
	CALL	PRINT
	JMP	ABORT
;
ERROR6:
	LXI	D,MSG8
	MVI	C,B$PRINT
	CALL	BDOSE		;PROMPT QUESTION
	MVI	C,CI
	CALL	BDOSE
	RET			;RETURN WITH INPUT
;
ERR6A:
	LXI	D,MSG8A
	CALL	PRINT
	JMP	ABORT
;
ERROR7:
	LXI	D,MSG9
	CALL	PRINT
	LXI	D,MSG10
	CALL	PRINT
	JMP	ABORT
;
ERROR8:
	LXI	D,MSG11
	CALL	PRINT
	JMP	ABORT
;
ERROR9:
	LXI	D,MSG12
	CALL	PRINT
	JMP	ABORT
;
ERROR10:
	LXI	D,MSG16
	CALL	PRINT
	JMP	ABORT
;
ERROR11:
	LXI	D,MSG17
	CALL	PRINT
	JMP	ABORT
;
;
;GENERAL PURPOSE SUBROUTINES
;
PRINT:
	PUSH	D
	CALL	CRLF
	POP	D
	MVI	C,B$PRINT
	CALL	BDOSE		;PRINT MESSAGE
	RET
;
U$ABORT:
	LXI	D,MSG18
	CALL	PRINT
	JMP	EOJ
;
ABORT:
	LXI	D,MSG13
	CALL	PRINT
	JMP	EOJ
;
CRLF:
	MVI	E,ACR
	MVI	C,CO
	CALL	BDOSE
	MVI	E,ALF
	MVI	C,CO
	CALL	BDOSE
	RET
;
DONE:
	CALL	CRLF		;NORMAL EOJ MSG
	LXI	D,MSG14
	MVI	C,B$PRINT
	CALL	BDOSE
;
EOJ:
	LDA	CUSER		;RESET USER
	MOV	E,A
	MVI	C,USER
	CALL	BDOSE
	JMP	WBOOT
;
SHOW:
	CALL	CRLF
	LXI	D,MSG15
	MVI	C,B$PRINT
	CALL	BDOSE
	MVI	D,9
SHOW1:				;DISPLAY FILENAME IN READ FCB
	INX	H
	DCR	D
	JNZ	SHOW2
	MVI	E,'.'		;PRINT THE SEPARATOR
	MVI	C,CO
	CALL	BDOSE	
SHOW2:
	MOV	A,M
	CPI	0
	RZ
	CPI	' '		;SKIP BLANKS
	JZ	SHOW1
	MOV	E,A
	MVI	C,CO
	CALL	BDOSE
	JMP	SHOW1
;
;
BUFSIZ:
	LHLD	BDOS+1
	LXI	D,-6
	DAD	D		;HL = POINTER TO BASE OF BDOS
	LXI	D,BUFSTART
	ORA	A		;CY=0
	MOV	A,L
	SBB	E		;SUBTRACT E FROM L
	MOV	L,A
	MOV	A,H
	SBB	D		;SUBTRACT D FROM H
	MOV	H,A
	SHLD	SIZEB
	MVI	B,7
	ORA	A		;RESET CARRY
BUFSIZ0:
	MOV	A,H		;DIVIDE HL BY 128 (WHICH
	RAR			; JUST HAPPENS TO BE THE
	MOV	H,A		; SIZE OF A CP/M LOGICAL
	MOV	A,L		; SECTOR
	RAR
	MOV	L,A
	ORA	A		;RESET CARRY
	DCR	B
	JNZ	BUFSIZ0
	MOV	A,H		;> 255 ?
	CPI	0
	JZ	BUFSIZ1		;NO
	MVI	A,255		;YES, BIGGEST BUFFER
	JMP	BUFSIZ2
;
BUFSIZ1:
	MOV	A,L
BUFSIZ2:
	STA	COUNT		;NO SECTORS IN BUFFER
	RET
;
BDOSE:
	PUSH	B		;BDOS ENTRY
	PUSH	D
	PUSH	H
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	RET
;
NSCAN:
	LXI	D,0		;CLEAR WORK	
	MOV	A,M		;GET CHAR
	CPI	'9'+1		;IS IT A DIGIT
	JNC	NSCAN2		;> 9
	CPI	'0'
	JC	NSCAN2		;< 0
NSCAN0:
	SUI	'0'		;REMOVE ASCII BIAS
	PUSH	H		;SAVE PTR
	XCHG			;GET WORK IN HL
	PUSH	H
	POP	D
	DAD	H
	DAD	H
	DAD	D
	DAD	H		;HL=HL*10
	MVI	D,0
	MOV	E,A		;NEW DIGIT
	DAD	D		;ADD IT IN
	XCHG			;PUT WORK BACK
	POP	H		;RESTORE PTR
	INX	H		;AND STEP IT
	MOV	A,M
	CPI	'9'+1
	JNC	NSCAN1
	CPI	'0'
	JC	NSCAN1
	JMP	NSCAN0		;LOOP
;
NSCAN1:	MOV	A,E		;GET NUMBER
	CPI	16		;<= 15
	JNC	NSCAN2
	ORA	A		;CLEAR CY
	JMP	NSCAN3
;
NSCAN2:
	STC			;SET CARRY
NSCAN3:
	RET			;EXIT HERE
;
MSG1	DB	'PUT  Version ',VERSION/10 + '0','.'
	DB	VERSION MOD 10 + '0'
	DB	', by Angus Bliss and Bill Bolton',ACR,ALF,'$'
;
MSG2	DB	'Usage:',ACR,ALF
	DB	'	A>put [d:]filename f.t [d:] [-NW] <cr>'
	DB	ACR,ALF
	DB	'Where:',ACR,ALF
	DB	'	filename - is any valid CP/M file '
	DB	'specifier',ACR,ALF
	DB	' 	f	 - is source user area',ACR,ALF
	DB	'	t	 - is destination user area'
	DB	ACR,ALF
	DB	'	d:	 - is optional drive specifier'
	DB	ACR,ALF
	DB	'	-	 - is an option flag',ACR,ALF
	DB	'	N	 - is no query to overwrite '
	DB	'existing file',ACR,ALF
	DB	'	W	 - is force overwrite of R/O '
	DB	'file',ACR,ALF,ALF
	DB	'	Will prompt if destination '
	DB	'file is already present',ACR,ALF,'$'
;
MSG3	DB	'No parameters given',ACR,ALF,'$'
;
MSG4	DB	'Invalid user number(s)',ACR,ALF,'$'
;
MSG5	DB	'Sorry - you need CP/M 2.x',ACR,ALF,'$'
;
MSG6	DB	'Open fail on source file.',ACR,ALF,'$'
;
MSG7	DB	'Read failure on source file.',ACR,ALF,'$'
;
MSG8	DB	'  Destination file is present.',ACR,ALF
	DB	'	Continue (y) or Abort (n)?$'
;
MSG8A	DB	'Destination is present and R/O.$'
;
MSG9	DB	'Open failure on destination file.$'
;
MSG10	DB	'Destination directory probably full.$'
;
MSG11	DB	'Write error on destination.$'
;
MSG12	DB	'Close fail on destination.$'
;
MSG13	DB	'ABORT - returning to CP/M.',ACR,ALF,'$'
;
MSG14	DB	'**** Normal end-of-job ****',ACR,ALF,'$'
;
MSG15	DB	'	Putting file : $'
;
MSG16	DB	'Rename error on destination.$'
;
MSG17	DB	'Rename error on R/O file.$'
;
MSG18	DB	'ABORT, Control C typed at console - '
	DB	'returning to CP/M',ACR,ALF,'$'
;
CUSER	DB	0		;INITIATING USER
FUSER	DB	0		;FILE FROM USER
TUSER	DB	0		;FILE TO USER
SIZEB	DW	0		;BUFFER IN BYTES
BUFPT	DW	0		;DMA POINTER
COUNT	DB	0		;BUFFER SIZE IN SECTORS
ACOUNT	DB	0		;ACTUAL SECTOR COUNT
OPEN	DB	0		;FILE OPEN SWITCH
EOF	DB	0		;END OF SOURCE SWITCH
WILD	DB	0		;WILDCARD SWITCH
O$W	DB	0		;OVER WRITE SWITCH	
N$Q	DB	0		;NO QUERY SWITCH
DEST$DRV DB	0		;DESTINATION DRIVE
F$COUNT	DB	0		;FILES TRANSFERED COUNTER
D$COUNT	DB	0		;FILES TO SEARCH COUNTER
DIR$POINT DB	0		;TEMP STORAGE FOR SEARCH NEXT
;
TYPE:
	DB	'   '		;SECONDARY FILE TYPE
				; FOR RENAME AFTER WRITE
FCB1:				;SOURCE FCB
	DB	0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0 
;
FCB2:				;DESTINATION FCB
	DB	0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0 
;
	DS	32		;16 LEVEL STACK
STACK	EQU	$		;SOME STACK SPACE
;
BUFSTART	EQU	$+10	;SOME LEEWAY
;
	END	START
