;**********************************************************
;							  *
;	          EXTENDED CPM-- 12/06/81		  *
;	  DESIGNED TO BE PORTABLE TO ANY CP/M 2.2	  *
;       SYSTEM THAT IS IN ORIGINAL 8080 CODE FOR CCP.	  *
;	(will work with others also, but you will	  *
;	have to find all of the CCP equates.)		  *
;			 RLP				  *
;							  *
;		will assemble w/ASM.COM			  *
;							  *
;	Donated 11/22/81 for the benefit of hobby	  *
;	computing, but may not be sold or copied	  *
;	for a profit.					  *
;							  *
MSIZE	EQU	61		;MEMORY SIZE THIS VERS	  *
				;CHANGE TO YOUR SIZE HERE *
OVERLAY	EQU	OVERLAY		;COMPUTED SYSGEN OVERLAY  *
;							  *
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;						>>
;	*** I M P O R T A N T ***		>>
;						>>
; You must obtain the address for CBOOT from	>>
; the argument of the first jump at the		>>
; beginning of YOUR BIOS and place it in the 	>>
; conditional equate assembled for your system.	>>
; Make it relative to the beginning of the	>>
; BIOS as shown.				>>
;						>>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;
;**********************************************************
;
;		   XTCPM.ASM  VERS 1.41
;
;	THIS PROGRAM PROVIDES FOR AN EXPANSION OF YOUR
;	SYSTEM BY EITHER ONE OR 2 KBYTES WITHOUT THE NEED
;	FOR A SPECIAL SYSGEN.  JUST ADD THIS CODE TO YOUR
;	SYSTEM BY OVERLAYING YOUR CPMxx.COM FILE USING DDT.
;	AFTER YOU HAVE OVERLAYED THE CODE INTO CPMxx.COM
;	WITH DDT, DO TWO SAVES. ONE, SAVE YOUR CPMxx.COM
;	FILE AS USUAL. TWO, DO A SAVE OF THE CODE WHICH
;	WILL BE IN THE IMAGE AT 100H. THIS WILL BE A TRAN-
;	SIENT THAT YOU MUST KEEP ON YOUR 'A' DRIVE AND CON-
;	TAINS THE ADDITIONAL CODE - SO SAVE IT WITH ENOUGH
;	SECTORS TO WRITE 1 OR 2 KB DEPENDING ON THE SIZE.
;	NAME THE TRANSIENT 'XTCPM.COM'. BE SURE THAT YOUR
;	BIOS IS SET TO DO A TURNKEY START BY HAVING IT
;	JMP TO THE FIRST ENTRY POINT (CCP+0H) AFTER BIOS
;	INITIALIZATION.  SOME PROPRIETARY BIOS'S DO THIS
;	THROUGH A MODE BYTE. FINALLY USE YOUR NORMAL 
;	'SYSGEN' TO WRITE THE CPMxx.COM TO YOUR SYSTEM
;	TRACKS. IT IS PREFERABLE THAT YOUR BIOS DO A JUMP
;	TO THE SECOND ENTRY POINT IN THE CCP (CCP+3H) ON
;	A WARM BOOT, HOWEVER THIS CODE WILL PROPERLY HANDLE
;	THE SITUATION EVEN IF YOUR BIOS IS NOT IMPLEMENTED
;	THAT WAY.
;
;	THE EXPANDED CODE SHOWN HERE PROVIDES FOR AN
;	EXTENDED CCP THAT CAN CONTAIN ADDITIONAL USER
;	DEFINED CCP COMMANDS AS WELL AS TO MAKE TRANSIENTS
;	APPEAR AS CCP-LIKE IN THAT THEY CAN BE INVOKED
;	REGARDLESS OF LOGGED DRIVE/USER AREA. THUS IT IS
;	COMPLETELY UN-NECESSARY TO PLACE THESE TRANSIENTS
;	IN MULTIPLE USER AREAS.  IT ALSO PROVIDES FOR
;	TWO LEVELS OF PASSWORD PROTECTION.  ADD A TURNKEY
;	START OF WARD'S 'BYE' W/O PASSWORD TO MAKE A VERS-
;	ATILE ON-LINE SYSTEM. JUST PUT 'BYE' AT 'FILNAM'
;	PRECEEDED BY A '3' AND ADD A TEMINATING NULL.
;	THEN PLACE 'BYE.COM' IN USER 13 ON YOUR 'A' DRIVE
;	AND SET TNKY2 EQUAL TO TRUE.
;
;	AS DISTRIBUTED HERE, ALL OF THE FEATURES ARE
;	AVAILABLE WITH LESS THAN ONE KILOBYTE OF SPACE ABOVE
;	CP/M. THE CODE REQUIRES A Z80 CPU BUT MAY BE
;	CONVERTED TO 8080 OR 8085. STUFF THE HOLES W/CARE.
;
;	TESTED OK WITH LIFEBOAT CP/M FOR NORTH STAR AND
;	NORTH STAR'S CP/M PRODUCT. IF YOU ARE USING THE
;	NORTH STAR CP/M WITH HARD DISK OR NON-STANDARD
;	PROM, BE CAREFUL NOT TO OVERWRITE THE LAST 80H
;	OF THE USER AREA CODE STARTING AT 3380H IN IMAGE.
;	ALSO BE SURE TO SAVE 54 FOR A CPMXX.COM FILE SINCE
;	THE IMAGE STARTS HIGHER THAN THE USUAL STANDARD.
;
;	ALSO TESTED WITH A STANDARD CP/M USING 8 IN DISKS
;	AND A MICROMATION DOUBLER.
;
;	YOU CAN APPEND YOUR OWN CUSTOMIZED VERSION OF 'BYE'
;	WITHOUT A MOVER INTO THE USER2 AREA IF YOU ARE CARE-
;	FUL TO AVOID LABEL CONFLICTS. IF YOU DO SO, THEN ADD
;	A COMMAND STRING AT THE 0 PASSWORD LEVEL AND POINT
;	TO THE ADDRESS OF YOUR 'BYE' ENTRY POINT. ALSO,
;	REDIRECT THE STACK IN 'BYE' TO BE THE SAME AS THE
;	STACK IN THIS CODE AND ELIMINATE THE SEPARATE STACK.
;	ALSO SEE THE NOTE AT LABEL CCPINIT.
;
;	NOTE....THIS PROGRAM DOES NOT CLOBBER ANYTHING IN THE
;	TPA EXCEPT WHEN LOADING 'BYE' AS A TRANSIENT WHICH
;	CAN BE AVOIDED AS ABOVE.
;
;	YOU CAN ELIMINATE THE USER1 CODE AREA IN ITS ENTIRETY
;	IF YOU ARE ALREADY HAPPY WITH YOUR I/O DRIVERS. THERE
;	IS A CONDITIONAL ASSEMBLY EQUATE TO ACCOMPLISH THIS.
;
;	12/6/81 Fixed warm boot branch address in CCPINIT.
;	Added second DIR string and address so that CCP code
;	for DIR will run at 0 password level and a DIR.COM
;	transient will run above that level if it is on the
;	system disk.  See NOTE just above label DIR1.
;	Changed location of PASBYTE vector to the buffer at
;	the end of the BDOS (spare location) so that if the
;	CCP is crashed by DDT or anything else, BYE will still
;	be able to find the PASBYTE to reset it. Former loc-
;	ation of this vector was at a spare location in the CCP.
;	Clarified USER2 equate for IF STDCPM.   RLP
;
;	12/4/81 Major revision, see the DOC file. Also
;	cleaned up the code.			RLP
;
;	11/30/81 Added code to make USERs 13, 14, & 15 on 'A'
;	universal depending on password level achieved at
;	log-on time. Removed nested IF's. Cleaned up code so
;	that a warm boot will properly initialize the CCP.
;	Also removed bug that would have prevented a file
;	from being executed by a transient that loads the
;	command line, sets the buffer pointer, and jumps to
;	the beginning of the CCP.		RLP
;
;	11/25/81 Added conditional assembly equate to
;	allow removal of i/o drivers and added routines
;	to link the code in USER2 to your own customized
;	BIOS.					RLP
;	
***********************************************************
;
;		SYSTEM EQUATES
;	IMPORTANT   You must check these system equates to
;be sure that they are valid for your system and change them
;if they are not.  You may choose to integrate the USER1 code
;with your open BIOS if you have one and thus eliminate the
;USER1 equate.  USER2 will usually be just above CP/M including
;any buffer and will usually be MSIZE*1024+32. 32 is to allow
;room for stack. Change the value of ADJUST until the values
;for CCP, BDOS and BIOS are correct for your system. The value
;0D00H for Lifeboat on North Star and for North Star CP/M is
;correct, and the value 600H is correct for standard CP/M.
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE	;SEE OPTIONS BLOCK
				;TO SELECT ALL OPTIONS
;
IOBYTE	EQU	0003H		;I/O BYTE ADDRESS
DRIVE	EQU	0004H		;LOC OF CURR DRV BYTE
STAKSIZ	EQU	32		;15 LEVEL STACK + BOOT FLAG
				;AND PASBYTE
;
;		- - - - - - - - - - - - -
;
;select only one of these true except that if Lifeboat BIOS
;then make decision on S22X
;
STDCPM	EQU	FALSE	;true if standard CP/M on IBM
			;compatible floppy disk.
NSTRCPM	EQU	FALSE	;true if North Star CP/M
LIFBOAT	EQU	TRUE	;true if Lifeboat CP/M for North
			;Star DQ.
S22X	EQU	TRUE	;TRUE FOR 2.21A & 2.22. THIS IS A
			;SPECIAL EQUATE FOR LIFEBOAT BIOS'S
			;AND ANY OTHERS THAT HAVE ADDED AN
			;ADDITIONAL JMP FOR WINIT IN THE 
			;USER1 JUMP TABLE. SET TO FALSE
			;FOR ALL OTHERS. BE SURE THAT YOUR
			;BIOS JMP TABLE IS PROPERLY LINKED
			;TO USER1 OR JUST FORGET IT IF YOU
			;INTEGRATE THIS CODE INTO YOUR 
			;OPEN BIOS.
M2BIOS	EQU	FALSE	;true if micromation bios
			;
;		- - - - - - - - - - - -
			;
;
SKIP	EQU	0	;EQUATE THIS TO SIZE OF ROM TO SKIP
	;NOTE:  This will add an additional offset to the
	;	USER2 equate if you wish to place the USER2
	;	code above any PROM or ROM areas. Thus, it
	;	will be unecessary to reserve space between
	;	the top of CP/M and ROM
	;
	IF	LIFBOAT
	;
ADJUST	EQU	0D00H
BIOS	EQU	(MSIZE*1024)-ADJUST	;BASE OF BIOS
USER1	EQU	BIOS+0700H		;BASE 1RST USER AREA
USER2	EQU	MSIZE*1024+STAKSIZ+SKIP	;BASE 2ND USER AREA
RAMEND	EQU	0FC00H			;CHANGE TO YOUR VALUE
CBOOT	EQU	BIOS+80H		;COLDBOOT VECTOR FOR 2.22
CCPIMAGE EQU	0A00H			;CHANGE THIS TO EQUAL THE
					;ADDRESS OF YOUR CCP IN
					;THE DDT/SYSGEN IMAGE.
OVERLAY	EQU	2000H-BIOS		;COMPUTED OVERLAY
	;
	ENDIF
	;
	IF	NSTRCPM
	;
ADJUST	EQU	0D00H
BIOS	EQU	(MSIZE*1024)-ADJUST	;BASE OF BIOS
USER1	EQU	BIOS+0700H		;BASE 1RST USER AREA
USER2	EQU	MSIZE*1024+STAKSIZ+SKIP	;BASE 2ND USER AREA
RAMEND	EQU	0FC00H			;CHANGE TO YOUR VALUE
CBOOT	EQU	BIOS+0xxH
CCPIMAGE EQU	1500H
OVERLAY	EQU	2B00H-BIOS		;COMPUTED OVERLAY
	;
	ENDIF
	;
	IF	STDCPM
	;
ADJUST	EQU	600H
BIOS	EQU	(MSIZE*1024)-ADJUST	;BASE OF BIOS
;USER1	EQU	BIOS + (YOUR OFFSET TO USER1 PATCH AREA) 
USER2	EQU	MSIZE*1024+STAKSIZ+SKIP	;*NOTE* IF YOUR BIOS CODE
					;INCLUDING ITS BUFFER SPACE
					;HAS SPACE LEFT BELOW
					;MSIZE*1024, YOU CAN SUBTRACT
					;THE AMOUNT OF SPACE YOU HAVE
					;LEFT FROM THIS USER2 EQUATE.
					;SEE THE CODE AT 'IF M2BIOS'
					;BELOW FOR AN EXAMPLE OF THIS. 
;RAMEND	EQU	(PUT YOUR VALUE HERE)
CBOOT	EQU	BIOS+0xxH
CCPIMAGE EQU	980H			;ADDR OF CCP IN DDT IMAGE
OVERLAY	EQU	1F80H-BIOS		;COMPUTED OVERLAY
	;
	ENDIF
	;
	IF	M2BIOS
	;This is for a particular user's CBIOS that uses a
	;Micromation Doubler and has a user code area begin-
	;ning at BIOS+23DH.  Make your own conditional ass-
	;embly equates for your values.
ADJUST	EQU	600H
BIOS	EQU	(MSIZE*1024)-ADJUST
USER1	EQU	BIOS+23DH		;BASE 1RST USER AREA
USER2	EQU	MSIZE*1024+STAKSIZ-256+SKIP ;BASE 2ND USER AREA
RAMEND	EQU	0F400H			;CHANGE TO YOUR VALUE
					;USED ONLY IN PARITY
					;SETTING CODE IN 'INITIAL'
CBOOT	EQU	BIOS+0ABH
CCPIMAGE EQU	980H			;ADDR OF CCP IN DDT IMAGE
OVERLAY	EQU	1F80H-BIOS		;COMPUTED OVERLAY
	;
	ENDIF
	;
BOOTFLG	EQU	USER2-1			;LOC OF BOOT FLAG
					;IF NOT 0, then a
					;cold boot is in progress.
PASBYTE	EQU	USER2-2			;LOC OF PASBYTE
STACK	EQU	USER2-2			;TOP OF STACK
CCP	EQU	BIOS-1600H		;BASE OF CCP
BDOS	EQU	CCP+800H		;BASE OF BDOS
;
;
IOBYT	EQU	00000001B	;VALUE OF IOBYTE
		; ; ; ;---------;CONSOLE IS TTY:	00
		; ; ;		;	    CRT:	01
		; ; ;		;	    BAT:	10
		; ; ;		;	    UC1:	11
		; ; ;-----------;READER IS  TTY:	00
	 	; ;		;	    RDR:	01 
		; ;		;	    UR1:	10 
		; ;		;	    UR2:	11 
		; ;-------------;PUNCH IS   TTY:	00 
		;		;	    PUN:	01 
		;		;	    UP1:	10 
		;		;	    UP2:	11  
		;---------------;LIST IS    TTY:	00 
				;	    CRT:	01 
				;	    LPT:	10 
				;-----------UL1:	11 
;
DRIVERS	EQU	TRUE	;WANT THE I/O DRIVERS? (CONTAINED
			;IN USER1 CODE AREA)
BGTST	EQU	TRUE	;WANT BACKGROUND MEMORY TEST?
;							   
PARITY	EQU	TRUE	;WANT TO SET PARITY MEMORY	   
;							   
TNKY2	EQU	TRUE	;WANT 2ND TURNKEY START
;
MAXUSER	EQU	2	;FOR EXAMPLE..MAX PUBLIC USER AREA 
;							   
SGNON	EQU	FALSE	;NEED SIGN ON MESSAGE?
;
PASBYT	EQU	00100100B	;PASSWORD BYTE	   
		  ;  ;----------;PASSWORD 2 REQUIRED	   
		  ;-------------;PASSWORD 1 REQUIRED	   
;							   
;END OF OPTIONS BLOCK					   
;***********************************************************
;***********************************************************
;
;		PORT ASSIGNMENTS
;
;Be sure to check port assignments for your system.
;
PARAL	EQU	0	;PARALLEL I/O PORT
SER1D	EQU	2	;LEFT SERIAL PORT DATA
SER1ST	EQU	3	;LEFT SERIAL PORT STATUS
SER2D	EQU	4	;RIGHT SERIAL PORT DATA
SER2ST	EQU	5	;RIGHT SERIAL PORT STATUS
MOTHER	EQU	6	;MOTHERBOARD COMMAND/STATUS
MEMORY	EQU	0C0H	;PARITY MEMORY PORT unique to
			;North Star
;
;**********************************************************
;START OF USER1 CODE
;
;		JUMP TABLE LINKS BIOS TO XTCPM
;		The rest of the jumps are at base
;		of the BIOS.
;
;NOTICE.  The IOBYTE processor in this code saves and re-
;stores BC, DE, and HL registers.
;
;**********************************************************
;
	IF	DRIVERS
	;
	ORG	USER1
	JMP	INITIAL		;USER INIT ROUTINE
	;
	ENDIF
	;
	IF	S22X
	;
	DS	3		;SPACE FOR JMP TO WINIT
				;USED BY 221A & HIGHER ONLY
	ENDIF
	;
	IF	DRIVERS AND NOT (STDCPM OR M2BIOS)
	;
	JMP	CONST		;CONSOLE STATUS ROUTINE
	JMP	CONIN		;CONSOLE INPUT
	JMP	CONOUT		;CONSOLE OUTPUT
	;
	ENDIF
	;
	IF	DRIVERS
;
;***********************************************************
;
;		IOBYTE PROCESSOR
;
;***********************************************************
;
CONSOL:
	MVI	A,CONCNT	;SHIFT COUNTER
;
DISPAT:	;I/O DISPATCHER - TO MAP LOGICAL TO PHYSICAL
	XTHL	 		;GET RETURN ADDR & SAVE HL
	PUSH	B		;SAVE BC
	PUSH	D		;SAVE DE
 	MOV	B,A		;PUT COUNTER VALUE IN B
	LDA	IOBYTE		;GET THE IO BYTE
	;
DISPAT1:
	DCR	B		;SHIFT IOBYTE UNTIL
DB	JRZ,	DISPAT2-$-1	;COUNT IN B=0
	RRC
	RRC
DB	JR,	DISPAT1-$-1 AND 0FFH ;SHIFT NEXT TWO BYTES
	;
DISPAT2:
	ANI	03H		;MASK OUT BITS 0,1
	RLC			;DOUBLE
	MVI	D,0		;CLEAR D
	MOV	E,A		;
	DAD	D		;H,L POINTS TO ENTRY
	MOV	A,M		;GET LOW BYTE
	INX	H		;POINT TO HIGH BYTE
	MOV	H,M		;GET HIGH BYTE
	MOV	L,A		;H,L POINTS TO SUBR.
	POP	D		;RESTORE DE
	POP	B		;RESTORE BC
	XTHL			;RSTR HL & PUT RET ON STACK
	RET
;
CONST:	;CONSOLE STATUS, RETURN 0FFH IF READY ,0 IF NOT
	CALL	CONSOL		;COMMON ROUTINE
	DW	TTYST		;TTY STATUS
	DW	CRTST		;CRT STATUS
	DW	RDRST		;READER STATUS 
	DW	UC1ST		;USER CONSOLE STATUS
;
CONIN:
	ENDIF
	;
	IF	BGTST
	;
	CALL	TEST		;BACKGROUND MEMORY TEST
	;
	ENDIF
	;
	IF	DRIVERS
	;
	CALL	CONST
	ORA	A		;SET CC
DB	JRZ,	CONIN-$-1 AND 0FFH
	;CONSOLE INPUT ROUTINE
	CALL	CONSOL		;COMMON CONSOLE ROUTINE
	DW	TTYIN		;TTY INPUT
	DW	CRTIN		;CRT INPUT
	DW	READER		;READER INPUT 
	DW	UC1IN		;USER CONSOLE INPUT
	;
CONOUT: ;CONSOLE OUTPUT ROUTINE
	CALL	CONSOL		;COMMON ROUTINE
	DW	TTYOUT		;TTY OUTPUT
	DW	CRTOUT		;CRT OUTPUT
	DW	LIST		;LIST OUTPUT 
	DW	UC1OUT		;USER CONSOLE OUTPUT
;
READER0:
	ENDIF
	;
	IF	BGTST
	;
	CALL TEST		;BACKGROUND MEM TEST
	;
	ENDIF
	;
	IF	DRIVERS
	;
READER:	;READER INPUT ROUTINE
	MVI	A,RDRCNT	;SHIFT COUNTER
	CALL	DISPAT		;GO TO LOOKUP ROUTINE
	DW	TTYRDR		;READER IS TTY KBD.
	DW	HSRIN		;IS HIGH SPEED READER
	DW	UR1IN		;USER READER ONE
	DW	UR2IN		;USER READER TWO
;
PUNCH:	;PUNCH OUTPUT ROUTINE
	MVI	A,PCHCNT	;SHIFT COUNTER
	CALL	DISPAT		;LOOK UP ROUTINE
	DW	TTYOUT		;PUNCH IS TTY
	DW	PUNOUT		;HIGH SPEED PUNCH
	DW	UP1OUT		;USER PUNCH ONE
	DW	UP2OUT		;USER PUNCH TWO
;
LIST:	;LIST OUTPUT ROUTINE
	MVI	A,LSTCNT	;SHIFT COUNTER
	CALL	DISPAT		;GO TO DISPATCHER
	DW	TTYOUT		;LIST DEVICE IS TTY
	DW	CRTOUT		;LIST DEVICE IS CRT
	DW	LPTOUT		;LINE PRINTER
	DW	UL1OUT		;USER LIST DEVICE
;
LISTST:	;LIST DEVICE STATUS ROUTINE
	MVI	A,LSTCNT	;SHIFT COUNTER
	CALL	DISPAT		;LOOK UP ROUTINE
	DW	TTYST		;RETURN TTY STATUS
	DW	CRTST		;RETURN CRT STATUS
	DW	LPTST		;LINE PRINTER STATUS
	DW	UL1ST		;USER LIST DEVICE STATUS
;
;**********************************************************
;
;	       INPUT/OUTPUT DRIVERS
;
;**********************************************************
;
;		CRT DRIVERS 
;
CRTST:	;CRT STATUS
	IN 	SER1ST	;GET SERIAL ONE STATUS
DB	JR,	TSTSTAT-$-1
;
CRTIN:	IN	SER1D	;GET DATA
STRPRET: ANI	7FH	;STRIP PARITY
	RET
;
CRTOUT:	;CRT OUTPUT
	IN	SER1ST	;GET STATUS FOR 1RST SERIAL
	ANI	OUTRDY	;READY TO RECEIVE DATA?
DB	JRZ,	CRTOUT-$-1 AND 0FFH	;NO, LOOP TIL READY
	MOV	A,C	;GET DATA
      	OUT	SER1D	;SEND TO DATA PORT
	RET
;
;		TTY DRIVERS 
;
TTYST:		;TTY INPUT STATUS
	IN	SER2ST	;GET SERIAL TWO STATUS
DB	JR,	TSTSTAT-$-1
;
TTYRDR:	;TTY KEYBOARD AS READER
	IN	SER2ST
	ANI	INRDY
	JZ	READER0
TTYIN:	IN	SER2D	;GET DATA
DB	JR,	STRPRET-$-1 AND 0FFH	
;
TTYOUT:	;TTY OUTPUT
	IN	SER2ST	;GET STATUS FOR SERIAL PORT 1
	ANI	OUTRDY	;READY FOR DATA?
DB	JRZ,	TTYOUT-$-1 AND 0FFH	;NO, WAIT TIL READY
	MOV	A,C	;GET DATA
	OUT	SER2D	;SEND TO SERIAL 2 DATA PORT
	RET
;
;		READER DRIVER (PARALLEL INPUT PORT)
;
RDRST:				;READER STATUS
	IN	MOTHER		;GET STATUS
TSTSTAT:
	ANI	INRDY		;MASK FOR READY
	RRC			;SHIFT TO LSB
DCRCMA:
	DCR	A		;IF ZERO, MAKE FF
	CMA			;COMPLEMENT THE ACC.
	RET

HSRIN:				;HIGH-SPEED READER INPUT 
	IN	MOTHER		;GET STATUS
	ANI	INRDY		;READY WITH DATA?
	JZ	READER0		;LOOP UNTIL PI FLAG
	IN	PARAL		;GET PARALLEL INPUT DATA
	ANI	07FH		;STRIP PARITY
	MOV	B,A		;SAVE
	MVI	A,30H		;LOAD COMMAND BYTE TO A
	OUT	MOTHER		;RESET PI FLAG
	MOV	A,B		;RECOVER DATA
	RET
;
;		PUNCH DRIVER (PARALLEL OUTPUT PORT)
;
PUNOUT:				;HIGH-SPEED PUNCH OUTPUT 
LPTOUT:				;LINE PRINTER OUT
	;PUNOUT AND LPTOUT ARE SAME ROUTINE
	IN	MOTHER		;GET STATUS
	ANI	OUTRDY		;READY FOR DATA
DB	JRZ, 	LPTOUT-$-1 AND 0FFH	;NO, WAIT TIL READY
	MOV	A,C		;GET DATA
	OUT	PARAL		;SEND TO PARALLEL PORT
	MVI	A,20H		;COMMAND BYTE
	OUT	MOTHER		;RESET PO FLAG
	MOV	A,C		;GET DATA AGAIN
	RET
;
LPTST:				;LINE PRINTER STATUS 
	IN	MOTHER		;GET STATUS
	ANI	OUTRDY		;READY TO RECEIVE CHAR
DB	JR,	DCRCMA-$-1 AND 0FFH
;
;**********************************************************
;
;		USER DEFINED I/O DEVICE DRIVERS
;
;**********************************************************
;
UC1ST:	;USER CONSOLE 1 STATUS
UL1ST:	;USER LIST DEVICE STATUS
	MVI	A,0FFH		;ALWAYS READY
	RET
;
UC1OUT:	;USER CONSOLE 1 IN (NULL)	
UP1OUT:	;USER PUNCH ONE OUT (NULL)
UP2OUT:	;USER PUNCH TWO OUTPUT "
UL1OUT:	;USER LIST OUTPUT (NULL)
	MOV	A,C		;CHARACTER INTO A
	RET
;
UC1IN:	;USER CONSOLE 1 IN (NULL)
UR1IN:	;USER READER ONE INPUT
UR2IN:	;USER READER TWO INPUT
	MVI	A,EOF		;END OF FILE FOR NOW
	RET
;
;**********************************************************
;THIS IS THE INIT ROUTINE....You should change this for
;your hardware.  Usually you should make it the same as
;the init routine that came with the version of CP/M that
;is customized for your system. The init routine below is
;for a North Star Horizon.
;
INITIAL:
	MVI	A,IOBYT		;THE VALUE
	STA	IOBYTE		;THE LOCATION
;
	XRA	A		;SET TO CLEAR MOTHERBOARD
	OUT	MOTHER
	OUT	MOTHER		;EXTRAS FOR TIMING, 
	OUT	MOTHER
	OUT	MOTHER		;DON'T ASK
	;
;
	MVI	A,40H		;DISABLE PARITY LOGIC
	OUT	MEMORY		;BEFORE READING RAM
	;
	ENDIF
	;
	IF	PARITY	
	;
	LXI	H,RAMEND	;PARITY SETTING CODE
	MOV	D,H
	MOV	E,L
	LXI	B,RAMEND+1	;NUMBER OF BYTES TO CLEAR
DW	LDDR			;SET PARITY ON ALL RAM
	MVI	A,41H		;PARITY ENABLE CODE
	OUT	MEMORY		;REARM PARITY LOGIC
	;
	ENDIF
	;
	IF	DRIVERS
	;
	MVI	A,0CEH		;INITIALIZE UARTS
				;2 STOPS, 16X CLOCK
				;8 BITS, NO PARITY
	OUT	SER1ST
	OUT	SER2ST
;
	MVI	A,37H		;COMMAND: RTS, ER,
				;RXF, DTR, TXEN
	OUT	SER1ST
	OUT	SER2ST
;
	IN	PARAL
	IN	SER1D
	IN	SER2D
;
;
	MVI	A,30H		;RESET PI FLAG
	OUT	MOTHER
	;
	RET
	;
	ENDIF
;
USR1END: EQU	$		;END OF USER1 CODE FOR NOW
USR1SIZE EQU	USR1END-USER1	;MUST NOT EXCEED SPACE 
				;ALLOCATED IN USER1
;
;**********************************************************
; USER2 code starts here. Will be loaded to just above
; CP/M by the CCP.
;The following ORG statement will provide for loading of
;USER2 code just above CP/M by the specially initialized
;CCP(which this program does); AND, will provide for the
;memory image to appear at 100H when the OVERLAY offset
;is used with DDT to patch in your code to a CPMxx.COM
;file. Save your USER2 code as a transient called XTCPM.COM
;and keep it on your A drive since it will be needed for all
;cold boots.  Also save your CPMxx.COM file after patching
;with DDT and use your normal SYSGEN program to write it
;to the system tracks. Save both files to appropriate size.
;
;Any code placed in this area should have the EQU  $+OFFSET
;at each label and Z80 jump relatives should use the FR and
;BR post-fixes as shown.
;
;THIS ORG STATEMENT SHOULD CAUSE THE FOLLOWING CODE TO APPEAR
;AT 100H IN THE DDT IMAGE AFTER OFFSETTING WITH THE OVERLAY
;BIAS THAT YOU USE IN PATCHING YOUR BIOS INTO CPMxx.COM.
;
	ORG	CCP-(CCPIMAGE-100H)
SOURCE:	EQU	$		;SOURCE OF USER2 CODE
OFFSET:	EQU	USER2-SOURCE	;OFFSET FOR USER2 CODE
FR:	EQU	-1-OFFSET	;FORWARD RELATIVE
BR:	EQU	FR+0FFH+1	;BACKWARD RELATIVE
;
	
;**********************************************************
;This routine is the first to be called when going to the
;TPA on a coldboot turnkey start.  It simply changes the
;CCP parameters to restore the dma and address for the TPA
;back to 100H and resets the address tested for CCP overwrite.
;The turnkey start in the mean time has loaded the rest of
;the code in the USER2 area to a location just above CP/M
;and all properly linked to the code in the USER1 area.
;
	RET			;TO RET IF EXECUTED FROM
				;THE COMMAND LINE.
;This is the entry point when called by the CCP.
XTCPM:	EQU	$+OFFSET	;ROUTINE TO RESTORE CCP DMA
				;AND TPA ADDRESSES TO 100H
	LXI	H,TPA
	SHLD	CCPDMA
	SHLD	TPASUB
	LXI	H,CCP
	SHLD	TSTOVWR
	;
	MVI	A,PASBYT ;GET THE PASSWORD BYTE
			 ;If you use Ward's 'BYE' ,then
			 ;you should restore the PASBYT
			 ;to its initial value in the
			 ;HANGUP routine of 'BYE'.
			 ;Use the following code:
			 ;LHLD	0001H	;get bios vector
			 ;LXI	D,-28H 	;set to subtract (SEARA+2)
			 ;DAD	D	;hl pnts to PASBYTE address
			 ;MOV	A,M	;load hl with address
			 ;INX	H
			 ;MOV	H,M
			 ;MOV	L,A
			 ;MVI	M,24H	;reset PASBYTE
			 ;
	STA	PASBYTE	 ;STORE IT
	;
	CALL	RSTPRMS
	;
	IF	SGNON
	;
	MVI	C,WRBUF		;GIVE SYSTEM SIGNON MESSAGE
	LXI	D,SIGNON
	CALL	BDOSFNC
	;
	ENDIF
	;
	IF	NOT TNKY2
	;
	RET
	;
	ENDIF
	;
	IF	TNKY2
	;
	LXI	H,COMBUF+1
	SHLD	BUFPTR
	DCX	H
	XCHG
	LXI	H,FILNAM
	LXI	B,5
DW	LDIR
	MVI	C,0
	JMP	CCP
	;
FILNAM:	EQU	$+OFFSET
	DB	3,'BYE',0
	;
	ENDIF
	;
	IF	SGNON
	;
SIGNON: EQU	$+OFFSET
	DB	CR,LF
	DB	MSIZE/10+30H
	DB	MSIZE MOD 10 + 30H
	DB	'K CP/M 2.2 - XTCPM$'
	;
	ENDIF
;
;**********************************************************
;The password checking routine- uses string comparator
;that is contained in the CCP.
;
CKPASS0: EQU	$+OFFSET
	LXI	H,PASBYTE
DW	BIT2M
DB	JRZ,	OK-$+FR
	LXI	H,PAS2
	SHLD	STRPTR
	LXI	H,PASTBL2
	SHLD	CMDPTR
DB	JR,	CKPASS-$+FR
	;
PASSINT: EQU	$+OFFSET
	CALL	LOGTST
DB	JRZ,	OK-$+FR
	LXI	H,PAS1
	SHLD	STRPTR
	LXI	H,PASTBL1
	SHLD	CMDPTR
	;
CKPASS:	EQU	$+OFFSET
	MVI	A,1
	STA	CMDCNT
	MVI	A,10
	STA	CMDLEN
	MVI	C,4	;GIVES 4 TRIES
	PUSH	B
	;
CKAGIN:	EQU	$+OFFSET
	CALL	ILPRT
	DB	'Password:',0
	POP	B
	MVI	B,14	;TO KEEP HIM FROM CLOBBERING
			;THE CODE
	LXI	H,NAME
DRCON:	EQU	$+OFFSET
	CALL	VCONIN
	CPI	CR
DB	JRNZ,	DRCN2-$+FR
	MVI	A,20H
	MVI	B,1
DRCN2:	EQU	$+OFFSET
	MOV	M,A
	INX	H
	DCR	B
DB	JRNZ,	DRCON-$+BR	
	;
	DCR	C
	JZ	RELOG0
	PUSH	B
	JMP	SRCHCMD
	;
OK:	EQU	$+OFFSET
	CALL	ILPRT
	DB	'Ok.',0
	JMP	RETCCP
;
PASTBL1: EQU	$+OFFSET
	DW	OK1
	DW	CKAGIN
PASTBL2: EQU	$+OFFSET
	DW	OK2
	DW	CKAGIN
;
;**********************************************************
;This is the in-line print subroutine.
;
ILPRT:	EQU	$+OFFSET
DB	EXX	
	CALL	CRLF
DB	EXX
	;
ILPRT2:	EQU	$+OFFSET
	XTHL		;SAVE HL, GET MSG
ILPLP:	EQU	$+OFFSET
	MOV	C,M	;GET CHAR
	CALL	VCONOUT	;OUTPUT IT
	INX	H	;POINT TO NEXT
	MOV	A,M	;TEST
	ORA	A	;..FOR END
DB	JRNZ,	ILPLP-$+BR
	XTHL		;RESTORE HL, RET ADDR
	RET		;RET PAST MSG
;
;***********************************************************
;ROUTINE TO TEST LOG IN
;
LOGTST:	EQU	$+OFFSET
	LXI	H,PASBYTE
DW	BIT5M
	RET
;
;**********************************************************
;
RELOG0:	EQU	$+OFFSET
	CALL	CRLF
RELOG:	EQU	$+OFFSET
	CALL	LOGTST
DB	JRZ,	EXIT-$+FR
	CALL	LGMSG
EXIT:	EQU	$+OFFSET
	JMP	RETCCP
	;
LGMSG:	EQU	$+OFFSET
	CALL	ILPRT2
DB	'Login please. ',CR,LF,0
	RET
;
;**********************************************************
;Routines to trap transient files if they are resident
;on drive A.  If DIR.COM not present, then the CCP code for
;the DIR command will be executed. If TYPE.COM not present,
;then the CCP code for the TYPE command will be executed.
;This trapping is useful for SD.COM and MLIST.COM re-named
;to DIR.COM and TYPE.COM respectively.
;**NOTE**  Do not use a Directory transient that scans user
;areas such as SD-21 or SD-22. KDIR is ok.  You can use SD-42
;if you set AOPT to FALSE and add an IF AOPT and ENDIF around
;the [lda,newusr; cmp m; jnz mordir] sequence at the label,
;SYSFOK. Otherwise you will defeat the universal user area
;features in this program so far as directories are concerned,
;although the universallity will still apply to execution and
;finding of files in users 13, 14, and 15.
;
DIR1:	EQU	$+OFFSET
	LXI	H,TRAPBYT
DW	SET0M			;SET DIR TRAP
	JMP	GETRANS
	;
TYPE1:	EQU	$+OFFSET
	LXI	H,TRAPBYT
DW	SET1M
	JMP	GETRANS
	;
HUH0:	EQU	$+OFFSET
	LXI	H,TRAPBYT
DW	BIT0M			;SEE IF DIR TRAP SET
	JNZ	DIR		;CCP DIR CODE IF NOT
DW	BIT1M			;SEE IF TYPE TRAP SET
DW	RES1M
	JNZ	TYPE		;CCP TYPE CODE IF NOT
	CALL	ILPRT
	DB	'No!, ',BELL,0
	CALL	LOGTST
DB	JRNZ,	RELOG-$+BR
	JMP	HUH+3		
	;
;**********************************************************
;This is the command string which is divided into private
;and public sections. Do not use more than five letters for
;the name of any command/transient and fill out each string
;to exactly five characters by using spaces below. Strings
;must occur at 5 character intervals.  If you use some
;other string length such as 4 as in the original CCP, just
;change string lengths below and change the LENCMD equate
;from 5 to 4.  I use 5 below so that LOGIN & LOGON can fit.
;
CMDSTR2: EQU	$+OFFSET
	DB	'ERA  REN  SAVE '
CMDSTR1: EQU	$+OFFSET
	DB	'TYPE USER PASS DIR  '
CMDSTR0: EQU	$+OFFSET
	DB	'DIR  LOGINLOGON'
;
;**********************************************************
;This is the command address table which is divided into
;private and public areas. The address of the routine to be
;jumped to must be here for CCP-included code as well as
;any that you put in your customized BIOS.
;You can expand the table to any extent that you have space
;and the command string above must be expanded in synchronism.
;
CMDTBL2: EQU	$+OFFSET
	DW	ERA	;ERA....PRIVATE COMMANDS
	DW	REN	;REN
	DW	SAVE	;SAVE
CMDTBL1: EQU	$+OFFSET
	DW	TYPE1	;TYPE..In case MLIST.COM renamed
			;to 'TYPE.COM'. Put the transient
			;in USER 14 on drive A.
	DW	USER	;USER
	DW	CKPASS0	;PASS(word) to gain access to
			;private user areas. Can also
			;be set at LOGIN by entering the
			;second password.
	DW	DIR1	;DIR....If you wish to use SD or
			;KDIR as a CCP transient instead of
			;the CCP DIR code, just change the
			;name of your transient to DIR.COM
			;and put it in USER 14 on drive A.
			;The transient will run only if 1rst
			;password level has been achieved.
			;If transient not present then CCP
			;'DIR' code will run.
;These commands will be the only ones allowed until the user
;logs in to the system with LOGIN.
CMDTBL0: EQU	$+OFFSET
	DW	DIR	;WILL RUN THE CCP 'DIR' CODE
	DW	PASSINT	;LOGIN
	DW	PASSINT	;LOGON
GETRAN0: EQU	$+OFFSET ;This must be last entry in table.
	DW	GETRANS	;GET THE TRANSIENT AND EXECUTE OR
			;RETURN TO CCP W/HUH MESSAGE IF NOT
			;ON DISK.
;
;**********************************************************
;This patch which is called from the CCP provides for a
;report of USER number at the prompt....i.e. A2> for USER 2,
;'A' drive.
;
UPATCH:	EQU	$+OFFSET
	CALL	GETUSR		;GET USER NUMBER
	ANI	0FH		;KILL UNWANTED BITS
DB	JRZ,	UPA2-$+FR	;IF USER 0, DON'T REPORT
	CPI	10
DB	JRC,	UPA1-$+FR	;JIF USER NUM = 0 THRU 9
	SUI	10		;USER NUM = 10 THRU 15
	PUSH	PSW
	MVI	C,'1'
	CALL	VCONOUT		;PRINT A '1'
	POP	PSW
	;
UPA1:	EQU	$+OFFSET
	ADI	'0'
	MOV	C,A
	CALL	VCONOUT		;PRINT DIGIT
	;
UPA2:	EQU	$+OFFSET
	MVI	C,'>'
	CALL	VCONOUT		;PRINT '>', EXIT
	XRA	A
	STA	TRAPBYT		;CLEAR THE TRAPS
	;FALL THROUGH TO RESET PARAMETERS & RETURN
;
;**********************************************************
;Routines to reset parameters for string matching and other
;purposes.
;
RSTPRMS: EQU	$+OFFSET
	MVI	A,0C3H		;SET BDOS TO TREAT USERS 
	STA	BDOS+75EH	;..13, 14 AND/ OR 15 AS
	LXI	H,UNIV		;..UNIVERSAL DEPENDING ON
	SHLD	BDOS+75FH	;..PASSWORD LEVEL.
	;
	MVI	A,LENCMD
	STA	CMDLEN
	LXI	H,PASBYTE
DW	BIT2M
DB	JRNZ,	RST1-$+FR
	;
RST2:	EQU	$+OFFSET
	LXI	H,CMDSTR2
	SHLD	STRPTR
	MVI	A,(GETRAN0-CMDTBL2)/2
	STA	CMDCNT
	LXI	H,CMDTBL2
	SHLD	CMDPTR
	MVI	A,16
	STA	TSTUSR
	RET
	;
RST1:	EQU	$+OFFSET
	LXI	H,CMDSTR1
	SHLD	STRPTR
	MVI	A,(GETRAN0-CMDTBL1)/2
	STA	CMDCNT
	LXI	H,CMDTBL1
	SHLD	CMDPTR
	MVI	A,MAXUSER+1
	STA	TSTUSR
	LDA	PASBYTE
DW	BIT5A
	RZ
RST0:	EQU	$+OFFSET
	MVI	A,(GETRAN0-CMDTBL0)/2
	STA	CMDCNT
	LXI	H,CMDSTR0
	SHLD	STRPTR
	LXI	H,CMDTBL0
	SHLD	CMDPTR
	RET
;
;**********************************************************
;BIOS routines called herein.
;
VCONIN:	EQU	$+OFFSET
DB	EXX
	CALL	BIOS+9H
DB	EXX
	RET
	;
VCONOUT: EQU	$+OFFSET
	PUSH	H
	PUSH	D
	PUSH	B
	CALL	BIOS+0CH
	POP	B
	POP	D
	POP	H	
	RET
;
;**********************************************************
;This routine will make USER 13, 14, and/or 15 universal on
;drive A only depending on password level. Files placed in
;user 13 will be universal without any password. Files placed
;in user 14 will additionally be universal on drive A at the
;first password level, and files placed in user 15 will be
;universal at the second password level in addition to those
;in users 13 and 14.
;	***** WARNING! *****
;NEVER have two files by the same name & extension on the
;same disk (one in USER 15 and the other in some other
;USER) because CP/M will take which ever one comes first
;in the directory.
;
UNIV:	EQU	$+OFFSET
	MOV	A,B		;duplicate instruction overlayed
	ORA	A		;check pointer for zero byte
DB	JRNZ,	UNIV1-$+FR	;skip if not zero byte of fcb
	;
	LDA	CURDSK	;get current disk
	ANI	0FH		;mask out high nibble
	CPI	0		;is it drive A? (may be changed
				;to any drive. 0,1,2.. = A,B,C..)
DB	JRNZ,	UNIV1-$+FR	;skip if not
	;
	LDAX	D		;get fcb first byte
	CPI	0E5H		;check for erased
DB	JRZ,	UNIV1-$+FR	;skip if erased
	;
	LDA	PASBYTE
DW	BIT5A
DB	JRNZ,	DFU13-$+FR
DW	BIT2A
DB	JRNZ,	DFU14-$+FR
	;
	MOV	A,M
	CPI	15		;and check for user 15
DB	JRZ,	MATCH0-$+FR	;jump if zero
	;
DFU14:	EQU	$+OFFSET
	MOV	A,M
	CPI	14		;check for user 14
DB	JRZ,	MATCH0-$+FR
	;
DFU13:	EQU	$+OFFSET
	MOV	A,M
	CPI	13		;check for user 13
MATCH0:	EQU	$+OFFSET
	JZ	MATCH
	;
UNIV1:	EQU	$+OFFSET
	MOV	A,B		;duplicate instuction overlayed
	CPI	0DH		;	"	"	"
	JMP	TINUSRCH	;and return where routine left off
	;
;**********************************************************
;This is a background memory test that runs whenever the
;current console (including remote) is waiting for an input
;character to be typed.
;
;TEST ROUTINE TO CONSTANTLY TEST MEMORY
;
	IF	BGTST AND DRIVERS
;
TSTPT:	EQU	$+OFFSET
	DW	0
TEST:	EQU	$+OFFSET
	DI
	PUSH	H
	LHLD	TSTPT
	INX	H
	MOV	A,H
	CPI	($+OFFSET)/100H-SKIP/100H
DB	JRNZ,	TEST1-$+FR
	LXI	H,0
;
TEST1:	EQU	$+OFFSET
	SHLD	TSTPT
	MOV	A,M
	CMA
	MOV	M,A
	CMP	M
DB	JRZ,	TEST2-$+FR
	XRA	M
	PUSH	PSW
	PUSH	H
	CALL	CRLF
	POP	H
	POP	PSW
	PUSH	PSW
	CALL	PRTHX
	CALL	ILPRT2
	DB	'H Bits bad at: ',0
	MOV	A,H
	CALL	PRTHX
	MOV	A,L
	CALL	PRTHX
	POP	PSW
;
TEST2:	EQU	$+OFFSET
	CMA
	MOV	M,A
	POP	H
	EI
	RET
;
PRTHX:	EQU	$+OFFSET
	PUSH	PSW
	RRC
	RRC
	RRC
	RRC
	CALL	PRTHX1
	POP	PSW
;
PRTHX1:	EQU	$+OFFSET
	ANI	0FH
	CPI	10
DB	JRC,	PRTHX2-$+FR
	ADI	7
;
PRTHX2:	EQU	$+OFFSET
	ADI	'0'
	MOV	C,A
DB	JR,	VCONOUT-$+BR
	;
;END OF CONSTANT MEMORY TEST
;
	ENDIF
;
USR2END: EQU	$+OFFSET	;END OF USER2 CODE FOR NOW
INITSIZE EQU	INITSIZE	;
USR2SIZE EQU	USR2END-USER2	;DO NOT EXCEED SPACE ALLOCAT-
				;ED ABOVE YOUR RUNNING CP/M. 
;
;**********************************************************
;	EQUATES FOR CCP PATCHES
LENCMD	EQU	5		;LENGTH OF CCP CMD STRINGS
GETUSR	EQU	CCP+113H	;GET USER #
SETUSR	EQU	CCP+115H	;SET USER #
TSTUSR	EQU	CCP+692H	;TEST USER #
USRSAV	EQU	CCP+118H
UPATCH0	EQU	CCP+390H
UPATCH1	EQU	CCP+393H
HUH	EQU	CCP+209H	;CCP HUH RESPONSE
CRLF	EQU	CCP+98H		;CCP CR/LF FUNCTION
RETCCP  EQU	CCP+382H	;RE-ENTRY TO CCP
GETCMD	EQU	CCP+398H	;GET THE ENTERED COMMAND
SRCHCMD	EQU	CCP+3B1H	;ENTRY TO SEARCH OF COMMANDS
COMBUF	EQU	CCP+7H		;LOC OF CMD BUFFER
BUFPTR	EQU	CCP+88H		;LOC OF CMD BUFFER POINTER
CMDPTR	EQU	CCP+3B5H	;LOC OF CMD ADDR TBL PTR
GETRPTR	EQU	CCP+3AFH	;LOC OF GET TRANS PTR
STRPTR	EQU	CCP+32FH	;POINTER TO CCP CMD STRINGS
CMDCNT	EQU	CCP+335H	;BYTE VALUE = # OF COMMANDS
CMDLEN	EQU	CCP+33BH	;BYTE VALUE = LENGTH OF EACH
				;COMMAND STRING.
TSTOPN	EQU	CCP+6DCH	;TEST FILE OPEN FUNCTION
CMDDRV	EQU	CCP+7F0H	;BYTE INDICATES DRV IN CMD
CMDERR	EQU	CCP+76BH	;HUH RESPONSE IF DR #
NAME	EQU	CCP+7CEH	;LOC FOR FN IN FCB
XTNSN	EQU	CCP+7D6H	;STORAGE LOC FOR FILE TYPE,
				;EXT #, AND RECORD COUNT
RELOOK	EQU	CCP+6CDH	;RELOOK W/INDICATED DRIVE
TSTOVWR	EQU	CCP+6F5H	;TEST OVERWRITE OF CCP
CCPDMA	EQU	CCP+6DFH	;LOC TO SUBSTITUTE FOR
				;DMA ADDRESS
TPASUB	EQU	CCP+75DH	;LOC TO SUSTITUTE BEFORE
  				;CALLING THE TPA
CLDBOOT	EQU	CCP+35CH	;COLDBOOT ENTRY POINT
CONTINUE EQU	CCP+75FH	;CONTINUE AFTER CALL TO TPA
GETRANS	EQU	CCP+6A8H	;GET TRANS OR CCP COMMAND
ERA	EQU	CCP+51FH	;ADDR OF ERA ROUTINE
REN	EQU	CCP+610H	;ADDR OF REN ROUTINE
SAVE	EQU	CCP+5ADH	;ADDR OF SAVE ROUTINE
DIR	EQU	CCP+477H	;ADDR OF DIR ROUTINE
TYPE	EQU	CCP+55DH	;ADDR OF TYPE ROUTINE
USER	EQU	CCP+68EH	;ADDR OF USER ROUTINE
TPA	EQU	100H		;OR YOUR TPA ADDRESS
STKPTR1	EQU	CCP+35DH
STKPTR2	EQU	CCP+760H
STKPTR3	EQU	CCP+383H
PRNSTR1	EQU	CCP+0A7H	;PRINT STRING WITH LEADING
				;CR/LF..POINT TO IT WITH BC
PRNSTR2	EQU	CCP+0ACH	;PRINT STRING..POINT WITH HL
CCPSPARE EQU	CCP+7F2H	;14 SPARE BYTES AT END CCP
	;Addresses of holes left in CCP that are filled in
	;with patches.
HOLE1	EQU	CCP+79BH	;16 BYTES
HOLE2	EQU	CCP+310H	;30 BYTES	
HOLE3	EQU	CCP+3C1H	;24 BYTES
HOLE4	EQU	CCP+1F5H	;20 BYTES
HOLE5	EQU	CCP+6A5H	;3 BYTES
;
;**********************************************************
;	CCP patches
;**********************************************************
	ORG	GETRPTR
	DW	GETRANS
	;
	ORG	CMDCNT
	DB	0
	;
	ORG	CMDPTR
	DW	GETR0
	;
	ORG	HOLE5
GETR0:	DW	GETRANS
	DB	0	;3 BYTES AT HOLE5
;
;Patch the turnkey start string into the CCP command buffer.
	ORG	COMBUF
TNKY:	DB	05,'XTCPM',0       
;
;**********************************************************
;To trap the HUH message.
;
	ORG	HUH
	JMP	HUH0
;
;**********************************************************
;To redirect the CCP stack so a larger stack is available.
;
	ORG	STKPTR1
	DW	STACK
	ORG	STKPTR2
	DW	STACK
	ORG	STKPTR3
	DW	STACK
;
;**********************************************************
; This patch is used to restrict access to the higher user
; areas while leaving the lower user areas public. The high-
; est available public user area is defined by MAXUSER.
;
	ORG	HOLE1
OK2:	LXI	H,PASBYTE
DW	RES2M
	;
OK1:	LXI	H,PASBYTE
DW	RES5M
	JMP	OK
;
TRAPBYT:
	DB	0
	DB	0,0	;SPARES
			;DO NOT EXPAND
			;16 BYTES AT HOLE1
;***********************************************************
;The following sequence of statements are to patch the CCP
;so that the transient 'XTCPM.COM' will be loaded above CP/M
;by the CCP without the need for a special 'sysgen' routine.
;Use the normal SYSGEN program with your system.
	ORG	CCP+1
	DW	CCPINIT
	;
	ORG	HOLE2
CCPINIT:
	LXI	H,CCP+35CH
	SHLD	CCP+1
	;
	LXI	H,BOOTFLG
DW	BIT1M			;SEE IF COLD BOOT
DW	RES1M
	JZ	CCP+358H	;ELSE GOTO WARM BOOT ENTRY 
	;
	LXI	H,USER2+800H	;TO MOVE OVERWRITE TEST LOC
				;TO TOP OF SYS MEMORY
	SHLD	TSTOVWR
	LXI	H,USER2		;TO SUBSTITUTE DMA AND TPA
				;TO USER2 ADDRESS TEMPORARILY
	JMP	BRIDGE
;
	DB	0,0		;SPARES
;Do not expand, 30 bytes available here.
;
;**********************************************************
	ORG	USRSAV
	DW	SAVUSR
	;
	ORG	HOLE3
SAVUSR:	MOV	A,E
	CPI	0FFH
	JZ	BDOSFNC
	RLC
	RLC
	RLC
	RLC
	MOV	B,A
	LDA	DRIVE
	ANI	0FH
	ORA	B
	STA	DRIVE
	JMP	BDOSFNC
	DB	0	;DO NOT EXPAND, 24 BYTES HERE
;
;**********************************************************
;These are the passwords for entry to CP/M from BYE	   
;and to USER area and command privileges above MAXUSER	   
;repectively. Passwords may be up to 10 characters, and   
;must be filled out to this value with spaces.		   
	ORG	HOLE4
PAS1:	DB	'PASSWORD1 '
PAS2:	DB	'PASSWORD2 '		   
			;DO NOT EXPAND, EXACTLY 20 BYTES
			;ARE AVAILABLE HERE.
;							   
;**********************************************************
; This patch causes user number to be reported at the cp/m
; prompt.....i.e. - A2>.  User 0 report is suppressed.
;
	ORG	UPATCH0
	MVI	C,USRFNC
	;
	ORG	UPATCH1
	DW	UPATCH
	;the routine UPATCH is located in the USER2 area
;**********************************************************
; This patch causes the CCP of a cp/m 2.x system to look on
; drive A when you are logged into a drive other than A and
; call for a .COM file that does not exist on that drive.
; Giving an explicit drive reference overrides this feature,
; so that you can always force the file to be loaded from a
; specific drive.
;
	ORG	TSTOPN
	DW	APATCH		;REPLACES 'CMDERR'
;
	ORG	CCPSPARE
APATCH:	LXI	H,CMDDRV	;GET DRIVE FROM CURRENT CMD.
	ORA	M		;FETCHES DRIVE
	JNZ	CMDERR		;GIVE ERR IF CMD HAS DRIVE #
	INR	M		;FORCE TO DRIVE A
	LXI	D,XTNSN		;UNDO WHEN...
	JMP	RELOOK		;REENTERING
	;DON'T EXPAND. 14 BYTES HERE.
;
;**********************************************************
;	BDOS patches
;**********************************************************
;
BDOSPARE EQU	BDOS+0DEEH	;18 SPARE BYTES AT END BDOS
MATCH	EQU	BDOS+077CH
CURDSK	EQU	BDOS+342H
TINUSRCH EQU	BDOS+761H
;
	ORG	BDOS+0DDBH	;SEARA+2
	DW	PASBYTE		;ADDRESS OF PASS WORD BYTE
;
	ORG	BDOSPARE
BRIDGE:	SHLD	CCPDMA
	INX	H
	SHLD	TPASUB
	JMP	CLDBOOT  	;COLDBOOT ENTRY POINT
;end of CCP initialization
;
CBOOT0:	LXI	H,BOOTFLG
DW	SET1M	
	JMP	CBOOT
		;DO NOT EXPAND, EXACTLY 18 BYTES HERE
;
;**********************************************************
;	BIOS patches
;**********************************************************
;This jump table overlays the table at the base of the BIOS
;thus making it unecessary to have all of the jumps in the 
;jump table at the base of the USER1 area.
;
	ORG	BIOS
	JMP	CBOOT0
	;
	IF	DRIVERS	AND NOT (STDCPM OR M2BIOS)
	;
	ORG	BIOS+0FH
	JMP	LIST
	JMP	PUNCH
	JMP	READER
	;
	ORG	BIOS+2DH
	JMP	LISTST
	;
	ENDIF
	;
	IF	DRIVERS AND (STDCPM OR M2BIOS)
	;
	ORG	BIOS+6H
	JMP	CONST
	JMP	CONIN
	JMP	CONOUT
	JMP	LIST
	JMP	PUNCH
	JMP	READER
	;
	ORG	BIOS+2DH
	JMP	LISTST
	;
	ENDIF
	;
;************************************************************
;
;		SPECIAL ASCII CODES
;
LF	EQU	0AH	; LINE FEED
CR	EQU	0DH	; CARRIAGE RETURN
EOF	EQU	1AH	; END OF FILE
BELL	EQU	07H	; DING
NULL	EQU	00H	; NOTHING
ESC	EQU	1BH	; ESCAPE - USED IN TWO CHAR COMMANDS
LIN	EQU	6CH	; CHAR AFTER ESCAPE TO CLEAR LINE
SCRN	EQU	45H	; CHAR AFTER ESCAPE TO CLEAR SCREEN
;
;		STATUS MASKS
;
INRDY	EQU	02H	;INPUT READY MASK
OUTRDY	EQU	01H	;OUTPUT READY MASK
;
;	POSITION OF BIT PAIR IN IOBYTE
;
CONCNT	EQU	1	;CONSOLE BITS 0,1
RDRCNT	EQU	2	;READER  BITS 2,3
PCHCNT	EQU	3	;PUNCH   BITS 4,5
LSTCNT	EQU	4	;LIST    BITS 6,7
;
;**********************************************************
;
;		Z80 EQUATES
;
JR	EQU	18H	;JUMP RELATIVE
JRZ	EQU	28H	;JUMP RELATIVE ON ZERO
JRNZ	EQU	20H	;JUMP RELATIVE ON NOT ZERO
JRC	EQU	38H	;JUMP RELATIVE ON CARRY
JRNC	EQU	30H	;JUMP RELATIVE IF CARRY RESET
LDIR	EQU	0B0EDH	;MEMORY BLOCK MOVE
LDDR	EQU	0B8EDH	;MOVE BLOCK UNTIL COUNTER=0
MOVIVA	EQU	47EDH	;MOVE ACC TO INTERRUPT REGISTER
IM2	EQU	5EEDH	;SET INTERRUPT MODE 2
BIT2A	EQU	57CBH	;TEST BIT 2 IN ACC.
BIT5A	EQU	6FCBH	;TEST BIT 5 IN ACC.
BIT0M	EQU	46CBH	;TEST BIT 0 IN MEM
BIT1M	EQU	4ECBH	;TEST BIT 1 IN MEM
BIT2M	EQU	56CBH	;TEST BIT 2 IN MEM
BIT5M	EQU	6ECBH	;TEST BIT 5 IN MEM
SET0M	EQU	0C6CBH	;SET BIT 0 IN MEM
SET1M	EQU	0CECBH	;SET BIT 1 IN MEM
RES1M	EQU	8ECBH	;RESET BIT 1 IN MEM
RES2M	EQU	96CBH	;RESET BIT 2 IN MEM
RES5M	EQU	0AECBH	;RESET BIT 5 IN MEM
EXX	EQU	0D9H	;EXCH REG PAIRS B, D, & H
;
;
;BDOS EQUATES
;
BDOSFNC	EQU	BDOS+6H
USRFNC	EQU	32
WRBUF	EQU	9
