;
; WSPAT-DM.ASM
;
; Combines WSPATCH.ASM and WS-ZCPR.ASM into one handy patch file
;
; WSPATCH.ASM allows WordStar to use the ZCPR3 TCAP segment to achieve
; "terminal independence" (In other words, no need to patch it every time
; you use change terminals).
;
; WS-ZCPR.ASM allows WordStar to look for the *.OVR files on another user
; area than the current one.  Currently this is set for A15:.
;
; I have minimized any changes or deletions to these files, but some small
; changes were required to WS-ZCPR.ASM in order to relocate it.  The
; length of the SAVE command used after the .HEX patch file is read is
; currently 7Ah records (remember, ZCPR3 understands hex parameters for the
; SAVE command).
;
; Thanks for writing these patches, guys.  They sure take a lot of the
; headaches out of using WordStar!
;
; -David McCord  1-27-85

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;						;
;	WSPATCH.ASM				;
;						;
;	Patch for Wordstar 3.0 running under	;
;	The Echelon 'Z' System.  Modifies	;
;	Wordstar to use the current terminal	;
;	characteristics defined in Z3TCAP.	;
;						;
;	Copyright (c) 1985 by Dennis Wright	;
;	and Echelon, Inc.			;
;						;
;	Contains selected routines from 	;
;	Echelon's VLIB.				;
;						;
;	VLIB is Copyrighted by Richard Conn	;
;	and Echelon Inc.			;
;						;
;	Note: It may be possable to install	;
;	this patch on other versions of Word-	;
;	Star if the equates below are changed	;
;	to agree with versions' addresses.	;
;						;
;	    WordStar is a trademark of		;
;	MicroPro International Corporation	;
;						;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
	MACLIB	Z3BASE
;
;	WordStar 3.0 USER1 Patch Addresses from Installation manual.
;
TNAME	EQU	0190H		; Terminal name (not documented in manual).
HITE	EQU	0248H		; Number of lines.
WID	EQU	0249H		; Number of columns.
ERAEOL	EQU	026DH		; Erase to end of line.
LINDEL	EQU	0274H		; Delete line.
LININS	EQU	027BH		; Insert line.
IVON	EQU	0284H		; Highlighting on.
IVOFF	EQU	028BH		; Highlighting off.
DELMIS	EQU	02AFH		; Delay for non-cursor set functions.
TRMINI	EQU	0292H		; Terminal init string.
TRMUNI	EQU	029BH		; Terminal de-init string.
UCRPOS	EQU	0264H		; User cursor positioning routine.
INISUB	EQU	02A4H		; User init routine.
MORPAT	EQU	02E0H		; WordStar user patch area.
PBGMEM	EQU	035CH		; Contains beginning of text buffer address.
OCHAR	EQU	0267H		; Contains address of WordStar outchr routine.
;
;	The following equate is the base address of the patch routines.
;
PATCH	EQU	7849H		; Get this address from original PBGMEM.
;
	ORG	UCRPOS		; User-patchable cursor positioning routine.
	JMP	XYPOS		; Use VLIB cursor positioning routine.
;
	ORG	INISUB		; User-patchable init subroutine.
	JMP	INIT		; Use patch init routine.
;
	ORG	MORPAT		; WordStar user patch area.
INIT:	LXI	H,Z3ENV		; Point to 'Z' System environment descriptors.
	LXI	D,80H
	SHLD	ENVPTR		; Save pointer.
	PUSH	H		; Save for later.
	DAD	D		; Displace to Z3TCAP.
	SHLD	VIDPTR		; Save pointer.
	LHLD	OCHAR		; Address of WS outchr routine is kept here.
	SHLD	COUT1+1		; Inline modify patch cout to use WS outchr.
	POP	H		; Restore env pointer.
	LXI	D,47		; Offset to Z3TCAP crt select byte.
	DAD	D
	MOV	A,M		; Get it.
	LXI	D,2		; Set offset to crt 0 data area.
	ORA	A		; Crt 0?
	JZ	GETCRT		; Yes move crt 0 data to WS patch area.
	LXI	D,5		; No, move crt 1 data.
GETCRT:	DAD	D		; Offset to selected Z3TCAP crt data area.
	MOV	A,M		; Get Z3TCAPs terminal width.
	STA	WID		; Patch WS width.
	INX	H		; Bump to Z3TCAPs number of lines.
	MOV	A,M		; Get number of lines.
	STA	HITE		; Patch WS hite.
	XRA	A		; Clear following WS patchs.
	STA	LINDEL		; Line delete
	STA	LININS		; ..and line insert (we don't support these).
	LHLD	VIDPTR		; Get Z3TCAP pointer.
	LXI	D,22		; Offset to CE delay.
	DAD	D
	MOV	A,M		; Get CE delay.
	STA	DELMIS		; Patch WS misc delay.
	INX	H
	CALL	VIDSKP		; Skip past CL string.
	CALL	VIDSKP		; Skip past CM string.
	LXI	D,ERAEOL	; Point to WS eol string.
	CALL	FILL		; Replace with Z3TCAPs eol string.
	LXI	D,IVON		; Point to WS turn on highlighting.
	CALL	FILL		; Replace with Z3TCAPs string.
	LXI	D,IVOFF		; Point to WS turn off highlighting.
	CALL	FILL		; Replace with Z3TCAPs string.
	LXI	D,TRMINI	; Point to WS terminal init string.
	CALL	FILL		; Replace with Z3TCAPs string.
	LXI	D,TRMUNI	; Point to WS terminal de-init string.
	CALL	FILL		; Replace with Z3TCAPs string.
	CALL	NAMIT		; Replace WS terminal name with Z3TCAPs.
	RET			; We're done.
;
	ORG	PBGMEM		; The beginning of the text memory is
	DW	BEGMEM		; ..moved up to accommidate our patch.
;
	ORG	PATCH		; Extended patch area.
NAMIT:	LHLD	VIDPTR		; Point to Z3TCAPs terminal name
	LXI	D,TNAME		; ..and WS terminal name.
	MVI	C,16		; Move our 16 bytes.
NFILL:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	NFILL
	XCHG
NFILL2:	MOV	A,M		; Pad out the remainder with spaces.
	CPI	0FH		; Till this.
	RZ
	ORA	A		; Or zero.
	RZ
	MVI	A,20H
	MOV	M,A
	INX	H
	JMP	NFILL2
;
;	This routine moves Z3TCAP data into the WordStar patch area.
;	Because WordStar requires the number of bytes for each entry
;	we must count the bytes we patch and place the number at the
;	beginning of the patch.
;
FILL:	PUSH	D
	MVI	C,-1		; Set byte counter.
FIL1:	INX	D
	MOV	A,M		; Replace WS data with Z3TCAPs.
	STAX	D
	INX	H
	INR	C
	ORA	A		; Reached end?
	JNZ	FIL1
	POP	D		; Yes, restore pointer to first byte.
	MOV	A,C		; Insert number of bytes in string.
	STAX	D
	RET
;
COUT:	PUSH	H
	PUSH	D
COUT1:	CALL	0000H		; This is inline modified by the INIT.
	POP	D		; Routine to call WordStars outchr
	POP	H		; ..routine.
	RET
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;	The following routines are from VLIB by Richard Conn.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; GOTO XY	(From VLIB)
;	HL = Row/Col, with Home=0/0
;	Return with A=0 and Zero Flag Set if not done
;
XYPOS:	MOV	D,L		; Swap row & column
	MOV	E,H		; ..for vlib
	XCHG
GOTOXY:	PUSH	B		; Save regs
	PUSH	D
	PUSH	H
	LHLD	VIDPTR		; Pt to environment
	MOV	A,M		; No terminal?
	CPI	' '+1
	JC	ERR
	LXI	D,15H		; Pt to CM delay
	DAD	D
	MOV	A,M		; Get it
	STA	CMDELAY		; Save it
	INX	H		; Pt to CL string
	INX	H
	CALL	VIDSKP		; Skip CL string
	MOV	A,M		; Get first char of CM string
	ORA	A		; If no string, error
	JZ	ERR
	XCHG			; DE=address of CM string
	POP	H		; Get coordinates in HL
	PUSH	H
	CALL	GXY		; Output xy string with delay
	LDA	CMDELAY		; Pause
	CALL	VIDELAY
	POP	H		; Done
	POP	D
	POP	B
	XRA	A		; Return NZ
	DCR	A
	RET
ERR:	POP	H		; Done
	POP	D
	POP	B
	XRA	A		; Return Z
	RET
;
; GOTOXY	(From VLIB)
;   On input, H=Row and L=Column to Position To (0,0 is Home)
;   On input, DE=address of CM string
;
GXY:	XRA	A		; Set row/column
	STA	RCORDER		; Row before column
	STA	RCBASE		; Add 0 to base
;
; Cycle thru string
;
GXYLOOP:LDAX	D		; Get next char
	INX	D		; Pt to next
	ORA	A		; Done?
	RZ
	CPI	'%'		; Command?
	JZ	GXYCMD
	CPI	'\'		; Escape?
	JZ	GXYESC
	CALL	COUT		; Send char
	JMP	GXYLOOP

;
; Escape - output following byte literally
;
GXYESC:	LDAX	D		; Get next char
	CALL	COUT		; Output literally
	INX	D		; Pt to next
	JMP	GXYLOOP
;
; Interpret next character as a command character
;
GXYCMD:	LDAX	D		; Get command char
	INX	D		; Pt to next
	CPI	'd'		; %d
	JZ	GXYOUT1
	CPI	'2'		; %2
	JZ	GXYOUT2
	CPI	'3'		; %3
	JZ	GXYOUT3
	CPI	'.'		; %.
	JZ	GXYOUT4
	CPI	'+'		; %+v
	JZ	GXYOUT5
	CPI	'>'		; %>xy
	JZ	GXYGT
	CPI	'r'		; %r
	JZ	GXYREV
	CPI	'i'		; %i
	JZ	GXYINC
	CALL	COUT		; Output char if nothing else
	JMP	GXYLOOP
;
; Set row/col home to 0,0
;
GXYINC:	MVI	A,1		; Set rcbase to 1
	STA	RCBASE
	JMP	GXYLOOP
;
; Reverse order of output to column then row (default is row then column)
;
GXYREV:	MVI	A,1		; Set column and row order
	STA	RCORDER
	JMP	GXYLOOP
;
; Command: >xy
;   If value of row/col is greater than x, add y to it
;
GXYGT:	CALL	GETVAL		; Get value
	MOV	C,A		; Save value
	LDAX	D		; Get value to test
	INX	D		; Pt to next
	CMP	C		; If carry, value>x
	JNC	GXYGT1
	LDAX	D		; Get value to add
	ADD	C
	CALL	PUTVAL		; Put value back
GXYGT1:	INX	D		; Pt to next
	JMP	GXYLOOP		; Resume
;
; Command: +n
;   Add n to next value and output
;
GXYOUT5:LDAX	D		; Get value to add
	INX	D		; Pt to next
	MOV	B,A		; Save in B
	CALL	GETVAL		; Get value
	ADD	B		; Add in B
	CALL	COUT		; Output value
RCMARK:	LDA	RCORDER		; Mark output
	ORI	80H
	STA	RCORDER
	JMP	GXYLOOP
;
; Command: .
;   Output next value
;
GXYOUT4:CALL	GETVAL		; Get value
	CALL	COUT		; Output value
	JMP	RCMARK
;
; Command: 3
;   Output next value as 3 decimal digits
;
GXYOUT3:CALL	GETVAL		; Get value
	MVI	B,100		; Output 100's
	MVI	C,1		; Leading zeroes
	CALL	DIGOUT
GXYOT3:	MVI	B,10		; Output 10's
	MVI	C,1		; Leading zeroes
GXYOT2:	CALL	DIGOUT
	ADI	'0'		; Output 1's
	CALL	COUT
	JMP	RCMARK
;
; Command: 2
;   Output next value as 2 decimal digits
;
GXYOUT2:CALL	GETVAL		; Get value
	JMP	GXYOT3
;
; Command: d
;   Output next value as n decimal digits with no leading zeroes
;
GXYOUT1:CALL	GETVAL		; Get value
	MVI	B,100		; Output 100's
	MVI	C,0		; No leading zeroes
	CALL	DIGOUT
	MVI	B,10		; Output 10's
	MVI	C,0		; No leading zeroes
	JMP	GXYOT2
;
; Return next value in A
;
GETVAL:	LDA	RCORDER		; Get order flag
	ORA	A		; Already output the first value?
	JM	GETVAL2
	ANI	1		; Look at lsb
	JZ	GETVALR		; If 0, row first
GETVALC:LDA	RCBASE		; Get base offset
	ADD	L		; Get column
	RET
;
GETVALR:LDA	RCBASE		; Get base offset
	ADD	H		; Get row
	RET
;
GETVAL2:ANI	1		; Look at lsb
	JZ	GETVALC
	JMP	GETVALR
;
; Store A as next value
;
PUTVAL:	MOV	C,A		; Save value
	LDA	RCORDER		; Get order flag
	ORA	A		; Already output the first value?
	JM	PUTVAL2
	ANI	1		; Look at lsb
	JZ	PUTVALR		; If 0, row first
PUTVALC:MOV	L,C		; Set column
	RET
;
PUTVALR:MOV	H,C		; Set row
	RET
;
PUTVAL2:ANI	1		; Look at lsb
	JZ	PUTVALC
	JMP	PUTVALR
;
; Output A as decimal digit char
;   B=Quantity to Subtract from A, C=0 if no leading zero
;
DIGOUT:	PUSH	D		; Save DE
	MVI	D,'0'		; Char
DECOT1:	SUB	B		; Subtract
	JC	DECOT2
	INR	D		; Increment char
	JMP	DECOT1
;
DECOT2:	ADD	B		; Add back in
	PUSH	PSW		; Save result
	MOV	A,D		; Get digit
	CPI	'0'		; Zero?
	JNZ	DECOT3
	MOV	A,C		; Get zero flag
	ORA	A		; 0=no zero
	JZ	DECOT4
DECOT3:	MOV	A,D		; Get digit
	CALL	COUT		; Print it
DECOT4:	POP	PSW		; Get A
	POP	D		; Restore DE
	RET
;
; GXY Buffers
;
RCORDER:DS	1		; 0=row/col, else col/row
RCBASE:	DS	1		; 0=org is 0,0, else org is 1,1
CMDELAY:DS	1		; Number of milliseconds to delay for CM
;
;	VIDELAY pauses for the number of milliseconds indicated by the A
; register.  VIDELAY assumes a ZCPR3 environment and uses it to determine
; processor speed.
;
VIDELAY:PUSH	PSW		; Save regs
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A		; Save count in C
	ORA	A		; No delay?
	JZ	DONE
	LHLD	ENVPTR		; Pt to environment
	LXI	D,2BH		; Offset to processor speed
	DAD	D
	MOV	A,M		; Get processor speed
	ORA	A		; Zero?
	JNZ	VID1
	MVI	A,4		; Assume 4 MHz
VID1:	MOV	B,A		; Processor speed in B
VID2:	PUSH	B		; Delay 1 ms
	CALL	DELAY
	POP	B
	DCR	C		; Count down
	JNZ	VID2
DONE:	POP	H		; Restore regs
	POP	D
	POP	B
	POP	PSW
	RET
;
;  Delay 1 ms at Clock speed
;
DELAY:	CALL	DEL1		; Delay 1 ms at 1MHz
	DCR	B		; Count down clock speed
	JNZ	DELAY
	RET
;
;  Delay 1 ms at 1MHz
;
DEL1:	MVI	C,20		; 20 loops of 51 cycles each ~ 1000 cycles
DEL1A:	XTHL			; 18 cycles
	XTHL			; +18 = 36 cycles
	DCR	C		; + 5 = 41 cycles
	JNZ	DEL1A		; +10 = 51 cycles
	RET
;
;  VIDSKP - Skip over video string pted to by HL; pt to byte after string
;
VIDSKP:	MOV	A,M		; Get next char
	INX	H		; Pt to next
	ORA	A		; Done if zero
	RZ
	CPI	'\'		; Literal value?
	JNZ	VIDSKP		; Continue if not
	INX	H		; Pt to after literal value
	JMP	VIDSKP
;
;  ZCPR3 Pointer save area
;
VIDPTR:	DS	2		; First byte of termcap entry
ENVPTR:	DS	2		; 'Z' System environment package.
;

;******************************************************************************
;
	title		'ZCPR II  patches for Wordstar Overlay Files' 
;
;******************************************************************************
;
;		WS.COM can be made to work with ZCPR II		04/17/84
;
;  You know how irritating the @@@@'s get when you try to open a file in a 
; user area that does not have the *.OVR files present ?????  I had been 
; pipping everything to a given user area to edit and then back again. ARRGH.
; To make a long story short, a few hours with DDT revealed several key areas
; within WS.COM that should interest a true hacker. Seems as how the FCB for
; WSOVLY1.OVR is at 364Dh, WSMSGS.OVR is at 3784h, the only BDOS entry vector
; is at 1823h and finally, the area between 02E0h and 035Bh inclusive is a 
; safe patch area. Well, well well....... A real simple kludge of mushware will
; set a default drive and user area every time WS.COM looks for an *.OVR file
; and then set it back again after the DOS operation. The infernal thing can 
; find the *.OVR files from any drive and user area A dream come true and
; it is really painless......
;									
;
;//////////////////////////////////////////////////////////////////////////////
;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;
;
;		Copyright (c) 1984 .... Oak Brook Design 
;   
;	  		     All rights reserved.
;
;	Permission  is  hereby  granted  to copy and distribute this
;	program  for  any  non-commercial  purpose. Any use of this
;	material  for  commercial  advantage  without  prior written
;	consent of John P Sojak acting on behalf of Oak Brook Design 
;	is prohibited. 
;
;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
;//////////////////////////////////////////////////////////////////////////////
;
;	You MUST examine your Wordstar under SID or DCON to verify several
;	locations within the memory image.... > denotes a console input
;
;
;>	A0>DCON
;
;	--- dcon banner and some other gook ---
;
;>	#F100 5000 00		; clear the ram out
;>	#IWS.COM		; set up for read
;>	#R	
;
;>	#P0000			; set a break at warmboot 
;>	#G100
;
; 	--- you will get the usual WS menu.... Just eXit with 'X'
;
;	01 PASS 0000
;	 -Z-E- A=00 B=1800 D=0006 H=3441 S=3443 P=0000	
;	 ----- A'=00 B'=00 D'0000 H'0000 X=0000 Y=0000 JMP XXXX
;	*XXXX
;
;>	#D3780 3790
;	3780: 00 00 00 00 03 57 53 47 53 20 20 4F 56 52 .....WSMSGS  OVR
;	3790: 00	   \	
;		             there it is... 
;				
;					          here it is... 	    	
;>	#D3640 3660				/   
;	3640: 00 00 00 00 00 00 00 00 00 00 00 00 57 53 ..............WS
;	3650: 4F 56 4C 59 31 20 4F 56 52 00 00 00 00 00 OVLY1 OVR.......
;	3660: 00 . 	
;
;
;>	#L1820
;	1820   	INX  	H
;	1821	PUSH 	D	
;	1822	PUSH	H
;	1823	CALL	0005	--- here is the DOS entry @ 1823h  
;	1826	POP	H
;	1827	POP	D
;	1829	XTHL
;	182A	RET
;
;>	#-P		; clear the pass point at warmboot
;>	#G0		; and exit to CCP
;
;	A0>
;
;	Now you can assemble the file, I used Mac. M80 / L80 has a problem with
;	extranious 00 between the two ORGs. Dont use it. 
;
;	Use the same procedure to load WS.COM into memory again but this time,
;	dont set any pass point at 0000. Set up for the read with IWSUSER.HEX
;	and then do a R to overlay the hex file. Exit to the CCP and then
;	
;	A0>SAVE 64 WS-TEST.COM
;
;	Thats it..... WS  will, or should now, work with all user areas and 
;	drives.
;	
;
;******************************************************************************
;

bdos		equ	0005	; DOS entry

;
;==============================================================================
; The WSMSGS.OVR fcb... 

msgfcb		equ	3784h

;==============================================================================
; The WSOVR1.OVR fcb... 

ovrfcb		equ	364Dh	


;==============================================================================
;
;	set these to your desired default drive and user for the *.OVR files

ovruser		equ	15	; User area...  0-31
ovrdrv		equ	01	; Drive Spec... A:-> 01, B:-> 02 etc

;
;******************************************************************************
;
;			the patcher starts here
;
;******************************************************************************
;

bdosvec		equ	$
;	intercept the call to DOS before actual entry ... 

		mov	a,c
		sta	nowfun
		cpi	15		; open file
		jz	chkit
		cpi	16		; close
		jz	chkit
		cpi	17		; search first
		jz	chkit
		cpi	18		; search next
		jz	chkit
		cpi	20		; read next
		jz	chkit
		cpi	33		; read random
		jz	chkit

;	pass the call on without further hassle, its not for us

passit:		jmp	bdos


;******************************************************************************
;
;	check <de> for the two OVR fcb addresses, set <cy> if not found
;

chkit:		mvi	a,(LOW msgfcb)	;  fcb for WSMSGS.OVR 
		cmp	e
		jnz	nogood
		mvi	a,(HIGH msgfcb)
		cmp	d
		jz	good
		
nogood:		mvi	a,(LOW ovrfcb)	;  fcb for WSOVLY1.OVR 
		cmp	e
		jnz	bdos
		mvi	a,(HIGH ovrfcb)
		cmp	d
		jnz	bdos

;	found one of the FCB's... set the user and drive 

good:		push	d	; save the FCB

		mvi	c,32	; get the current user number
		mvi	e,0FFh
		call	bdos
		ani	00111111b
		sta	user

		mvi	c,32		
		mvi	e,ovruser	; find the overlays in this user
		call	bdos
		pop	d

;	now fix the drive specification

		ldax	d
		sta	nowdrive
		mvi	a,ovrdrv	; find the overlays on this drive
		stax	d	

		lda	nowfun		; original function call
		mov	c,a
		push	d
		call	bdos	
		pop	d
		
		push	psw		; save the result flags for later
		lda	nowdrive
		stax	d

		mvi	c,32		; set the old user... maybe funnies
		lda	user
		mov	e,a
		push	d		; save original FCB ...
		call	bdos
		pop	d		; ... restore 
		lda	nowfun
		mov	c,a		; restore c
		pop	psw		; get error flags
		ret			; back to Wordstar


user:		db	15
nowdrive:	db	00		
nowfun:		db	00

	BEGMEM	EQU	$
;
		org	1823h	; remove the existing DOS hook
		call	bdosvec

	END
