	TITLE	U C S D 2 C P M  ---  CONVERT PASCAL FILES TO CP/M FORMAT
;	
;
;	VERSION:  1.0   2/9/80
;
; THIS PROGRAM WAS DONATED TO THE SOFTWARE LIBRARY OF
;
; THE PASCAL ADVANCEMENT SOCIETY OF CALIFORNIA,   BY:
;
;	JEFFRY G. SHAW
;	P.O. BOX 600457
;	SUNNYVALE, CAL 94088
;	(408) 257-7676
;
; SUGGESTIONS FOR IMPROVMENT AND REPORTS OF BUGS SHOULD
; BE REPORTED TO THE PROGRAM DONER AND ARE ENCOURAGED.
;
; SOMETIMES THIS PROGRAM HAS DIFFICULTY IN FINDING THE PASCAL
; FILE NAME IN THE DIRECTORY.  IF YOU KNOW THAT THE FILE IS
; THERE THEN JUST RETRY UNTIL THE FILE IF FOUND.
;
;	GOOD LUCK!
;
.COMMENT\

	Changed 27 April 1980:
INITFILES:	Now allows display of Pascal directory before asking for the
	filename.
TRANS$WRITE:	Nulls, ampersands, and the blank compression char's were
	corrected.

	Routines added:
LIST$PDIR:	allows you to review Pascal directory before copying.
TESTING switch:	originally for my own use, changes the program to type all
	Pascal files on the CRT.  If you change the 4th line of PRTCHAR to
	"LD C,5", the program will print the directory and the files on the 
	system printer, provided TESTING is on.  If you wished, you could 
	probably quite easily change the switch to one set by the user at run 
	time.

	BUGS:
1.	Just give the program what it asks for (like filenames) when running.
	It is NOT forgiving of filename errors.
2.	The space-decoding routine is still imperfect: A rare line is 
	one or two spaces too far to the left, and once the space compression
	code was left as a quote.  Probably intersector errors.
3.	Occasionally, the last record is duplicated, requiring you either to
	edit the CP/M file or to diminish by one the record count in the direc-
	tory (byte 15 of the last extent of the file) with a patch utility.

JIM GAGNE, M.D., DATAMED RESEARCH, 1433 ROSCOMARE ROAD, LOS ANGELES 90024
\
FALSE	EQU	0
TRUE 	EQU	NOT FALSE
;
TESTING	EQU	FALSE
;
	.Z80
;
HTAB	EQU	09		;HORIZONTAL TAB
CR	EQU	0DH	    	;CARRAIGE RETURN CHARACTER
LF	EQU	0AH	    	;LINE FEED CHARACTER
EOF	EQU	1AH	    	;CP/M EOF CHAR
BLANK	EQU	20H	    	;BLANK CHAR
SPCHAR	EQU	10H	    	;PASCAL SPECIAL CHAR
SECLTH	EQU	128	    	;LENGTH OF SECTOR
;
;
GETCHR	MACRO
; READ A CHARACTER FROM THE CONSOLE.
	LD	C,1
	CALL	BDOS
	ENDM
;
GETBUF	MACRO
; INPUT BUFFERED LINE FROM CONSOLE.
	LD	C,10
	LD	DE,INBUFF
	CALL	BDOS
	ENDM
;
PUTBUF	MACRO	MSG
; DISPLAY MESSAGE ON CONSOLE.
	LD	C,9
	LD	DE,MSG
	CALL	BDOS
	ENDM
;
DELFIL	MACRO	FCBADR
; DELETE CP/M FILE.
	LD	C,19
	LD	DE,FCBADR
	CALL	BDOS
	ENDM
;
NEWFIL	MACRO	FCBADR
; CREATE A NEW CP/M FILE.
	LD	C,22
	LD	DE,FCBADR
	CALL	BDOS
	CP	255		;ERROR?
	ENDM
;
	ASEG
	ORG	100H
	JP	START
;
;	COMMAND LINE BUFFER
;
MAXLEN	EQU	40
BUFLEN	EQU	0
INBUFF:	DEFB	MAXLEN,BUFLEN	;MAX BUFFER LENGTH, ACTUAL BUFFER LEN
	DEFS	MAXLEN
;
;	CP/M INTERFACE --- FCB DEFINITIONS
;
BDOS	EQU	05H	    	;LOCATION 5 CONTAINS JMP TO BDOS
FCBLEN	EQU	33	    	;LENGTH OF FILE CONTROL BLOCK
FCB:	DEFS	FCBLEN	    	;FILE CONTROL BLOCK FOR CP/M FILE
;
FCBFN	EQU	FCB+1	    	;CP/M FILE NAME
FCBFT	EQU	FCB+9	    	;     FILE TYPE
FCBNR	EQU	FCB+32	    	;     NEXT RECORD NUMBER (0 TO 127)
;
BUFCNT:	DEFB	128	    	;NO. OF REMAINING SPACES IN BUFFER
WRSTAT:	DEFS	1		
DFNAME:	DEFS	1		;CP/M DISK NAME
;
;  PASCAL DISK DIRECTORY
;
DTITLE	EQU	06H		;OFFSET TO DIRECTORY ENTRY TITLE
DENTSZ	EQU	1AH		;DIRECTORY ENTRY SIZE IN BYTES
TLEN	EQU	15		;MAX PASCAL FILE TITLE LENGTH
;
SYSTLE:	DEFB	TLEN		;PASCAL FILE TITLE
	DEFS	TLEN
S$DISK:	DEFS	1		;CP/M DISK SAVED HERE
P$DISK:	DEFS	1		;PASCAL DISK NAME 
DENTP:	DEFS	2		;POINTER TO PASCAL DIR ENTRY
FBLOCK:	DEFS	2		;FIRST PASCAL FILE BLOCK
NBLOCK:	DEFS	1		;NUMBER OF BLOCKS IN PASCAL FILE
PFTYPE:	DEFS	1		;PASCAL FILE TYPE, 0=PCODE, 1=TEXT
;
;	BIOS INTERFACE
;
TSELDSK	EQU	1BH		;DISK SELECT
TSETTRK	EQU	1EH		;SET TRACK
TSETSEC	EQU	21H		;SET SECTOR
TSETDMA	EQU	24H		;SET DATA TRANSFER AREA
TREAD	EQU	27H		;READ SECTOR TO DATA AREA
;
;	BIOS DIRECT CALLS
;
SELDSK:	CALL	0		;SELECT DISK
	RET	
;
SETTRK:	CALL	0		;SET TRACK NUMBER
	RET	
;
SETSEC:	CALL	0		;SET SECTOR NUMBER
	RET	
;
SETDMA:	CALL	0		;SET BUFFER ADDRESS
	RET	
;
READ:	CALL	0		;READ DISK
	RET	
;
;
;	ROUTINE TO WRITE A SECTOR TO CP/M FILE
;	DE=ADDRESS OF THE BUFFER TO BE WRITTEN
;
WRITE:	
	LD	C,26		;SET DMA ADDRESS FUNCTION
	CALL	BDOS
	LD	C,21		;WRITE A SECTOR FUNCTION
	LD	DE,FCB		;CP/M FILE FCB
	CALL	BDOS
	RET	
;
;	ROUTINE TO CLOSE CP/M FILE
;
CLOSE:	
	PUSH	HL		;SAVE HL
	LD	A,(PFTYPE)
	CP	1
	CALL	Z,WRTLBF	;IF TEXT FILE WRITE LAST BUFFER
	LD	DE,FCB		
	LD	C,16		;CLOSE FUNCTION
	CALL	BDOS
	CP	255		;ERROR?
	JR	Z,CL10		;YES-
	POP	HL
	RET
CL10:
	PUTBUF	MSG5		;"ERROR IN CLOSEING CP/M FILE."
	POP	HL
	RET	
;
;	ROUTINE TO SAVE CP/M LOGGED IN DISK
;
SAVE$DISK:
	LD	DE,0
	LD	C,25		;RETURN CURRENTLY SELECTED DISK
	CALL	BDOS
	LD	(S$DISK),A
	RET	
;
;	ROUTINE TO RESTORE CP/M DISK
;
RESTORE$DISK:
	LD	HL,S$DISK
	LD	C,(HL)		;C=CP/M DISK
	CALL	SELDSK
	LD	BC,80H
	CALL	SETDMA		;SET DEFAULT DMA BUFFER
	RET	
;
;	SELECT DISK
;
;	THE REGISTER PAIR DE HAS THE DISK NAME:
;		E=0, A DISK
;		E=1, B DISK
;
SEL$DISK:
	PUSH	HL
	LD	D,0H		;CLEAR D REGISTER
	LD	C,0EH		;SELECT FUNCTION
	CALL	BDOS		
	POP	HL
	RET	
;
;	ROUTINE TO OPEN A CP/M FILE.
;
;	IF A FILE ALREADY EXISTS, IT IS DELETED.
;	A NEW FILE IS CREATED. ON ENTRY FCB CONTAINS A DESCRIPTION OF
;	THE FILE TO BE OPENED. ON EXIT :
;		A = 0 ,  ERROR IN OPENING THE FILE
;		A = 1 ,  SUCCESSFUL FILE OPEN
;
OPEN$CPMFILE:
	PUSH	HL		;SAVE HL
	LD	A,SECLTH	;INITIALIZE NO. OF REMAINING SPACES
	LD	(BUFCNT),A	;  IN THE TEXT FILE BUFFER
;
	DELFIL	FCB
;
	NEWFIL	FCB
	JR	NZ,OPNEX1	; OK
;
	PUTBUF	MSG7		;"NO SPACE AVAILABLE ON DISK."
	XOR	A		;A=0 , ERROR
	POP	HL
	RET
;
OPNEX1:
	XOR	A		;SET RECORD COUNT TO ZERO
	LD	(FCBNR),A
	LD	A,1		;A=1 , SUCCESSFUL OPEN
	POP	HL
	RET	
;
;	ROUTINE TO INITIALIZE FCB.
;
;	FIRST ALL ENTRIES ARE FILLED WITH 0, THEN FILE NAME
;	AND EXTENSION ARE FILLED WITH BLANKS.
;
SETFCB:	
	LD	HL,FCB		;FCB ADDRESS
	LD	C,0H		;ZERO OUT ENTIRE FCB
	LD	B,FCBLEN    	;FCB LENGTH
SF10:	
	LD	(HL),C		;FCB(I) = 0
	INC	HL		;I = I + 1
	DJNZ	SF10		;B = B - 1
;
	LD	HL,FCBFN    	;FILE NAME ADDRESS
	LD	C,' '		;FILL FILE NAME AND TYPE WITH BLANKS
	LD	B,11		;TOTAL NUMBER OF BLANKS
SF20:	
	LD	(HL),C		;FCBFN(I) = ' '
	INC	HL		;I = I + 1
	DJNZ	SF20		;B = B - 1
	RET	
;
;	ROUTINE TO MOVE CP/M FILE NAME AND EXTENSION
;	FROM THE INPUT BUFFER INTO THE CP/M FCB.
;
MVCPM:
	LD	HL,INBUFF+1 	;ADDRESS OF ACTUAL BUFFER LENGTH
	LD	B,(HL)		;B=ACTUAL BUFFER LENGTH
	LD	C,B		;SAVE BUFFER LENGTH
	INC	HL		;HL=BEGINING OF FILE NAME
;
;	SCAN FOR PERIOD
;
	LD	A,'.'
MC10:
	CP	(HL)
	JR	Z,MC20		;IF BUFF(I)='.'
;
	INC	HL		;I = I+1
	DJNZ	MC10		;B = B-1
;
;     FILE WITH NO EXTENSION
;
				;BC = BUFFER LENGTH.
	LD	HL,INBUFF+2 	;HL = COMMAND LINE INPUT BUFFER
	LD	DE,FCBFN    	;DE = FILE NAME IN FCB
	LDIR			;MOVE FILE NAME FROM BUFFER TO FCB
	RET	
;
;    FILE WITH AN EXTENSION
;
MC20:
	LD	A,C		;A  = BUFFER LENGTH
	LD	C,B		;SAVE FILE NAME LENGTH
	SUB	B		;A  = FILE NAME LENGTH
	INC	HL		;MOVE ACROSS PERIOD
;
	LD	B,0		;BC = LENGTH OF THE FILE NAME
	PUSH	BC
	LD	C,A
	LD	HL,INBUFF+2
	LD	DE,FCBFN
	LDIR			;MOVE FILE NAME FROM BUFFER TO FCB
;
	POP	BC
	DEC	C		;EXTENSION LENGTH
	LD	HL,INBUFF+1 	;BUFFER LENGTH
	LD	A,(HL)		
	LD	D,0
	SUB	C
	LD	E,A		;DE = OFFSET TO EXTENSION
	ADD	HL,DE
	INC	HL		;HL=EXTENSION STARTING ADDRESS
;
				;BC = EXTENSION LENGTH
	LD	DE,FCBFT    	;MOVE FILE TYPE FROM BUFFER TO FCB
	LDIR
	RET	
;
;	ROUTINE TO MOVE PASCAL FILE NAME FROM COMMAND LINE BUFFER
;	TO SYSTLE. IF THE FILE NAME IS LONGER THAN 15 CHARACTERS
;	AN ERROR MESSAGE IS ISSUED.
;
MOVE$PASCAL:
	LD	HL,INBUFF+1 	;ADDRESS OF ACTUAL BUFFER LENGTH
	LD	A,15		;MAX LENGTH OF A PASCAL FILE NAME
	CP	(HL)		;CY = A - (M)
	JR	NC,MP10		;IF FILE NAME LENGTH <= 15
;
;     ILLEGAL FILE NAME
;
	PUTBUF	MSG2		;"ILLEGAL FILE NAME, TRY AGAIN!"
;
	GETBUF			;READ ANSWER...
;
	JR	MOVE$PASCAL
;
MP10:
	LD	DE,SYSTLE   	;DE=ADDRESS OF SYSTLE
	LD	HL,INBUFF+1 	;HL=ADDRESS OF COMMAND LINE BUFFER
	LD	C,(HL)		;C=ACTUAL BUFFER LENGTH
	INC	C		;C=C+1
	LD	B,0
	LDIR			;MOVE PASCAL FILE NAME FROM BUFFER TO SYSTLE
	RET	
;
;	ROUTINE TO OBTAIN THE EXISTING PASCAL FILE NAME AND THE 
;	DESIRED CP/M FILE NAME FROM THE USER. IT ALSO INITIALIZES
;	THE DISK NAMES OF THE FILES AND THE FCB OF THE CP/M FILE.
;
GET$FILENAMES:
	PUTBUF	MSG1		;"UCSD FILE NAME?"
;
	GETBUF			;READ THE ANSWER...
;
	CALL	MOVE$PASCAL 	;MOVE FILE NAME FROM BUFFER TO SYSTLE
GF10:
	PUTBUF	MSG11		;"PASCAL TEXT FILE?"
;
	GETCHR			;READ THE ANSWER....
;
	LD	B,0
	CP	'N'
	JR	Z,GF20		;B=0, PCODE FILE.
	CP	'Y'
	JR	NZ,GF10
	INC	B		;B=1, TEXT FILE
GF20:	
	LD	A,B
	LD	(PFTYPE),A
;
	XOR	A		;PASCAL DISK NAME = A
	LD	B,01H		;CP/M DISK NAME = B
	LD	DE,P$DISK   	;P$DISK = A
	LD	(DE),A
	LD	DE,DFNAME 	;CPM$DISK = B
	LD	A,B
	LD	(DE),A
;
   IF NOT TESTING
	PUTBUF	MSG4		;"CP/M FILE NAME?"
;
	GETBUF			;READ THE ANSWER....
;
	PUTBUF	CRLF
;
	CALL	SETFCB		;INITIALIZE FCB
;
	CALL	MVCPM    	;MOVE CP/M FILE NAME FROM INPUT BUFFER TO FCB
   ENDIF
	RET	
;
;	ROUTINE TO WRITE 512 BYTES IN PASBUF TO 4
;	SECTORS ON CP/M FILE.
;
WRTCPM:
	SUB	A
	LD	(CTLP$FLAG),A	;RESET CONTROL P FLAG
	LD	DE,PASBUF-128	
	PUSH	DE		;SAVE PASCAL FILE BUFFER ADDRESS ON STACK
	LD	B,4		;LOOP COUNT
WT$LP:	
	POP	DE		;PREVIOUS VALUE OF PASCAL BUFFER ADDRESS
	LD	HL,128		;ADD SECTOR LENGTH
	ADD	HL,DE		;HL = NEW PASCAL BUFFER ADDRESS
	PUSH	HL		;SAVE PASCAL ADDRESS FOR NEXT ITERATION
	PUSH	BC		;LOOP COUNT --- B REG ONLY
;
	LD	A,(PFTYPE)
	CP	1
	JR	Z,WT$L1		;IF TEXT FILE
;
   IF	TESTING
	RET			;SKIP CODE WRITES
   ELSE
	EX	DE,HL		;DE=BUFFER ADDRESS
	CALL	WRITE		;WRITE BUFFER TO CP/M FILE
	JR	WT$L2
   ENDIF
;
WT$L1:	
	CALL	TRANS$WRITE 	;TRANSLATE AND WRITE TEXT FILE BUFFER
WT$L2:	
;
	CP	0H
	JR	Z,WT$CONT   	;IF NO ERROR
;
	CP	01H
	JR	Z,WTERR8
;
	CP	02H
	JR	Z,WTERR9
;
	PUTBUF	MSG10		;"ERROR -- NO DIRECTORY SPACE."
	JR	WTERR$EXT
WTERR8:
	PUTBUF	MSG8		;"ERROR IN EXTENDING FILE."
	JR	WTERR$EXT
WTERR9:
	PUTBUF	MSG9		;"ERROR -- END OF DISK DATA."
WTERR$EXT:
	XOR	A		;A = 0, ERROR EXIT
	POP	BC		;RESTORE STACK
	POP	HL
	RET	
;
WT$CONT:
	POP	BC		;LOOP COUNT
	DJNZ	WT$LP		;B=B-1 UNTIL B=0
;
	POP	HL		;RESTORE STACK
	LD	A,1		;SUCCESSFUL WRITE
	RET	
;
;	ROUTINE TO TRANSLATE THE PASCAL TEXTFILE BUFFER AND TO
;	WRITE OUT THE TRANSLATED TEXT TO CP/M FILE. ON INPUT:
;	HL=PASCAL FILE BUFFER ADDRESS
;	DURING TRANSLATION CR IN PASCAL BUFFER IS TRANSLATED TO CR LF
;	AND NULLS ARE IGNORED.  BLANK COMPRESSION CHARACTERS ARE EXPANDED TO
;	THE CORRECT # OF HTABS & SPACES.
;	OUTPUT:  BUFFER TO BE WRITTEN OUT TO CP/M FILE.
;
TRANS$WRITE:
	PUSH	HL		;SAVE PASCAL BUFFER ADDRESS
	LD	HL,DMA$BUFF 	;HL=DMA BUFFER ADDRESS
	LD	A,(BUFCNT)	;A=REMAINING BUFFER SPACE
	LD	C,A	
;
	LD	A,SECLTH	
	SUB	C		;A=128-REMAINING BUFF. SPACE
	LD	E,A
	LD	D,0		;DE=OFFSET WITHIN THE BUFFER
;
	ADD	HL,DE		;HL=ADDRESS WITHIN THE BUFFER
;
	EX	DE,HL		;DE=ADDRESS WITHIN THE DMA (CP/M) BUFFER
	POP	HL		;HL=ADDRESS WITHIN THE PASCAL BUFFER
	LD	B,SECLTH
;
TR$LOOP:
	LD	A,(CTLP$FLAG)
	OR	A		;SEE IF LAST CHAR WAS ^P
	JR	NZ,GETSPC
	LD	A,(HL)		;A=PASCAL BUFF(I)
	CP	CR
	JP	Z,TR$CR		;IF A CR CHAR
;
	CP	SPCHAR
	JP	NZ,TRCONT  	;IF NOT A SPCHAR
	LD	A,0FFH
	LD	(CTLP$FLAG),A	;DUMP BUT FLAG IT WAS A ^P
	JR	TRCNT1
;
;
TRCONT:
	LD	(DE),A		;DMA BUFF(I)=A
	INC	DE		;I=I+1
	DEC	C		;DECREMENT DMA BUFFER COUNT
	CALL	Z,WRTBUF	;IF BUFFER FULL WRITE IT OUT
TRCNT1:
	INC	HL
	DJNZ	TR$LOOP		;DECREMENT PASCAL BUFFER COUNT
TRCNT2:
	LD	A,C
	LD	(BUFCNT),A
	LD	A,(WRSTAT)
	RET	
;
TR$CR:	
	LD	(DE),A		;DMA BUFF(I)=CR
	INC	DE		;DE=DE+1
	DEC	C		;DECREMENT DMA BUFFER COUNT
	CALL	Z,WRTBUF	;IF BUFFER FULL, WRITE IT OUT
;
	LD	A,LF		;A=LF CHAR
	JR	TRCONT
;
;THIS ROUTINE REPLACES "^P" (DLE, = BLANK EXPANSION CODE) / FILLCHAR 
; (NO. OF SPACES TO ADD PLUS 20H) WITH HTABS UNTIL < 8 SPACES, THEN REMAINDER
; W/ SPACES.
;
GETSPC:	SUB	A
	LD	(CTLP$FLAG),A	;RESET FLAG
	LD	A,(HL)		;GET NO. OF SPACES
	SUB	20H
	JR	Z,TRCNT1	;SOMETIMES NO SPACES TO ADD
	PUSH	AF		;SAVE # OF SPACES
	RRC	A
	RRC	A
	RRC	A		;DIV 8
	AND	0FH
	JR	Z,NO$HTABS
	PUSH	HL
	LD	L,A
	LD	A,HTAB
HT$LOOP:
	LD	(DE),A
	INC	DE
	DEC	C
	CALL	Z,WRTBUF
	DEC	L
	JR	NZ,HT$LOOP
	POP	HL
NO$HTABS:
	POP	AF		;RESTORE # OF SPACES
	AND	7		;MOD 8
	JR	Z,TRCNT1	;NO SPACES TO PRINT
	PUSH	HL
	LD	L,A
	LD	A,BLANK
BK$LOOP:
	LD	(DE),A
	INC	DE
	DEC	C
	CALL	Z,WRTBUF
	DEC	L
	JR	NZ,BK$LOOP
	POP	HL
	JR	TRCONT
CTLP$FLAG:
	DEFB	0
;
;	ROUTINE TO WRITE A TEXT FILE BUFFER TO A CP/M FILE
;
WRTBUF:
	PUSH	BC		;SAVE REGISTERS
	PUSH	HL
;
   IF	TESTING
	LD	HL,DMA$BUFF
	LD	B,80H		;BUFFER COUNT
TSTLP:	LD	A,(HL)
	CP	EOF		;DUMP EOF'S
	JR	Z,SKP$P
	LD	E,A
	CALL	PRTCHAR
SKP$P:	INC	HL
	DJNZ	TSTLP
	SUB	A
   ELSE
	LD	DE,DMA$BUFF
	CALL	WRITE		;WRITE DMA BUFFER
   ENDIF
	LD	(WRSTAT),A	;SAVE WRITE STATUS
;
	POP	HL		;RESTORE REGISTERS
	POP	BC
	LD	C,SECLTH	;RESET DMA BUFFER
	LD	DE,DMA$BUFF
	RET	
;
;	ROUTINE TO FILL LAST TEXT FILE BUFFER WITH 
;	'EOF' AND WRITE IT OUT TO A CP/M FILE
;
WRTLBF:
	LD	A,(BUFCNT)
	CP	SECLTH
	RET	Z		;IF BUFFER EMPTY, RETURN
;
	LD	HL,DMA$BUFF 	;HL=ADDRESS OF DMA BUFFER
	LD	B,A		;B=NO OF REMAINING BUFFER SPACES
	LD	E,SECLTH
	SUB	E		;E=128-NO. OF EMPTY BUFF. SPACES
	LD	E,A
	LD	D,0		;DE=OFFSET WITHIN THE BUFFER
	ADD	HL,DE		;HL=ADDRESS WITHIN THE BUFFER
;
	LD	A,EOF
WL10:
	LD	(HL),A		;DMA BUFF(I)=EOF
	INC	HL		;I=I+1
	DJNZ	WL10		;DECR THE COUNT UNTIL ZERO.
;
	LD	A,SECLTH
	LD	(BUFCNT),A
	CALL	WRTBUF
	RET	
;
;	ROUTINE TO READ 4 BLOCK (512 BYTES EACH) LONG PASCAL
;	DISK DIRECTORY INTO THE BUFFER PASBUF.
;
READ$DIR:
	;BUFFER ADDRESS IS ALREADY IN BC-REG
	LD	E,4		;DIR IS 4 BLOCKS LONG
	LD	HL,2		;AND STARTS AT BLOCK #2
	CALL	SYSRD		;GO GET IT
	RET	
;
;
;	ROUTINE TO FIND A PASCAL FILE WITH THE TITLE IN SYSTLE
;	ON THE DIRECTORY POINTED BY DENTP.
;	ON RETURN:
;		SET A=1 IF FILE FOUND
;		    A=0 IF FILE NOT FOUND
;
FIND$PFILE:
	LD	C,77		;STOP AFTER THE 77'TH ENTRY
	LD	HL,(DENTP)  	;GET STARTING ENTRY
FP10:
	LD	DE,DTITLE   	;ADVANCE TO TITLE STRING
	ADD	HL,DE
	LD	DE,SYSTLE   	;SET DE-REG TO COMPARISON STRING
	LD	A,(DE)		;A=LENGTH OF THE TITLE
	INC	A		;A=LENGTH+1
	LD	B,A		;B=LENGTH+1
FP20:				;START COMPARING.
	LD	A,(DE)
	CP	(HL)
	JR	NZ,FP30	  	;IT'S NOT THIS ONE
	INC	DE		;HEY, WE'VE STILL GOT A CHANCE
	INC	HL
	DJNZ	FP20		;LOOP IF NOT END OF STRING.
	LD	A,1		;A=1, FILE FOUND
	RET	
;
FP30:
	LD	HL,(DENTP)  	;ON TO THE NEXT ENTRY
	LD	DE,DENTSZ
	ADD	HL,DE
	LD	(DENTP),HL
	DEC	C		;WAIT, IS THERE ANY DIR LEFT?
	JR	NZ,FP10
;
	PUTBUF	MSG6		;"PASCAL FILE NOT FOUND."
;
	XOR	A		; SET A=0
	RET	
;
;
;
PRTCHAR:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,2	;PRINT CHAR IN E ON CONSOLE; CHANGE TO A 5 IF YOU
	CALL	BDOS	; WANT TO PRINT THE DIRECTORY INSTEAD
	POP	HL
	POP	DE
	POP	BC
	RET
;
;
WDSKMSG:
	DEFB	CR,LF,'OOPS!  THE DISK IN DRIVE A: IS NOT A VALID PASCAL DISK.$'
;
;
;	ROUTINE TO PRINT PASCAL DIRECTORY
;
LIST$PDIR:
	LD	HL,PASBUF+DTITLE	;POINT TO VOLUME NAME CHAR COUNT
	LD	A,(HL)
	OR	A		;CHECK FOR VALID NO. (0 < N < 8)
	JR	Z,LPD$ER
	CP	8
	JR	C,LPD$OK
LPD$ER:	PUTBUF	WDSKMSG		;"WRONG DISK"
	POP	HL		;DUMP RETURN
	JP	INITFILES	;TRY AGAIN
LPD$OK:	PUTBUF	PDIR$QUERY
	GETCHR
	CP	'Y'
	RET	NZ
	PUTBUF	PVOLMSG		;'DIRECTORY OF VOLUME '
	LD	HL,PASBUF+DTITLE	;POINT TO VOLUME NAME CHAR COUNT
	LD	B,(HL)
	INC	HL
LP05:	LD	E,(HL)		;TYPE OUT VOLUME NAME
	CALL	PRTCHAR
	INC	HL
	DJNZ	LP05
	PUTBUF	COLONCRLF
	LD	C,77		;STOP AFTER THE 77'TH ENTRY
	PUSH	BC
	LD	HL,(DENTP)  	;GET STARTING ENTRY
	SUB	A
	LD	(EPL$COUNT),A	;ZERO ENTRIES/LINE COUNTER
LP10:
	LD	DE,DTITLE   	;ADVANCE TO TITLE STRING
	ADD	HL,DE
	LD	A,(HL)		;A=LENGTH OF THE TITLE
	CP	0E5H		;0 OR E5 MEAN EMPTY ENTRY
	JR	Z,NXT$ENT
	OR	A
	JR	Z,NXT$ENT
	INC	HL
	LD	B,A
	LD	D,A
LP20:				;START LISTING DIRECTORY
	LD	E,(HL)
	CALL	PRTCHAR
	INC	HL
	DJNZ	LP20		;LOOP IF NOT END OF STRING.
;
	LD	A,(EPL$COUNT)
	INC	A
	AND	3		;4 ENTRIES PER LINE
	LD	(EPL$COUNT),A	;UPDATE ENTRY COUNTER
	JR	Z,NXTLIN
	LD	A,20		;PAD TO 20TH CHAR PER ENTRY W/ BLANKS
	SUB	D		;ACTUAL CHAR PRINTED SO FAR
	LD	B,A		;NO. OF BLANKS TO PRINT
	LD	E,BLANK
LP40:	CALL	PRTCHAR
	DJNZ	LP40
	JR	NXT$ENT		;DONE W/ THIS ENTRY, ON TO NEXT
NXTLIN:	PUTBUF	CRLF
;
NXT$ENT:
	LD	HL,(DENTP)  	;ON TO THE NEXT ENTRY
	LD	DE,DENTSZ
	ADD	HL,DE
	LD	(DENTP),HL
	POP	BC
	DEC	C		;WAIT, IS THERE ANY DIR LEFT?
	RET	Z		;77 ENTRIES IN ALL
	PUSH	BC
	JR	LP10
;
EPL$COUNT:
	DEFB	0
PVOLMSG:
	DEFB	CR,LF,LF,'DIRECTORY OF VOLUME $'
COLONCRLF:
	DEFB	':',CR,LF,LF,'$'
;
;
; READ BLOCKS FROM PASCAL DISKETTE
;
SYSRD:
	PUSH	DE		;SAVE BLOCK COUNT
	PUSH	HL		;AND BLOCK NUMBER
	CALL	READ$PFILE  	;BUFFER IS ADVANCED BY 512 BYTES
	POP	HL
	POP	DE
	INC	HL		;ADVANCE TO NEXT BLOCK
	DEC	E		;BUT, BEFORE WE GO ON
	JR	NZ,SYSRD    	;  SEE IF WE'RE DONE
	RET	
¿
;	ROUTINE TO READ A BLOCK (512 BYTES) FROM A PASCAL FILE
;
;
READ$PFILE:
	ADD	HL,HL		;THERE ARE 4 IBM SECTORS TO A PASCAL BLOCK
	ADD	HL,HL		;  SO MULT LOGICAL BLOCK BY 4 TO GET 1ST SEC
	LD	E,4
RP10:				;THIS GETS CONFUSING
	PUSH	BC		;SET BUFFER ADDRESS
	PUSH	DE
	PUSH	HL
	CALL	SETDMA
	POP	HL		;NOW COMPUTE PHYSICAL TRACK-SECTOR
	PUSH	HL
	CALL	MAP		;MAP CONVERTS LOGICAL SECTOR IN HL-REG
	LD	C,H		;  INTO PHYSICAL TRACK, H-REG, SECTOR, L-REG
	PUSH	HL
	CALL	SETTRK
	POP	HL
	LD	C,L
	CALL	SETSEC
	CALL	READ		;AND READ THE DATA
	POP	HL
	POP	DE
	POP	BC
	PUSH	HL		;ADVANCE THE BUFFER ADDRESS
	LD	HL,128
	ADD	HL,BC
	LD	B,H
	LD	C,L
	POP	HL
	INC	HL		;ADVANCE BLOCK COUNT
	DEC	E		;THEN SEE IF WE CONTINUE
	JR	NZ,RP10
	RET			;LEAVE, WHEN DONE
;
;	TURN LSN INTO IBM TRACK-SECTOR
;
;	NOTE:	TRACK 00 IS NOT USED SO BLOCK 0
;		IS AT TRACK 01 SECTOR 1
;
;	ON ENTRY:	HL-REG HAS LOGICAL SECTOR NO.
;	ON EXIT:	H-REG HAS PHYSICAL TRACK
;			L-REG HAS PHYSICAL SECTOR
;
MAP:
	PUSH	BC
	PUSH	DE
;
	CALL	DIV26
	LD	A,L
	ADD	A,A
	LD	B,A
	LD	A,12
	CP	L
	JR	NC,M10
	INC	B
M10:	
	LD	C,E
	XOR	A
	LD	D,A
	LD	H,A
	LD	L,B
	LD	A,6
M20:
	ADD	HL,DE
	DEC	A
	JR	NZ,M20
	PUSH	BC
	CALL	DIV26
	POP	BC
	INC	L
	LD	H,C
	INC	H
	POP	DE
	POP	BC
	RET	
;
;
DIV26:	
	LD	BC,-26
	LD	E,0FFH
DIVL:	
	INC	E
	ADD	HL,BC
	LD	A,H
	OR	A
	JP	P,DIVL
	LD	BC,26
	ADD	HL,BC
	RET	
;
;	THIS ROUTINE EXTRACTS THE INFORMATION ABOUT THE PASCAL
;	FILE FROM THE DIRECTORY. ON INPUT DENTP POINTS TO THE 
;	DIRECTORY ENTRY. ON EXIT :
;	FBLOCK = 1ST BLOCK (ABSOLUTE) OF THE FILE ON PASCAL DISK
;	NBLOCK = TOTAL NUMBER OF BLOCKS IN THE FILE
;
OPEN$PFILE:
	LD	HL,(DENTP)  	;OBTAIN FIRST BLOCK NUMBER
	LD	C,(HL)
	INC	HL
	LD	B,(HL)		;BC=FILE'S FIRST BLOCK NUMBER
;
	LD	A,(PFTYPE)
	CP	1
	JR	NZ,OP10  	;IF NOT A TEXT FILE
;
	INC	BC
	INC	BC		;SKIP IST 2 BLOCKS OF TEXT FILE
OP10:
	LD	A,C
	LD	(FBLOCK),A	
	CPL	
	LD	C,A
;
	LD	A,B
	LD	(FBLOCK+1),A	;FBLOCK=FIRST BLOCK TO BE COPIED TO CP/M FILE
	CPL	
	LD	B,A
;
	INC	BC		;BC=2'S COMPLEMENT OF IST BLOCK NO.
;
	LD	HL,(DENTP)
	LD	DE,02H		;OFFSET FOR LAST BLOCK NUMBER
	ADD	HL,DE		;HL = ADDRESS OF LAST BLOCK NUMBER
;
	LD	E,(HL)
	INC	HL
	LD	D,(HL)		;DE = LAST BLOCK NUMBER
;
	EX	DE,HL		;HL = DE
;
	ADD	HL,BC		;HL = LAST BLOCK NO. - FIRST BLOCK NO.
;
	LD	A,L		;A  = NUMBER OF BLOCKS IN THE FILE
	LD	DE,NBLOCK
	LD	(DE),A		;NBLOCK = NUMBER OF BLOCKS IN THE FILE
	RET	
;
;	ROUTINE TO INITIALIZE PASCAL AND CP/M FILES. WHICH MEANS
;	TO OBTAIN THE FILE NAMES FROM THE USER BY CALLING GET$FILENAMES
;	AND TO OPEN BOTH FILES FOR PROCESSING.
;
INITFILES:
   IF NOT TESTING
	LD	C,13		;INITIALIZE FUNCTION
	LD	DE,0
	CALL	BDOS
   ENDIF
;
	PUTBUF	MSG3		;GET DISKS IN RIGHT PLACES
	GETCHR			;WAIT TO CONTINUE
;
	LD	HL,PASBUF+DENTSZ 	;SET DIRECTORY ENTRY POINTER TO
	LD	(DENTP),HL	;1ST ENTRY AFTER VOLUME NAME
;	
   IF NOT TESTING
	CALL	SAVE$DISK   	;SAVE CP/M DISK
   ENDIF
;
	LD	HL,P$DISK
	LD	C,(HL)		;C = PASCAL DISK NAME
	CALL	SELDSK
;
	LD	BC,PASBUF
	CALL	READ$DIR    	;READ DIRECTORY IN PASBUF
	CALL	LIST$PDIR
;
   IF NOT TESTING
	CALL	RESTORE$DISK
   ENDIF
;
	CALL	GET$FILENAMES
;
	LD	HL,PASBUF+DENTSZ 	;SET DIRECTORY ENTRY POINTER TO
	LD	(DENTP),HL	;1ST ENTRY AFTER VOLUME NAME
;	
	CALL	FIND$PFILE  	;FIND PASCAL FILE ENTRY IN THE DIR
	CP	01H
	JR	NZ,INITFILES	;IF FILE NOT FOUND
;
   IF NOT TESTING
	LD	HL,DFNAME	
	LD	E,(HL)		;E=NAME OF THE DISK WITH CP/M FILE
	CALL	SEL$DISK    	;SELECT CP/M DISK
;
	CALL	OPEN$CPMFILE
;
	CP	01H
	JR	NZ,INITFILES	;IF ERROR
   ENDIF
;
	CALL	OPEN$PFILE	
	LD	A,80H
	LD	(BUFCNT),A
	SUB	A
	LD	(WRSTAT),A
	RET	
;
;	CONVERT UCSD PASCAL FILE TO CP/M FORMAT FILE. THIS PROGRAM
;	ASSUMES THAT THE USER HAS INSERTED A PASCAL FORMAT DISK IN ONE
;	OF THE DRIVES AND A CP/M FORMAT DISK IN THE OTHER DISK. THIS
;	PROGRAM ALSO REQUIRES THAT BDOS AND BIOS BE RESIDENT IN THE
;	MEMORY. FOR READING THE UCSD PASCAL FORMAT FILES THE DISK I/O
;	IS CARRIED OUT BY DIRECTLY CALLING BIOS.
;
START:
	LD	HL,0H		;ZERO O_T HL
	ADD	HL,SP		;HL = SP
	LD	(OLDSP),HL  	;SAVE OLDSP
	LD	SP,STKTOP   	;NEW SP
;
;	SET UP BIOS CALLS
;
	LD	HL,(1)
	LD	L,TSELDSK
	LD	(SELDSK+1),HL
	LD	L,TSETTRK
	LD	(SETTRK+1),HL
	LD	L,TSETSEC
	LD	(SETSEC+1),HL
	LD	L,TSETDMA
	LD	(SETDMA+1),HL
	LD	L,TREAD
	LD	(READ+1),HL
CONTINUE:
	CALL	INITFILES
;
;     MAIN LOOP :
;
; 	READ 1 BLOCK (512 BYTES) FROM THE PASCAL FILE
;	AND THEN WRITE 4 SECTORS TO THE CP/M FILE.
;
CONV$LOOP:
   IF NOT TESTING
	CALL	SAVE$DISK
   ENDIF
;
	LD	HL,P$DISK
	LD	C,(HL)
	CALL	SELDSK		;CALL BIOS TO SELECT PASCAL DISK
;
	LD	HL,(FBLOCK)
	PUSH	HL		;SAVE CURRENT BLOCK NUMBER
	INC	HL		;HL = NEXT BLOCK NUMBER
	LD	(FBLOCK),HL 	;SAVE NEXT BLOCK NUMBER
	POP	HL
	LD	BC,PASBUF 	;BUFFER FOR READING IN PASCAL BLOCK
	CALL	READ$PFILE		;READ A BLOCK FROM PASCAL FILE
;
   IF NOT TESTING
	CALL	RESTORE$DISK	;RESTORE CP/M DISK
;
	LD	HL,DFNAME	
	LD	E,(HL)
	CALL	SEL$DISK    	;SELECT CP/M DISK
   ENDIF
;
	CALL	WRTCPM   	;WRITE 4 SECTORS TO CP/M FILE
;
	CP	01H
	JR	NZ,CONTINUE 	;IF ERROR
;
	LD	HL,NBLOCK
	DEC	(HL)		;NBLOCK = NBLOCK - 1
	JR	NZ,CONV$LOOP
;
   IF NOT TESTING
	CALL	CLOSE		;CLOSE CP/M FILE
   ENDIF
	JR	CONTINUE
;
;MESSAGES DISPLAYED BY CONOUT
;
PDIR$QUREY:
	DEFB	CR,LF,'WOULD YOU LIKE TO SEE THE PASCAL DIRECTORY (Y/N)? $'
MSG1:	DEFB	CR,LF,LF,'*UCSD FILE NAME?  $'
MSG2:	DEFB	CR,LF,'*ILLEGAL FILE NAME, TRY IGAIN!$'
MSG3:	DEFB	CR,LF,LF,'	PUT PASCAL DISK IN DRIVE A, CP/M DISK IN DRIVE B.'
	DEFB	CR,LF,'THEN TYPE A SPACE. $'
MSG4:	DEFB	CR,LF,'*CP/M FILE NAME?  $'
MSG5:	DEFB	CR,LF,'*ERROR IN CLOSING CP/M FILE.$'
MSG6:	DEFB	CR,LF,'*UCSD FILE NOT FOUND.$'
MSG7:	DEFB	CR,LF,'*NO SPACE AVAILABLE FOR CP/M FILE.$'
MSG8:	DEFB	CR,LF,'*ERROR IN EXTENDING FILE.$'
MSG9:	DEFB	CR,LF,'*ERROR -- END OF DISK DATA.$'
MSG10:	DEFB	CR,LF,'*ERROR -- NO MORE DIRECTORY SPACE.$'
MSG11:	DEFB	CR,LF,'*PASCAL TEXT FILE (Y OR N)?  $'
CRLF:	DEFB	CR,LF,'$'
;
OLDSP:	DEFS	2		;OLD STACK POINTER
PASBUF:	DEFS	2048		; 4*512 BLOCKS
DMA$BUFF	EQU	PASBUF+1024
	DEFS	256	
STKTOP	EQU	$
;
;
	END	START
