		title	'cp/m plus pip enhancer'
		page	58

; purpose : to enhance the function of PIP.COM in the following points:

; a) if the source file has a byte count <>0, this byte count is transfered
;    to the destination file.

; b) if the source file has the ARCHIVE bit set, the destination file
;    will have its ARCHIVE bit set.

; c) if the source file has an UPDATE date&time stamp, this is copied
;    as CREATE and UPDATE date&time stamp to the destination file.

; It's simple to copy the ARCHIVE bit and byte count, as CP/M Plus
; has functions to get/set these dates. In case of date&time stamps,
; we have to go the dirty way: disabling the BIOS-internal clock
; software and setting the desired date&time in the SCB will allow
; us to do this forbidden thing. NOTE: this procedure WILL NOT WORK
; in systems without clock or with interrupt-driven ticker, but works
; fine in systems with hardware clock using BIOS function 26: TIME.
; NOTE also that it is not possible to use PIPRSX within another RSX,
; which manipulates the WBOOT jump at location 0000h.


;       Constants :

FALSE		equ	0		; boolean definitions
TRUE		equ	not FALSE
TEST		equ	FALSE		; diagnostic printer output
patch$loc1	equ	06CCh
patch$loc2	equ	0E62h
patch$loc3	equ	0E75h
patch$loc4	equ	0E21h
patch$loc5	equ	1871h
patch$loc6	equ	0B69h
patch$loc7	equ	195Ah
source$fcb	equ	22C7h
dest$fcb	equ	22F6h
pip$buffer	equ	237Dh
attr$fcb	equ	2325h

		page
;       RSX header :

		cseg
serial:		db	0,0,0,0,0,0	; serial number	field
start:		jmp	func$test	; jump to start	of RSX
next:		jmp	0		; link to BDOS
prev:		dw	0		; link to previous RSX
remove:		db	TRUE		; remove after operation
nonbank:	db	FALSE		; loaded on every system
RSX$name:	db	'PIPRSX  '	; name of RSX
loader:		db	0		; loader flag
reserved:	db	0,0		; reserved area


;	print macro:

print		macro	string
		local	str
		lxi	h,str
		call	pstring
		dseg
str:		db	string
		db	13,10
		db	0
		cseg
		endm

;       RSX function test :

		cseg
func$test:	mvi	a,12		; patch only on return version call
		cmp	c
		jnz	next

patch$pip:	mvi	a,JMP		; patch some locations in PIP.COM

		lxi	h,patch1
		sta	patch$loc1
		shld	patch$loc1+1

		lxi	h,patch2
		sta	patch$loc2
		shld	patch$loc2+1

		lxi	h,patch3
		sta	patch$loc3
		shld	patch$loc3+1

		lxi	h,patch4
		sta	patch$loc4
		shld	patch$loc4+1

		lxi	h,patch5
		sta	patch$loc5
		shld	patch$loc5+1

		lxi	h,patch6
		sta	patch$loc6
		shld	patch$loc6+1

		lxi	h,patch7
		sta	patch$loc7
		shld	patch$loc7+1

		if	TEST
		push	b
		push	d
		print	'PIP.COM patched'
		pop	d
		pop	b
		endif		

		jmp	next


;*******************************************************************************

; first patch: after getting input line.
;	       see if we can allow copy of attributes.

		cseg
patch1:		if	TEST
		print	'PATCH 1 reached'
		endif

		xra	a		; reset all RSX variables
		sta	inhinbit	; no multiple source files
		call	clear$vars
		call	rest$date	; restore old system date&time
		mvi	c,0		; not between []

		lxi	h,pip$buffer	; test input line for multiple input file
		mov	b,m		; B := char count
		inr	b

buffer$test:	inx	h		; character left?
		dcr	b
		jz	patch1$end

		mov	a,c		; between []?
		ora	a
		jnz	buffer$option
		
		mvi	a,','		; test for multiple input files
		cmp	m
		jz	patch1$multi
		
		mvi	a,'['		; test for option start
		cmp	m
		jnz	buffer$test

		mvi	c,TRUE		; set option flag
		jmp	buffer$test

patch1$multi:	mvi	a,TRUE
		sta	inhinbit
		if	TEST
		print	'MULTIPLE SOURCE FILES or CHANGING COPY'
		endif

patch1$end:	db	01h,25h,23h	; old code at patch$loc1
		jmp	patch$loc1+3

buffer$option:	mov	a,m		; get option char
		call	UPCASE		; convert to upper case
		cpi	']'		; end of option ?
		jz	option$end
		cpi	'D'
		jz	patch1$multi
		cpi	'F'
		jz	patch1$multi
		cpi	'I'
		jz	patch1$multi
		cpi	'L'
		jz	patch1$multi
		cpi	'N'
		jz	patch1$multi
		cpi	'P'
		jz	patch1$multi
		cpi	'Q'
		jz	patch1$multi
		cpi	'S'
		jz	patch1$multi
		cpi	'T'
		jz	patch1$multi
		cpi	'U'
		jz	patch1$multi
		cpi	'Z'
		jz	patch1$multi
		jmp	buffer$test

option$end:	mvi	c,FALSE		; reset option flag
		jmp	buffer$test


;*******************************************************************************

; second patch: before opening source file.
;		force OPEN to return byte count of source file.

		cseg
patch2:		if	TEST
		print	'PATCH 2 reached'
		endif
		lxi	h,source$fcb+32	; set FCB to get byte count
		mvi	m,0FFh

patch2$end:	db	01h,0C7h,22h	; old code at patch$loc2
		jmp	patch$loc2+3

;*******************************************************************************

; third patch: after successfull opening source file.
;	       save byte count, archive flag and date&time stamp.

		cseg
patch3:		if	TEST
		print	'PATCH 3 reached'
		endif
		lxi	h,source$fcb+32	; get byte count of source file
		mov	a,m
		sta	byte$count
		mvi	m,0		; set CR field to (record = 0)

		if	TEST
		ora	a
		jz	patch3$nocount
		print	'File has byte count'
patch3$nocount:	endif

		lxi	h,source$fcb+11	; get ARCHIVE bit of this file
		mov	a,m
		ral			; boolean from bit 7
		sbb	a
		sta	archive$flag

		if	TEST
		ora	a
		jz	patch3$noarch
		print	'File has archive bit set'
patch3$noarch:	endif

		lxi	h,source$fcb	; copy source fcb into internal buffer
		lxi	d,int$fcb	; because BDOS 102 destroys FCB contents
		mvi	b,36

patch3$move:	mov	a,m
		stax	d
		inx	h
		inx	d
		dcr	b
		jnz	patch3$move

		lxi	h,0		; clear update date&time stamp
		shld	update$date
		shld	update$time		

		lxi	d,int$fcb	; get date and time stamps
		mvi	c,102
		call	next
		ora	a		; error ?
		jnz	patch3$end	; yes: do not make date&time stamps
	
		lhld	int$fcb+28	; get update date stamp
		mov	a,l
		ora	h		; zero ?
		jz	patch3$create
		
		shld	update$date	; no: store it
		lhld	int$fcb+30	; update time stamp
		shld	update$time
		if	TEST
		print	'File has UPDATE stamp'
		endif
		jmp	patch3$end

patch3$create:	lhld	int$fcb+24	; get create or access date and time stamp
		mov	a,l
		ora	h		; zero ?
		jz	patch3$end
		
		shld	update$date	; no: store it
		lhld	int$fcb+26	; create time stamp
		shld	update$time
		if	TEST
		print	'File has CREATE stamp'
		endif

patch3$end:	db	3Ah,12h,24h	; old code at patch$loc3
		jmp	patch$loc3+3


;*******************************************************************************

; fourth patch : before making destination file.
;		 change system date&time.

		cseg
patch4:		if	TEST
		print	'PATCH 4 reached'
		endif
		lda	inhinbit	; is PIP copying single files ?
		ora	a
		jnz	patch4$end	; no: do nothing

		lhld	update$date	; yes: has file update stamp?
		mov	a,l
		ora	h
		jz	patch4$end	; no: do nothing
		
		call	set$date	; set new date&time

patch4$end:	db	01h,0F6h,22h	; old code at patch$loc4
		jmp	patch$loc4+3


;*******************************************************************************

; fifth patch : after closing destination file.
;		restore system date&time.

		cseg
patch5:		if	TEST
		print	'PATCH 5 reached'
		endif

		call	rest$date

patch5$end:	db	3Ah,7Bh,23h	; old code at patch$loc5
		jmp	patch$loc5+3


;*******************************************************************************

; sixth patch : within error handler.
;		set RSX ready for next start.

		cseg
patch6:		if	TEST
		print	'PATCH 6 reached'
		endif
		mvi	a,TRUE		; reset all internal parameters
		sta	inhinbit

		call	clear$vars
		call	rest$date

patch6$end:	db	3Ah,68h,23h	; old code at patch$loc6
		jmp	patch$loc6+3


;*******************************************************************************

; seventh patch: before assigning file attributes.

		cseg
patch7:		if	TEST
		print	'PATCH 7 reached'
		endif
		lda	inhinbit	; is PIP copying single files ?
		ora	a
		jnz	patch7$end	; no: do nothing

		lda	byte$count
		sta	attr$fcb+32	; set byte count into attribute fcb
		lxi	h,attr$fcb+6
		mvi	a,10000000b
		ora	m
		mov	m,a

		lxi	h,attr$fcb+11	; set archive bit?
		lda	archive$flag
		ani	10000000b
		ora	m
		mov	m,a

		call	clear$vars
		call	rest$date

patch7$end:	db	01h,25h,23h	; old code at patch$loc7
		jmp	patch$loc7+3


;*******************************************************************************
;
;		change system date$time :

		cseg
set$date:	lhld	1		; get address of BIOS jump table
		lxi	d,75		; offset of TIME jump entry
		dad	d
		mvi	a,JMP
		cmp	m		; test entry (must be JMP)
		rnz			; if not do nothing

		mvi	m,RET		; patch with RET opcode

		lxi	d,udate$pb	; set new date&time stamp
		mvi	c,49
		call	next
		
		lxi	d,utime$pb
		mvi	c,49
		call	next

		lxi	d,usec$pb	; seconds are zero
		mvi	c,49
		call	next

		mvi	a,TRUE
		sta	date$changed		
		if	TEST
		print	'SYSTEM date&time changed'
		endif
		ret

;*******************************************************************************
;
;		restore system date$time :

		cseg
rest$date:	lda	date$changed	; was it changed?
		ora	a
		rz			; no : do nothing
		
		lhld	1		; get address of BIOS jump table
		lxi	d,75		; offset of TIME jump entry
		dad	d
		mvi	m,JMP		; restore JMP entry
		
		mvi	a,FALSE
		sta	date$changed		
		if	TEST
		print	'SYSTEM date&time restored'
		endif
		ret


;*******************************************************************************
;
;		clear RSX variables :

		cseg
clear$vars:	xra	a
		sta	byte$count
		sta	archive$flag
		sta	update$date
		sta	update$date+1
		sta	source$fcb+32
		ret


;*******************************************************************************
;
; 		translate char in <A> to upper case. uses <AF>

		cseg
UPCASE:		cpi	'a'		; < 'a' ?
		rc			; no translation performed
		cpi	'z'+1		; > 'z' ?
		rnc			; no translation performed
		sui	'a'-'A'		; lower -> upper
		ret


;*******************************************************************************
;
;		data area :

		dseg
inhinbit:	db	TRUE		; is TRUE if PIP changes file contents
byte$count:	db	0		; of source file
archive$flag:	db	FALSE		; of source file
date$changed:	db	FALSE		; is TRUE if system date&time is changed

int$fcb:	ds	36		; copy of source file fcb

udate$pb:	db	58h		; parameter blocks for file date&time
		db	0FEh
update$date:	dw	0

utime$pb	db	5Ah
		db	0FEh
update$time:	dw	0

usec$pb:	db	5Ch
		db	0FFh
		db	0


;*******************************************************************************

		if	TEST
		cseg
pstring:	mov	a,m
		inx	h
		ora	a
		rz
		call	pbyte
		jmp	pstring

		cseg
pbyte:		push	h
		push	b

		ani	7FH
		mov	c,a
		lhld	1
		lxi	d,12
		dad	d
		call	ipchl

		pop	b
		pop	h
		ret

ipchl:		pchl
		
		endif

		end
