;************************************************************************
;									*
;               ISIS II TO CPM						*
;             FILE COPY UTILITY						*
;									*
;                VERSION 2.2						*
;	Modified by Rod Whitworth to allow nomination of the		*
;	drive & file of origin and destination. An input file		*
;	cannot reside on drive A: and a name must be given.		*
;	The output file defaults to A:nnnnnnnn.typ (named as 		*
;	in the input). The output drive name is also optional		*
;	The original program also transferred only whole sectors	*
;	causing garbage to be placed on end of files: fixed now.	*
;************************************************************************
cdisk	equ	4		;logged disk stored here

FALSE	EQU	0
TRUE	EQU	NOT FALSE
LOGICAL	EQU	TRUE		;SET TRUE IF SECTORS INCREMENTED IN BIOS

	ORG	100H
ICOPY:	LXI	SP,STACK	;SET STACK
	LDA	CDISK		;WHERE LOGGED
	STA	ODISK		;SAVED FOR LATER
	LDA	TFCB
	ORA	A
	JZ	EXIT		; NO FILE SPECIFIED
	SUI	1
	STA	DRNUM		;INPUT DRIVE
	MOV	C,A		;SELECT DISK
	LXI	D,0		;IN CASE NOT LOGGED IN
	CALL	SDSK
	CALL	VALID		;SEE IF IT'S OK FOR INPUT
	CALL	HOME		;HOME INPUT DISK
	LDA	OFCB+1		;IS THERE A NAME
	CPI	20H		;IF NOT IT'S A SPACE & WE CAN
	LXI	D,TFCB		;USE THE OLD NAME
	JZ	OTRAN		;BY JUMPING AROUND
	LXI	D,OFCB		;POINT TO OUTPUT DISK & NAME
OTRAN:	LXI	H,VFCB		;POINT TO OUR FCB
	MVI	B,16
	CALL	MOVE		;PUT IT SAFELY AWAY
	LDA	OFCB		;FIND THE REAL DESTINATION DISK
	STA	VFCB		;MAYBE REDUNDANT BUT SO WHAT ??
	ORA	A		;IS IT DEFAULT ?
	JZ	STDSK
	SUI	1		;OR DOES IT NEED ADJUSTMENT ?
STDSK:	STA	DESTN		;READY FOR SET-UP
	XRA	A
	STA	VFCB+32		;SET CURRENT RECORD TO ZERO
	LXI	D,DIRLNK	;POINT TO DIR LINK BLOCK LINK
	LXI	H,DBCA		;DIR BCA
	CALL	GLB		;GET LINK BLOCK
ICL0:	LXI	H,DBCA		;DIR BCA
	CALL	GDB		;GET DIR BLOCK
	JC	NOTF		;NO MORE DIR BLOCKS, ERR
	LXI	H,DBUF		;DIR BUFFER
	SHLD	DENT		;SAVE IN POINTER
	MVI	A,8		;NUMBER OF  ENTRIES/BLOCK
	STA	DCNT		;SAVE
ICL1:	LHLD	DENT		;GET DIR ENTRY PTR
	MOV	A,M		;GET STATUS BYTE
	CPI	00		;ACTIVE?
	JNZ	ILN1		;NO, GET NEXT ENTRY
	INX	H		;POINT TO FN
	LXI	D,TFCB+1	;POINT TO ENTERED FN
	MVI	B,6		;FN SIZE
	CALL	FCHK		;TEST FN
	JNZ	ILN1		;NO, GET NEXT ENTRY
	LHLD	DENT		;GET DIR ENTRY ADDR
	LXI	D,7		;OFFSET TO EXT.
	DAD	D		;ADDR OF EXT.
	LXI	D,TFCB+1+8	;EXT
	MVI	B,3		;SIZE OF EXT.
	CALL	FCHK		;TEST EXT
	JZ	ICL2		;OK, FOUND FILE
ILN1:	LHLD	DENT		;POINTER TO ENTRY
	LXI	D,16		;SIZE OF ENTRY
	DAD	D		;POINT TO NEXT ENTRY
	SHLD	DENT
	LDA	DCNT		;ENTRY COUNTER
	DCR	A		;DECREMENT
	STA	DCNT
	JNZ	ICL1		;LOOP
	JMP	ICL0

NOTF:	LXI	D,MSG1
	MVI	C,09
	CALL	CPM
	JMP	EXIT

IOERR:	LXI	D,MSG2
	MVI	C,09
	CALL	CPM
	JMP	EXIT


ICL2:	LHLD	DENT		;GET DIR ENTRY ADDRESS
	LXI	D,11		;OFFSET TO BYTE COUNT OF LAST BLOCK
	DAD	D
	MOV	A,M
	STA	LASTX		;# OF BYTES IN LAST TRANSFER
	INX	H		;POINT TO BLOCK COUNT
	MOV	E,M
	INX	H
	MOV	D,M		;BLOCK COUNT IN DE
	XCHG
	SHLD	BCNT		;SAVE BLOCK COUNT
	INX	D		;POINT TO LINK BLOCK LINK
	LXI	H,FBCA		;FILE BCA
	CALL	GLB		;GET LINK BLOCK
	LDA	DESTN
	MOV	C,A		;OUTPUT DISK #
	CALL	SDSK		;SET DISK
	LXI	D,VFCB		;POINT TO FCB
	MVI	C,DELETE	;BYE TO ANY OLD ONE
	CALL	CPM
	LXI	D,VFCB
	MVI	C,CREATE
	CALL	CPM		;CREATE FILE
	CPI	0FFH		;ERROR?
	JZ	IOERR
ICL3:	LXI	H,FBCA		;POINT TO BCA
	CALL	GDB		;GET DATA BLOCK
	JC	DONE		;FINS
	LXI	D,FBUF		;COPY FBUF TO TBUF
	LXI	H,TBUF
	LDA	XFERC		;TRANSFER SIZE
	MOV	B,A
	CPI	128
	CNZ	FLAST		;FILL BUFF WITH EOF IF SHORT
	CALL	MOVE
	LDA	DESTN
	MOV	C,A
	CALL	SDSK		;SET DISK FOR OUTPUT
	LXI	D,VFCB
	MVI	C,WRITE
	CALL	CPM		;WRITE THE BLOCK ON CPM
	ORA	A
	JNZ	IOERR
	LHLD	BCNT		;GET BLOCK COUNT
	DCX	H		;DECREMENT
	SHLD	BCNT
	MOV	A,H
	CPI	0		;TEST FOR DONE
	JNZ	ICL3		;NO, LOOP
	MOV	A,L
	CPI	1
	JZ	CSWAP
	CPI	0
	JNZ	ICL3
DONE:	LDA	DESTN
	MOV	C,A
	CALL	SDSK		;SET DISK FOR OUTPUT
	LXI	D,VFCB		;FCB
	MVI	C,CLOSE
	CALL	CPM
EXIT:	LDA	ODISK
	MOV	C,A		
	CALL	SDSK		;FORCE LOGIN TO ORIGINAL DISK
	JMP	BOOT		;FINISHED

;***************************************;
;
;           GLB - GET LINK BLOCK
;
;         DE= A(LINK TO LINK BLOCK)
;         HL= A(BLOCK CONTROL AREA)
;
;***************************************;
GLB:	SHLD	BCAP		;SAVE BCA ADDR
	CALL	SEEKR		;READ LINK BLOCK
	LHLD	BCAP		;GET BCA ADDR
	LXI	D,BCBL		;OFFSET TO LINK BUFFER
	DAD	D
	LXI	D,TBUF
	MVI	B,128
	CALL	MOVE		;COPY TO LINK BUFFER
	LHLD	BCAP		;GET BCA ADDR
	LXI	D,BCBL+4	;OFFSET TO FIRST DATA LINK
	XCHG
	DAD	D		;DE=A(FIRST DATA LINK)
	XCHG
	LXI	B,BCAL		;OFFSET TO LINK PTR
	DAD	B
	MOV	M,E
	INX	H
	MOV	M,D		;SET LINK PTR
	LHLD	BCAP		;BCA ADDR
	LXI	D,BCALC		;LINK COUNT
	DAD	D
	MVI	M,62		;NO OF LINKS
	RET

;****************************************:
;
;            GDB:   GET DATA BLOCK
;
;            HL= A(BLOCK CONTROL AREA)
;
;****************************************;

GDB:	SHLD	BCAP		;SAVE BCA ADDR
	LXI	D,BCAL		;OFFSET TO LINK BUF
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M		;GET LINK ADDR
	PUSH	D		;SAVE LINK ADDR
	LDAX	D		;GET LINK BYTE
	INX	D
	MOV	C,A
	LDAX	D		;GET ANOTHER LINK BYTE
	ORA	C		;TEST FOR ZERO LINK
	POP	D
	JZ	GDBE		;END, EXIT
	PUSH	D		;SAVE D AGAIN
	PUSH	H
	CALL	SEEKR		;GET DATA BLOCK
	POP	H
	POP	D
	INX	D
	INX	D
	MOV	M,D
	DCX	H
	MOV	M,E		;UPDATE LINK PRT
	LHLD	BCAP		;GET BCA ADDR
	LXI	D,BCBD		;OFFSET TO DATA BUF
	DAD	D
	LXI	D,TBUF
	MVI	B,128
	CALL	MOVE		;COPY DATA TO BUF
	LHLD	BCAP		;GET BCA ADDR
	LXI	D,BCALC		;LINK COUNT
	DAD	D
	DCR	M		;DECREMENT
	RNZ			;OK, CONTINUE
	LHLD	BCAP		;GET BCA ADDR
	LXI	D,BCBL+2	;POINT TO LINK BUF
	DAD	D
	MOV	E,M		;GET LINK
	INX	H
	MOV	D,M
	DCX	H
	MOV	E,A
	ORA	D		;TEST FOR ZERO LINK
	JZ	GDBE		;END, EXIT
	XCHG			;DE = A(NEXT LINK)
	LHLD	BCAP		;BCA ADDR
	CALL	GLB		;GET LINK BLOCK
	RET

GDBE:	STC			;INDICATE EOF
	RET

;************************************;
;
;       FCHK:   FILE ID CHECK
;
;       DE = A(ISIS FILE ID)
;	HL = A(CPM FILE ID)
;	B = SIZE OF FIELD
;
;*************************************;

FCHK:	XCHG
FCK1:	LDAX	D		;GET BYTE
	CPI	00		;SEE IF END OF ID
	JNZ	FCK2		;YES, SEE IF END OK
	MOV	A,M		;GET BYTE
	CPI	' '		;END BOTH
	RZ			;DONE
	ORA	1
	RET			;DONE
FCK2:	CMP	M		;COMPARE
	RNZ			;N,G.
	INX	D
	INX	H
	DCR	B		;DECREMENT COUNT
	JNZ	FCK1
	RET

;***************************************;
;
;         SEEKR:   SEEK DISK BLOCK
;
;         DE = A(LINK)
;
;***************************************;

SEEKR:	PUSH	D		;SAVE DE
	LDA	DRNUM
	MOV	C,A
	LXI	D,1		;WE'VE BEEN HERE BEFORE
	CALL	SDSK
	POP	D
	PUSH	D
	LDAX	D		;GET SECTOR
	MOV	C,A
	CALL	SSEC		;SET SECTOR
	POP	D
	PUSH	D
	INX	D
	LDAX	D		;GET TRACK
	MOV	C,A
	CALL	STRK
	CALL	READ		;READ BLOCK
	POP	D
	RET
;************************************************
;						*
;	FLAST: SET UP BUFFER FOR LAST BLOCK	*
;	INITIALISE TO 1AH (CP/M EOF)		*
;	PRIOR TO TRANSFER OF REAL DATA		*
;						*
;************************************************

FLAST:
	PUSH	H
	LXI	H,TBUF
	MVI	A,128
EOFL:	MVI	M,01AH
	INX	H
	DCR	A
	JNZ	EOFL
	POP	H
	RET
CSWAP:			;DOES THE COUNT CORRECTION FOR 
			;THE LAST BLOCK
	LDA	LASTX
	STA	XFERC
	JMP	ICL3

;*****************************************;
;
;       MOVE:   MOVE DATA
;
;	DE = A (SOURCE)
;	HL = A(DEST)
;	B = COUNT
;
;******************************************;

MOVE:	LDAX	D		;GET BYTE
	MOV	M,A		;STORE BYTE
	INX	H
	INX	D		;BUMP PTRS
	DCR	B		;DECREMENT COUNT
	JNZ	MOVE
	RET

;*******************************************;
;
;	CPM INTERFACE ROUTINES
;
;*******************************************;
VALID:	;CHECKS DPB TO SEE IF SS/SD FLOPPY AND EXIT IF NOT
	;HL = 0 IF NOT SELECTABLE ELSE -> DPH ADDR
	MOV	A,H
	ORA	L
	JZ	DONE		;GO TO CPM
	LXI	D,10
	DAD	D
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A		;HL -> DPB
	MOV	A,M
	CPI	26
	JNZ	DONE		;IF NOT 26 SPT
	INX	H
	MOV	A,M
	ORA	A
	JNZ	DONE		;HIGH BYTE OF SPT
	LXI	D,4
	DAD	D
	MOV	A,M
	CPI	242		;SINGLE DENSITY DSM
	JNZ	DONE
	RET			;PRETTY GOOD CHANCE IT'S OK NOW


SDSK:	LHLD	0001H		;GET BIOS ADDR
	MVI	L,1BH
	PCHL
SSEC:	LHLD	0001H
	MVI	L,21H
	IF	LOGICAL		;USED TO REDUCE THE SECTOR #
	DCR	C		;IF THE BIOS INCREMENTS IT
	ENDIF			;TO 'CORRECT' LOGICAL TO PHYSICAL
	PCHL
STRK:	LHLD	0001H
	MVI	L,1EH
	PCHL
READ:	LHLD	0001H
	MVI	L,27H
	PCHL
HOME:	LHLD	0001H
	MVI	L,18H
	PCHL

;******************************************;
;
;	BLOCK CONTROL AREA DEFINITIONS
;
;******************************************;

BCA	EQU	0
BCAL	EQU	0
BCALC	EQU	BCAL+2
BCBL	EQU	BCALC+1
BCBD	EQU	BCBL+128

;******************************************;
;
;		    DATA
;
;******************************************;
MSG1:	DB	'FILE NOT FOUND',0DH,0AH,'$'
MSG2:	DB	'I/O ERROR',0DH,0AH,'$'

XFERC:	DB	128		;INITIAL BLOCK SIZE MODIFIED FOR LAST
LASTX:	DS	1		;BYTE COUNT OF LAST RECORD
DRNUM	DS	1		;INPUT DRIVE
DESTN	DS	1		;OUTPUT DRIVE
ODISK	DB	0		;PROBLY A:
DIRLNK:	DB	01,01
DENT:	DS	2
DCNT:	DS	1
BCNT:	DS	2
BCAP:	DS	2
	DS	64
STACK	EQU	$

DBCA:	DS	2
	DS	1
	DS	128
DBUF:	DS	128

FBCA:	DS	2
	DS	1
	DS	128
FBUF:	DS	128
VFCB	DS	36

TBUF	EQU	0080H
TFCB	EQU	005CH
OFCB	EQU	006CH	;OUTPUT FILE DRIVE AND [NAME]
CPM	EQU	0005H
BOOT	EQU	0000H
CREATE	EQU	22
WRITE	EQU	21
CLOSE	EQU	16
DELETE	EQU	19
	END
