;===================================================================
;	MICRO RESOURCES CP/M -- MTX FILE TRANSFER PROGRAM
;		FOR USE WITH PERTEC PCC 2000 DD CP/M 1.4
;===================================================================
;
;
; THIS PROGRAM IS A UTILITY TO TRANSFER FILES FROM PCC 2000 MTX
; DOUBLE DENSITY DISKS TO CP/M DISKS OR TO TRANSFER A CP/M
; FILE TO A PCC 2000 MTX DOUBLE DENSITY DISK. A PROVISION HAS
; ALSO BEEN INCLUDED TO ALLOW EXAMINATION OF THE DIRECTORY OF
; THE MTX DISKETTE. THE OPERATION OF THIS PROGRAM ASSUMES
; THAT A CP/M SYSTEM DISK IS PRESENT IN DRIVE A: AND THAT THE
; MTX DATA DISK IS IN DRIVE B:. THE BIOS PRIMITIVES OF THE CP/M
; SYSTEM ARE ACCESSED DIRECTLY TO ALLOW READING AND WRITING
; OF THE DOUBLE DENSITY MTX DISK WITH IT'S 2 TO 1 LOGICAL
; TO PHYSICAL SECTOR MAPPING. NOTE THAT THIS SOFTWARE IS
; SPECIFICALLY TAYLORED TO FUNCTION THROUGH THE BIOS VECTORS
; OF THE PERTED IMPLEMENTATION OF CP/M 1.4 ON A PCC 2000 WITH
; DOUBLE DENSITY FLOPPY DISKS.
;
; THIS PROGRAM DOES NOT PACK THE FILES OF THE MTX DISK
; WHEN CREATING NEW FILES UPON TRANSFER FROM A CP/M FILE.
; SUBSEQUENT PACKING MAY DONE UPON THE DESTINATION MTX/PCC 2000
; SYSTEM.
;
; THE DIRECTORIES OF EITHER DISK WILL BE SCANNED FOR FILES
; OF SIMILAR NAMES BEFORE A TRANSFER IS ACTUALLY MADE. A PROMPT
; QUESTION WILL INDICATE WHEN THE FILE ALREADY EXISTS. IN THE
; CASE OF THE CP/M DISK THE INDICATED FILE MAY BE ERASED IF
; DESIRED. WITH THE MTX DISK A FILE WILL NOT BE DELETED
; AND THE NAME OF THE SOURCE FILE ON THE CP/M DISK WILL HAVE
; TO BE CHANGED TO A NAME NOT CURRENTLY ON THE MTX DISK.
; FILE NAMES OF MTX FILES ARE A MAXIMUM OF 6 ASCII CHARACTERS
; IN LENGTH SO THE CP/M FILE TRANSFERRED TO THE MTX DISK
; WILL HAVE ITS NAME TRUNCATED TO THE FIRST SIX CHARACTERS.
; THE MTX FILE TYPE WILL ALWAYS BE SET TO 00 AS AN INDEXED
; FILE TYPE. RECORD SIZE OF A MTX DESTINATION FILE WILL BE
; SET TO 128 BYTES. FOR MTX DESTINATION FILES THAT ARE AN
; ODD NUMBER OF 128 BYTE RECORDS THE LAST HALF IS BACK
; FILLED WITH AN ARBITRARY CHOICE OF ZEROS.
;
; FOR FILES TRANSFERRED TO CP/M FROM MTX DISKS THE FILE
; TYPE WILL BE CHECKED AND IF THE SOURCE TYPE IS NOT AN
; INDEXED FILE THEN THE TRANSFER TO CP/M WILL NOT BE
; MADE.
;
; THIS PROGRAM BUFFERS CP/M SECTOR READS UP 156 IN 
; LENGTH TO MAKE THE TRANSFER MORE EFFICIENT.
;
;
;
;
;*****************************************************************
;
;THIS SOFTWARE IS PROTECTED UNDER THE FOLLOWING COPYRIGHT
;AND MAY NOT BE REPRODUCED OR COPIED IN ANY FORM WITHOUT 
;SPECIFIC PERMISSION FROM MICRO RESOURCES.
;
;
;   COPYRIGHT (C) 1980
;
;
;	MICRO RESOURCES
;	MICHAEL J. KARAS
;	2468 HANSEN CT.
;	SIMI VALLEY, CA 93065
;	(805) 527-7922
;
;ANY QUESTIONS ABOUT THIS PROGRAM OR ITS APPLICATION
;CAN BE DIRECTED TO THE ABOVE ADDRESS OR TELEPHONE NUMBER.
;
;*********************************************************************
;
WBOOT	EQU	00		;CP/M WARM BOOT ENTRY ADDRESS
;
ERRLIM	EQU	10		;MAX ALLOWABLE ERRORS
; 
;DEFINE ASCII CHARACTERS USED
;
LF	EQU	10		;LINEFEED
CR	EQU	13		;CARRIAGE RETURN
; 
;
;START OF EXECUTABLE CODE
;
	ORG	100H
	CALL	START		;GO PRINT ID
	DB	'MICRO RESOURCES FILE TRANSFER UTILITY '
	DB	CR,LF,'  PCC 2000 CP/M <----> PCC 2000 MTX'
	DB	CR,LF,'          VER 1.31 11/15/80'
	DB	CR,LF,'$'
;
	DB	'COPYRIGHT 1980 MICRO RESOURCES'
	DB	'2468 HANSEN CT.'
	DB	'SIMI VALLEY, CA 93065'
	DB	'(805) 527-7922'
; 
START:
	POP	D		;GET ID MESSAGE
	MVI	C,PRINT
	CALL	BDOS		;PRINT ID MESSAGE
;
;INIT PRIVATE STACK
;
	LXI	H,0		;HL=0
	DAD	SP		;HL=STACK FROM CP/M
	SHLD	STACK		;..SAVE IT
	LXI	SP,STACK 	;SP=MY STACK
;
;INITIALIZE THE CP/M FILE BUFFERING PARAMETERS
;
	MVI	A,00H
	STA	SECINBF		;ZERO SEC IN BUFFER COUNTER
	STA	EOFLG		;RESET CP/M END OF FILE FLAG
	LXI	H,DBUF		;INITIALIZE BUFFER POINTER
	SHLD	SECPTR
;
;INITIALIZE THE JMPS TO CP/M BIOS
;
	CALL	INITADR
;
;SAVE PROGRAM OPTION
;
	CALL	PROCOPT
;
;MOVE THE FILENAME FROM FCB 2 TO FCB 1
;
	CALL	MOVEFCB
;
;JMP TO APPROPRIATE FUNCTION
;
	LDA	OPTION		;GET PROGRAM OPTION
;
	CPI	'R'		;READ MTX FILE?
	JZ	MTXRD
;
	CPI	'W'		;WRITE MTX FILE?
	JZ	MTXWR
;
	CPI	'D'     	;LOOK AT MTX DIRECTORY
	JZ	MTXDIR
;
	CPI	'H'		;GO TO HELP FILE PRINTOUT?
	JZ	HELP
;
;INVALID OPTION
;
	JMP	BADOPT
;
;
;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*				                        *
*	MTXDIR: LISTS MTX DIRECTORY TO CONSOLE          *
*       					        *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
;
;THE DIRECTORY OF THE DOUBLE DENSITY MTX
;DISK IN DRIVE B: IS LISTED TO THE CONSOLE IN
;A SINGLE COLUMN FORMAT. CONSOLE I/O IS DONE
;THROUGH CP/M SO THAT THE CTL-S AND CTL-P
;FUNCTIONS WILL WORK. THE LISTING ALSO STOPS
;AFTER EACH 16 ENTRIES HAVE BEEN TYPED
;
MTXDIR:
	CALL	ILPRT		;PRINT THE DIRECTORY HEADER LABEL
	DB	CR,LF,CR,LF,'            MTX DISK DIRECTORY'
	DB	CR,LF,CR,LF
	DB	'FILENAME  TYPE RECORD RECORD START  END ',CR,LF
	DB	'               COUNT  SIZE   SECTOR SECTOR',CR,LF
	DB	'------------------------------------------',CR,LF,0
;
	MVI	A,016		;SET PRINT LOOP COUNT
	STA	LISTCNT
	MVI	A,00		;SET SCAN FOR FIRST ENTRY
	JMP	MDIR2		;JUMP INTO LOOP
MDIR1:
	MVI	A,01		;SET SCAN CODE FOR NEXT ENTRY
MDIR2:
	CALL	MDIRSC		;SCAN FOR NEXT DIRECTORY ENTRY
	ORA	A		;SET FLAGES FOR RETURN CODE
	JNZ	MDEREX		;GO EXIT FOR FULL DISK DIRECTORY
	LDA	MDENT		;GET FIRST CHAR OF DERECTORY ENTRY
	CPI	0FFH		;MUST BE AT END OF THE LIST
	JZ	MDIREX
	CPI	000H		;CHECK IF THIS ENTRY IS EMPTY
	JZ	MDIR1		;SKIP PRINT IF SO
	CALL	PRTDIR		;PRINT THIS ENTRY
;
	LDA	LISTCNT		;GET LIST LINE COUNT
	DCR	A		;DECREMENT IT
	STA	LISTCNT		;STORE NEW COUNT BACK
	JNZ	MDIR1		;GO TO DO NEXT ENTRY
;
	CALL	ILPRT		;GIVE THE GUY A CHANCE TO STOP A LONG DIRECTORY
	DB	1,CR,LF,0
;
	MVI	A,016		;RESET PRINT LOOP COUNT
	STA	LISTCNT
	JMP	MDIR1		;GO START ON 16 MORE ENTRIES
;
MDIREX:
	CALL	ILPRT		;PRINT END OF LIST MESSAGE
	DB	CR,LF,CR,LF,'END OF MTX DISK DIRECTORY',CR,LF,0
MDIREX1:
	MVI	C,00H		;RESTORE SELECT OF DRIVE A:
	CALL	SELDSK		;DIRECT BIOS ACCESS
	JMP	EXIT		;DONE WITH DIRECTORY
;
MDEREX:
	CALL	ILPRT		;PRINT DISK FULL MESSAGE
	DB	CR,LF,'++MTX DISK DIRECTORY SPACE FULL++',CR,LF,0
;
	JMP	MDIREX1
;
;
;
;
;SCAN MTX DIRECTORY ROUTINE
;	ROUTINE ENTRY IS CODED TO ALLOW VARIABLE FUNCTIONS
;	CODE IS PASSED IN THE A REGISTER
;	CODES AS FOLLOWS:
;		00=OPEN SCAN FROM BEGINNING WITH LOGICAL SECTOR 1
;		01=GET NEXT DIRECTORY ENTRY
;
;	RETURN VALUE IS THE FIRST OR NEXT ENTRY AS SPECIFIED BY THE
;	FUNCTION CODE AND IS PASSED IN BUFFER "MDENT" WHICH IS 16
;	BYTES LONG.
;
;	THIS ROUTINE SCANS AND RETURNS ALL POSSIBLE ENTRIES
;	AND WHEN END OF LOGICAL SECTOR 25 IS ENCOUNTERED AN 
;	0FFH CODE IS RETURNED IN THE ACCUMULATOR. ELSE A 00H 
;	CODE IS RETURNED.
;
;
;
MDIRSC:
	ORA	A		;SET FLAGS FOR FUNCTION CHECK
	JNZ	NEXTENT		;FOR NON-ZERO CODE JUST GET NEXT ENTRY
;
;SETUP FOR BEGINNING OF SCAN
	MVI	A,000H		;SETUP START SECTOR OF DIRECTORY-1 
	STA	MDIRSEC
	MVI	A,016		;SET CURRENT SUBSCAN COUNT TO MAX
	STA	DIRSECI
;
NEXTENT:
	LDA	DIRSECI		;GET SECTOR INDEX
	CPI	016		;AT MAXIMUM ENTRY?
	JNZ	SAMSEC		;NO STAY WITH THIS SECTOR
	LXI	H,MDIRSEC
	INR	M		;INCREMENT CURRENT SECTOR NUMBER
	MOV	A,M		;GET THE SECTOR NUMBER
	CPI	026		;CHECK FOR THE LAST SECTOR
	JNZ	NXTSEC		;GO AROUND THE END OF TRACK EXIT
;
	MVI	A,0FFH		;SETUP EXIT FOR END OF DIRECTORY SPACE
	RET
;
NXTSEC:
	MOV	L,A
	MVI	H,00H
	CALL	MRDSEC		;GO READ MTX SECTOR
	LXI	H,MTXBUF	;MOVE INPUT BUFFER 
	LXI	D,DIRBUF	;..TO DIRECTORY BUFFER
	MVI	B,00H		;SET COUNT FOR FULL 256
	CALL	MOVE
	MVI	A,00
	STA	DIRSECI		;SET SECTOR INDEX TO MINIMUM
;
SAMSEC:
	LXI	H,DIRSECI
	MOV	B,M		;GET THE INDEX
	INR	M		;INCR THE SECTOR INDEX
	LXI	H,DIRBUF	;POINT TO THE MTX DIRECTORY BUFFER
	LXI	D,016		;LENGTH OF AN ENTRY
	MOV	A,B
	CPI	00H		;CHECK FOR ILLEGAL ZERO LOOP COUNT
	JZ	SAMSEC2
SAMSEC1:
	DAD	D		;LOOP TO MAKE OFFSET INDEX
	DCR	B
	JNZ	SAMSEC1
SAMSEC2:
	LXI	D,MDENT		;POINT TO ENTRY PASS BUFFER
	MVI	B,016		;SETUP LENGTH OF DIRECTORY ENTRY
	CALL	MOVE		;GO MOVE A DIRECTORY ENTRY
	LXI	H,MDENT		;SETUP TO STRIP MTX MBS'S FROM FILE NAME
	MOV	A,M		;SEE IF FIRST BYTE IS 0FFH
	CPI	0FFH		;IF SO WE SKIP MSB STRIP
	JZ	ENDRET
	MVI	B,06H		;CHARACTER COUNT
SAMSEC3:
	MOV	A,M		;GET CHARACTER OF NAME
	ANI	07FH		;STRIP MTX STRING CRAP
	MOV	M,A
	INX	H
	DCR	B		;DEC NAME BYTE COUNT
	JNZ	SAMSEC3
;
ENDRET:
	MVI	A,00H		;SET NORMAL RETURN FLAG
	RET
;
;
;
;
;DIRECTORY SCAN POINTER PARAMETERS
;
;
MDIRSEC	DB	00		;CURRENT DIRECTORY SECTOR NUMBER
DIRSECI	DB	00		;INDEX OF SCAN INTO CURRENT DIRECTORY SECTOR
DIRBUF	DS	256		;256 BYTE DIRECTORY ENTRY BUFFER
LISTCNT	DB	00		;DIRECTORY LIST ENTRY COUNT
;
;
;
;PRINT DIRECTORY ENTRY SUBROUTINE
;
;
PRTDIR:
	LXI	H,MDENT		;POINT TO DIRECTORY ENTRY BUFFER
	MVI	A,'B'
	CALL	CTYPE		;PRINT OUT DRIVE DESIGNATOR
	MVI	A,':'
	CALL	CTYPE
	MVI	B,06		;FILE NAME CHARACTER COUNT
NAMLP:
	MOV	A,M		;GET NAME CHARACTER
	CALL	CTYPE
	INX	H		;INCREMENT BUFFER POINTER
	DCR	B
	JNZ	NAMLP		;GO FOR NEXT NAME CHAR
;
	CALL	SP3		;THREE SPACES
	MOV	A,M		;GET TYPE BYTE
	CALL	HEXO		;SHOW IT
	INX	H		;INCREMENT THE BUFFER POINTER
;
	INX	H		;SKIP THE KEY SIZE FIELD
;
	CALL	SP2		;TWO SPACES
	MOV	D,M		;GET HIGH RECORD COUNT BYTE
	INX	H
	MOV	E,M		;GET LOW RECORD COUNT BYTE
	INX	H
	PUSH	H		;SAVE STRING POINTER
	CALL	DECPRT		;GO PRINT RECORD COUNT
	POP	H
;
	CALL	SP2		;TWO SPACES
	MOV	D,M		;GET HIGH RECORD SIZE BYTE
	INX	H
	MOV	E,M		;GET LOW RECORD SIZE BYTE
	INX	H
	PUSH	H		;SAVE STRING POINTER
	CALL	DECPRT		;GO PRINT RECORD SIZE
	POP	H
;
	CALL	SP2		;TWO SPACES
	MOV	D,M		;GET HIGH START SECTOR BYTE
	INX	H
	MOV	E,M		;GET LOW START SECTOR BYTE
	INX	H
	PUSH	H		;SAVE STRING POINTER
	CALL	DECPRT		;GO PRINT START SECTOR COUNT
	POP	H
;
	CALL	SP2		;TWO SPACES
	MOV	D,M		;GET HIGH END SECTOR BYTE
	INX	H
	MOV	E,M		;GET LOW END SECTOR BYTE
	DCX	D		;SET THE ENDING SECTOR TO REAL VALUE
	CALL	DECPRT		;GO PRINT ENDING SECTOR COUNT
;
	CALL	CRLF		;GET READY FOR NEXT LINE
	RET
;
;
;CONVERT AND PRINT A TWO BYTE VALUE AT CONSOLE IN DECIMAL
;
DECPRT:
	XCHG			;HL=WORD TO PRINT
	LXI	D,STRBUF	;POINT TO A CONVERT BUFFER
	CALL	BINASC		;CONVERT TO DECIMAL ASCII
	MVI	B,05H		;BYTE COUNT PRINT STRING
	LXI	H,STRBUF	;POINT TO THE PRINT BUFFER
DECPRT1:
	MOV	A,M		;GET PARAMETER CHARACTER
	PUSH	H
	CALL	CTYPE		;PRINT AT CONSOLE
	POP	H
	INX	H		;INCREMENT STRING POINTER
	DCR	B		;DEC BYTE COUNT
	JNZ	DECPRT1		;DONE WITH FIVE CHAR YET
	RET
;
;
;DECIMAL PRINTING ROUTINE TEMPORARY BUFFER STRING SPACE
;
STRBUF:
	DS	14		;RESERVE SOME BYTES FOR BUFFER
;
;
;CONSOLE SPACE PRINTING ROUTINE
;
SP5:
	MVI	A,' '		;5 SPACES
	CALL	CTYPE
SP4:
	MVI	A,' '		;4 SPACES
	CALL	CTYPE
SP3:
	MVI	A,' '		;3 SPACES
	CALL	CTYPE
SP2:
	MVI	A,' '		;2 SPACES
	CALL	CTYPE
SP1:
	MVI	A,' '		;1 SPACE
	CALL	CTYPE
	RET
;
;
;
;
* * * * * * * * * * * * * * * * * * * * *
*					*
*	MTXWR: COPIES FILE TO MTX       *
*					*
* * * * * * * * * * * * * * * * * * * * *
;
;THE CP/M FILE SPECIFIED IN THE COMMAND
;IS TRANSFERRED TO THE MTX DISK IN DRIVE B:
;
MTXWR:
	CALL	OPENFIL		;OPEN THE FILE
	LXI	H,0000H		;SET INITIAL RECORD COUNT TO ZERO
	SHLD	RECCNT
	MVI	H,26		;SET MAX UTILIZED SECTOR TO 26
				;..MTX STARTS FILES AT LOGICAL SECTOR 26
	SHLD	MAXSEC		;STORE AWAY
	MVI	A,00H
	STA	MTXEOF		;CLEAR MTX EOF FLAG
	MVI	A,00H		;SETUP FOR DIRECTORY SCAN
	JMP	SCAN2		;JUMP INTO LOOP
SCAN1:
	MVI	A,01H		;SET SCAN CODE FOR NEXT ENTRY
SCAN2:
	CALL	MDIRSC		;SCAN FOR NEXT DIRECTORY ENTRY
	ORA	A		;SET FLAGS FOR RETURN CODE
	JNZ	MDIRFUL		;EXIT FOR FULL DIRECTORY
;
	MVI	B,06		;SETUP FOR COMPARE OF FILE NAMES
	LXI	D,FCB+1		;..FOR 6 CHAR FROM CP/M FCB
	LXI	H,MDENT		;..TO MTX DIRECTORY ENTRY
	CALL	COMPARE		;GO DO THE COMPARE
	ORA	A		;CHECK	RETURN FLAG
	JNZ	FEXEX		;EXIT WITH FILE EXISTS MESSAGE
;
	LDA	MDENT		;FIRST BYTE OF NAME "FF" IF END
	CPI	0FFH		;ARE WE AT END OF LIST?
	JZ	SCAN3		;IF AT END THEN BALE OUT OF LOOP
;
	LHLD	MAXSEC		;SET TO UPDATE MAX USED SECTOR IF NEEDED
	XCHG
	LHLD	MDENT+14
	MOV	A,L		;COMPARE HIGH BYTE OF THIS ONE AND CURRENT MAX
	CMP	E
	JC	SCAN1		;NO UPDATE NEEDED
	JNZ	UDTMAX		;IF HIGH BYTE GREATER THEN UPDATE MAX
	MOV	A,H		;COMPARE LOW BYTES OF THIS ONE AND CURRENT MAX
	CMP	D
	JC	SCAN1		;NO UPDATE IF HIGHS EQUAL AND LOW NOT BIGGER
UDTMAX:
	SHLD	MAXSEC		;STORE NEW BIG SEC NUM AT MAX SEC LOC
	JMP	SCAN1		;GO DO MORE DIRECTORY ENTRIES
;
SCAN3:
	MVI	B,06		;MOVE FILE NAME INTO MTX DIRECTORY ENTRY
	LXI	H,FCB+1
	LXI	D,MDENT
SCANAP:
	MOV	A,M		;GET CHAR FROM FCB AREA
	ORI	080H		;PUT ON TOP BIT FOR MTX COMPATIBILITY
	STAX	D		;PUT IN NEW DIRECTORY ENTRY
	INX	H		;BUMP POINTERS
	INX	D
	DCR	B		;DEC NAME BYTE COUNT
	JNZ	SCANAP
;
	MVI	A,00		;SET THE NEW FILE TYPE TO ZERO AS INDEXED FILE
	STA	MDENT+6
	STA	MDENT+7		;SET KEYSIZE FIELD TO ZERO
	STA	MDENT+10	;SET HIGH BYTE OF RECORD SIZE TO ZERO
	MVI	A,080H
	STA	MDENT+11	;SET RECORD SIZE TO 128 BYTES AS CP/M
				;SECTOR SIZE
	LHLD	MAXSEC		;GET CURRENT MAX SECTOR AND PUT AS START
	SHLD	MDENT+12
;
	SHLD	MDENT+14	;SET INITIAL END SECTOR
MFILWLP:
	CALL	RDSECT		;READ A SECTOR FROM CP/M
	JC	UPDATE		;UPDATE MTX DIRECTORY IF DONE
	LHLD	RECCNT		;INC RECORD COUNT
	CALL	MTXINC
	SHLD	RECCNT
	LXI	H,080H		;MOVE SECTOR FROM CPM AREA
	LXI	D,MTXBUF
	CALL	MOVE128
	CALL	RDSECT		;READ ANOTHER SECTOR FROM CP/M
	JC	UPDAFIL		;UPDATE MTX DIRECTORY IF DONE
	LHLD	RECCNT		;INC RECORD COUNT
	CALL	MTXINC
	SHLD	RECCNT
	LXI	H,080H		;MOVE SECTOR FROM CP/M AREA
	LXI	D,MTXBUF+128
	CALL	MOVE128
WRTENT:
	LXI	H,MDENT+14	;POINT TO SECTOR TO BE WRITTEN
	MOV	D,M
	INX	H
	MOV	E,M
;
	LXI	H,07B8H		;CHECK IF DISK FULL
	MOV	A,H
	CMP	D
	JNZ	NOTFUL
	MOV	A,L
	CMP	E
	JZ	DSKFUL		;EXIT WITH MESSAGE IF DISK IS FULL
;
NOTFUL:
	XCHG			;HL=LOGICAL SECTOR TO WRITE
	CALL	MWRSEC		;GO WRITE CURRENT MTX SECTOR
	LHLD	MDENT+14	;INCREMENT END SECTOR
	CALL	MTXINC
	SHLD	MDENT+14
;
	LDA	MTXEOF		;SEE IF WE GOT TO END BEFORE
	ORA	A
	JNZ	UPDATE		;GO TO UPDATE DIRECTORY IF END
;
	JMP	MFILWLP		;GO TO GET THE NEXT SECTOR FROM CP/M
;
;
;CP/M EOF ENCOUNTERED ON MIDDLE OF SECTOR NEEDS FILLING TO END
;	WE ARBITRARILY CHOOSE TO FILL WITH ZEROS.
;
UPDAFIL:
	LXI	H,MTXBUF+128	;POINT TO SECOND HALF OF BUFFER
	MVI	B,128		;SET FILL COUNT
SHFILL:
	MVI	M,00H		;PUT IN A ZERO
	INX	H		;BUMP POINTER
	DCR	B		;DEC BYTE COUNT
	JNZ	SHFILL		;MORE TO DO?
;
	MVI	A,0FFH		;SET END OF FILE FLAG
	STA	MTXEOF
	JMP	WRTENT		;GO WRITE SECTOR
;
;FILE WRITTEN, NOW UPDATE THE DIRECTORY
;
UPDATE:
	XRA	A		;INITIALIZE TWO SECTOR UPDATE FLAG
	STA	U2SEC
;
	LHLD	RECCNT		;PUT RECORD COUNT INTO DIR ENTRY
	MOV	A,H		;CHECK IF THERE WAS NO CP/M FILE SIZE
	ORA	L
	JZ	NOSIZF		;EXIT IF FILE FIZE IS ZERO
	SHLD	MDENT+8
;
	LXI	H,DIRBUF 	;MOVE DIRECTORY SECTOR TO OUTPUT BUFFER
	LXI	D,MTXBUF
	MVI	B,00H		;SET THE COUNT FOR FULL 256 BYTE MOVE
	CALL	MOVE
	LDA	DIRSECI		;GET DIRECTORY SECTOR INDEX	
	LXI	H,MTXBUF-16
	LXI	D,16		;MAKE INSERT POINTER FOR NEW ENTRY
UPDAT:
	DAD	D
	DCR	A
	JNZ	UPDAT
;
	XCHG			;PUT INSERT POINTER IN DE
	LXI	H,MDENT		;POINT TO NEW DIRECTORY ENTRY
	MVI	B,16		;LENGTH OF DIRECTORY ENTRY
	CALL	MOVE		;MOVE INTO OUTPUT BUFFER
	LDA	DIRSECI		;SEE IF NEW ENTRY IS LAST IN SECTOR
	CPI	016
	JNZ	ONESEC		;JUMP TO PUT END ENTRY IN THIS SECTOR
;
	MVI	A,0FFH		;SET U2SEC FLAG
	STA	U2SEC
	JMP	WDIRSEC		;GO WRITE CURRENT SECTOR
;
ONESEC:
	MVI	A,0FFH	 	;PUT "FF"ed OUT ENTRY INTO THIS SECTOR
	MVI	B,16		;ENTRY LENGTH-- DE OK FROM PREVIOUS MOVE
ONESEC1:
	STAX	D		;PUT FF IN ENTRY
	INX	D		;INC FF'ING POINTER	
	DCR	B		;DEC BYTE COUNTER
	JNZ	ONESEC1
;
WDIRSEC:
	LDA	MDIRSEC		;GET THE DIRECTORY SECTOR NUMBER
	MVI	H,0		;MAKE TWO BYTE LOGICAL SECTOR NUMBER
	MOV	L,A
	CALL	MWRSEC		;GO WRITE THE SECTOR OF DIRECTORY
	LDA	U2SEC		;CHECK IF WE SHOULD UPDATE TWO SECTORS
	ORA	A
	JNZ	GETSEC		;IF SO GO PREPARE FOR IT
;
	JMP	DONE		;ALL DONE
;
GETSEC:
	XRA	A
	STA	U2SEC		;RESET UPDATE TWO SECTORS FLAG
	LDA	MDIRSEC		;GET CURRENT DIRECTORY SECTOR NUMBER
	INR	A
	STA	MDIRSEC		;STORE THIS NEW ONE AWAY
	CPI	01BH		;SEE IF DIRECTORY FULL HERE
	JZ	UDFULL		;GO PRINT EXIT MESSAGE
;
	MVI	H,0		;MAKE TWO BYTE SECTOR NUMBER
	MOV	L,A
	CALL	MRDSEC		;GO READ NEXT SECTOR
;
	LXI	D,MTXBUF	;POINT TO START OF DIRECTORY SECTOR
	JMP	ONESEC		;GO END THE PROCESS OF UPDATE ON SECOND SEC
;
;
;SUBROUTINE TO INCREMENT A REVERSE ORDER TWO BYTE PAIR
;
MTXINC:
	MOV	A,H		;SET TO INCREMENT END SECTOR
	MOV	H,L
	MOV	L,A
	INX	H		;INC THE END SECTOR
	MOV	A,H
	MOV	H,L
	MOV	L,A
	RET
;
;
;EXIT POINT FROM WRITE IF MTX DIRECTORY IS FULL UPON UPDATE
;
UDFULL:
	CALL	ILPRT		;PRINT DIRECTORY FULL MESSAGE
	DB	CR,LF,'++MTX DIRECTORY FULL UPON UPDATE++'
	DB	CR,LF,'++TRANSFERRED FILE EXISTS ON DISK++',CR,LF,0
	JMP	EXIT
;
;
;EXIT POINT FROM WRITE IF MTX DIRECTORY IS FULL
;
MDIRFUL:
	CALL	ILPRT		;PRINT DIRECTORY FULL MESSAGE
	DB	CR,LF,'++MTX DIRECTORY MUST BE FULL++',CR,LF,0
	JMP	EXIT
;
;
;EXIT POINT FROM WRITE IF NOTHING IN CP/M FILE
;
NOSIZF:
	CALL	ILPRT		;PRINT NO SIZE TO CP/M FILE
	DB	CR,LF,'++CP/M FILE EMPTY-NO MTX FILE CREATED++',CR,LF,0
	JMP	EXIT
;
;
;EXIT POINT FROM WRITE IF MTX DISK GETS FULL
;
DSKFUL:
	CALL	ILPRT		;PRINT DISK FULL MESSAGE
	DB	CR,LF,'++FILE TOO BIG - MTX DISK FULL++'
	DB	CR,LF,'++DIRECTORY NOT UPDATED WITH NEW FILE++',CR,LF,0
	JMP	EXIT
;
;EXIT POINT FROM WRITE IF MTX FILE ALREADY EXISTS
;
FEXEX:
	CALL	ILPRT
	DB	CR,LF,'++MTX FILE ALREADY EXISTS++'
	DB	CR,LF,'++CHANGE NAME OF SOURCE FILE ON CP/M++',CR,LF,0
	JMP	EXIT
;
;
;
;
;PARAMETER STORAGE AREA FOR MTX WRITE ROUTINE
;
U2SEC	DB	00		;USED TO SEE IF TWO SECTORS S/B SET IN DIR
MAXSEC	DW	00		;STORAGE FOR MAXIMUM USED LOGICAL SECTOR
MTXEOF	DB	00		;FLAG TO INDICATE IF END OF MTX FILE
RECCNT	DW	00		;CURRENT MTX FILE SIZE IN 128 BYTE RECORDS
;
;
;
* * * * * * * * * * * * * * * * * * * * *
*					*
*	MTXRD: COPY FILE FROM MTX       *
*					*
* * * * * * * * * * * * * * * * * * * * *
;
;FETCHES AN MTX FILE AND THEN WRITES
;IT TO A CP/M FILE ON DRIVE A:
;
MTXRD:
	CALL	ERASFIL		;ERASE THE CP/M FILE
	CALL	MAKEFIL		;..THEN MAKE NEW
	MVI	A,00		;SET SCAN FOR FIRST ENTRY
	JMP	FIND2		;JUMP INTO LOOP
FIND1:
	MVI	A,01		;SET SCAN CODE FOR NEXT ENTRY
FIND2:
	CALL	MDIRSC		;SCAN FOR NEXT DIRECTORY ENTRY
	ORA	A		;SET FLAGES FOR RETURN CODE
	JNZ	MDNFEX		;GO EXIT FOR NOT FOUND MTX FILE
	LDA	MDENT		;FIRST CHAR OF FILE NAME
	CPI	0FFH		;MUST BE AT END OF THE LIST IF "FF"
	JZ	MDNFEX		;GO EXIT FOR NOT FOUND MTX FILE
	MVI	B,06H		;SETUP FOR COMPARE FILE NAMES
	LXI	D,FCB+1		;..FOR 6 CHAR FROM FCB TO
	LXI	H,MDENT		;..MTX DIRECTORY ENTRY
	CALL	COMPARE		;GO COMPARE
	ORA	A		;CHECK THE RETURN FLAG
	JZ	FIND1		;NO NAME MATCH IF ZERO	
;
;FOUND FILE ON MTX DISK
;
RMTXLP:
	LDA	MDENT+6
	CPI	000H		;CKECK IF SOURCE FILE IS OF INDEXED TYPE
	JNZ	NINDX		;EXIT WITH ERROR IF NOT INDEXED FILE
;
	LXI	H,MDENT+12	;GET LOGICAL SECTOR NUMBER
	MOV	D,M		;..HIGH BYTE
	INX	H
	MOV	E,M		;..LOW BYTE
	INX	D		;INCREMENT SECTOR FOR NEXT TIME
	MOV	M,E		;STORE BACK AWAY
	DCX	H
	MOV	M,D
	DCX	D		;GET BACK NUMBER OF ONE WE WANT
	XCHG			;LOGICAL SECTOR COUNT TO HL
	CALL	MRDSEC		;GO READ IT FROM MTX DISK
;
	LXI	H,MTXBUF	;MOVE 1ST HALF TO CP/M AREA
	LXI	D,080H		;..TO 128 BYTE PASS BUFFER FOR CP/M
	CALL	MOVE128
	CALL	WRSECT		;MOVE DATA TO CP/M QUEUE
;
	LXI	H,MTXBUF+128	;MOVE 2ND HALF TO CP/M AREA
	LXI	D,080H		;..TO 128 BYTE PASS BUFFER FOR CP/M
	CALL	MOVE128
	CALL	WRSECT		;MOVE 2ND HALF DATA TO CP/M QUEUE
;
	LHLD	MDENT+12	;GET CURRENT INCREMENTED LOGICAL SECTOR
	XCHG			;TO (DE). NOTE HI/LOW ORDER WRONG
	LHLD	MDENT+14	;GET DIRECTORY ENTRY 1+ ENDING SECTOR TO (HL)
	MOV	A,H		;COMPARE LOW BYTES OF SECTOR NUMBERS
	CMP	D
	JNZ	RMTXLP		;BOTH MUST COMPARE FOR EQUALITY
	MOV	A,L		;NOW COMPARE HIGH BYTES OF SECTOR NUMBERS
	CMP	E
	JNZ	RMTXLP		;AGAIN NO COMPARE SO MORE TO READ
;
;GOT EOF ON SECTOR - FLUSH BUFFERS, END
;
	CALL	WRBLOCK		;WRITE THE LAST BLOCK
	CALL	CLOSFIL		;CLOSE THE FILE
	JMP	DONE		;GO PRINT END OF TRANSFER MESSAGE
;
;
;
MDNFEX:
	CALL	ILPRT		;PRINT MTX FILE NOT FOUND
	DB	CR,LF,'++MTX FILE NOT FOUND++',CR,LF,0
	JMP	EXIT
;
;
;
NINDX:
	CALL	ILPRT		;PRINT MTX FILE TYPE NOT INDEXED TYPE
	DB	CR,LF,'++MTX FILE NOT AN INDEXED FILE++',CR,LF,0
	JMP	EXIT
;
;
;
* * * * * * * * * * * * * * * * * * * * *
*					*
*		SUBROUTINES		*
*					*
* * * * * * * * * * * * * * * * * * * * *
;
;
;
;---->	ERASFIL: ERASE THE INCOMING FILE.
;
;IF IT EXISTS, ASK IF IT MAY BE ERASED.
;
ERASFIL:
	LXI	D,FCB		;POINT TO CTL BLOCK
	MVI	C,SRCHF 	;SEE IF IT..
	CALL	BDOS		;..EXISTS
	INR	A		;FOUND?
	RZ			;..NO, RETURN
	CALL	ILPRT		;PRINT:
	DB	'++CP/M FILE EXISTS, TYPE Y TO ERASE: ',0
	CALL	KEYIN		;GET A CHARACTER FROM CONSOLE
	ANI	5FH		;MAKE UPPER CASE
	CPI	'Y'		;WANT ERASED?
	JNZ	EXIT		;QUIT IF NOT ERASE
	CALL	CRLF		;BACK TO START OF LINE
;
;ERASE OLD FILE
;
	LXI	D,FCB		;POINT TO FCB
	MVI	C,ERASE		;GET BDOS FNC
	CALL	BDOS		;DO THE ERASE
	RET			;FROM "ERASFIL"
;
;---->	MAKEFIL: MAKES THE FILE TO BE RECEIVED
;
MAKEFIL:
	LXI	D,FCB		;POINT TO FCB
	MVI	C,MAKE		;GET BDOS FNC
	CALL	BDOS		;TO THE MAKE
	INR	A		;FF=BAD?
	RNZ			;OPEN OK
;
;DIRECTORY FULL - CAN'T MAKE FILE
;
	CALL	ERXIT
	DB	'++ERROR - CANNOT MAKE FILE++',CR,LF
	DB	'++DIRECTORY MUST BE FULL++',CR,LF,'$'
;
;---->	OPENFIL: OPENS THE FILE TO BE SENT
;
OPENFIL:
	LXI	D,FCB		;POINT TO FILE
	MVI	C,OPEN		;GET FUNCTION
	CALL	BDOS		;OPEN IT
	INR	A		;OPEN OK?
	RNZ			;FILE OPENED OK
	CALL	ERXIT		;..NO, ABORT
	DB	'++CANNOT OPEN CP/M FILE++',CR,LF,'$'
;
;
;---->	CLOSFIL: CLOSES THE RECEIVED FILE
;
CLOSFIL:
	LXI	D,FCB		;POINT TO FILE
	MVI	C,CLOSE		;GET FUNCTION
	CALL	BDOS		;CLOSE IT
	INR	A		;CLOSE OK?
	RNZ			;..YES, RETURN
	CALL	ERXIT		;..NO, ABORT
	DB	'++CANNOT CLOSE CP/M FILE++',CR,LF,'$'
;
;---->	RDSECT: READS A SECTOR
;
;FOR SPEED, THIS ROUTINE BUFFERS UP 156
;SECTORS AT A TIME.
;
RDSECT:
	LDA	SECINBF		;GET # SECT IN BUFF.
	DCR	A		;DECREMENT..
	STA	SECINBF		;..IT
	CPI	0FFH		;CHECK IF SECTOR COUNT UNDERFLOWS
	JZ	RDBLOCK		;EXHAUSTED?  NEED MORE.
	LHLD	SECPTR		;GET POINTER
	LXI	D,80H		;TO DATA 
	CALL	MOVE128		;MOVE TO BUFFER
	SHLD	SECPTR		;SAVE BUFFER POINTER
	STC
	CMC			;CLEAR CARRY SO EOF NOT INDICATED
				;ON NORMAL RETURN
	RET			;FROM "READSEC"
;
;BUFFER IS EMPTY - READ IN ANOTHER BLOCK OF 156
;
RDBLOCK:
	LDA	EOFLG		;GET EOF FLAG
	CPI	1		;IS IT SET?
	STC			;TO SHOW EOF
	RZ			;GOT EOF
	MVI	C,00H		;SELECT DRIVE A:
	CALL	SELDSK
	MVI	C,0		;SECTORS IN BLOCK
	LXI	D,DBUF		;TO DISK BUFFER
RDSECLP:
	PUSH	B
	PUSH	D
	MVI	C,STDMA		;SET DMA..
	CALL	BDOS		;..ADDR
	LXI	D,FCB
	MVI	C,READ
	CALL	BDOS
	POP	D
	POP	B
	ORA	A		;READ OK?
	JZ	RDSECOK		;YES
	DCR	A		;EOF?
	JZ	REOF		;GOT EOF
;
;READ ERROR
;
	CALL	ERXIT
	DB	'++CP/M FILE READ ERROR++',CR,LF,'$'
;
RDSECOK:
	LXI	H,80H
	DAD	D		;TO NEXT BUFF
	XCHG			;BUFF TO DE
	INR	C		;MORE SECTORS?
	MOV	A,C		;GET COUNT
	CPI	156		;DONE?
	JZ	RDBFULL		;..YES, BUF IS FULL
	JMP	RDSECLP		;READ MORE
;
REOF:
	MVI	A,1
	STA	EOFLG		;SET EOF FLAG
	MOV	A,C
;
;BUFFER IS FULL, OR GOT EOF
;
RDBFULL:
	STA	SECINBF		;STORE SECTOR COUNT
	LXI	H,DBUF		;INIT BUFFER..
	SHLD	SECPTR		;..POINTER
	LXI	D,80H		;RESET..
	MVI	C,STDMA		;..DMA..
	CALL	BDOS		;..ADDR
	JMP	RDSECT		;PASS SECT TO CALLER
;
;---->	WRSECT: WRITE A SECTOR
;
;WRITES THE SECTOR INTO A BUFFER. WHEN 156
;HAVE BEEN WRITTEN, WRITES THE BLOCK TO DISK.
;
;ENTRY POINT "WRBLOCK" FLUSHES THE BUFFER AT EOF.
;
WRSECT:
	LHLD	SECPTR		;GET BUFF ADDR
	XCHG			;TO DE FOR MOVE
	LXI	H,80H		;FROM HERE
	CALL	MOVE128		;MOVE TO BUFFER
	XCHG			;SAVE NEXT..
	SHLD	SECPTR		;..BLOCK POINTER
	LDA	SECINBF		;BUMP THE..
	INR	A		;..SECTOR #..
	STA	SECINBF		;..IN THE BUFF
	CPI	156		;HAVE WE 156?
	RNZ			;NO, RETURN
;
;---->	WRBLOCK: WRITES A BLOCK TO DISK
;
WRBLOCK:
	LDA	SECINBF		;# SECT IN BUFFER
	ORA	A		;0 MEANS END OF FILE
	RZ			;NONE TO WRITE
	PUSH	PSW		;SAVE SECINBUF
	MVI	C,00H		;SELECT DISK DRIVE A:
	CALL	SELDSK
	POP	PSW		;GET SECINBUF BACK
	MOV	C,A		;SAVE COUNT
	LXI	D,DBUF		;POINT TO DISK BUFF
DKWRLP:
	PUSH	H
	PUSH	D
	PUSH	B
	MVI	C,STDMA		;SET DMA
	CALL	BDOS		;TO BUFFER
	LXI	D,FCB		;THEN WRITE
	MVI	C,WRITE		;..THE..
	CALL	BDOS		;..BLOCK
	POP	B
	POP	D
	POP	H
	ORA	A
	JNZ	WRERR		;OOPS, ERROR
	LXI	H,80H		;LENGTH OF 1 SECT
	DAD	D		;HL= NEXT BUFF
	XCHG			;TO DE FOR SETDMA
	DCR	C		;MORE SECTORS?
	JNZ	DKWRLP		;..YES, LOOP
	XRA	A		;GET A ZERO
	STA	SECINBF		;RESET # OF SECTORS
	LXI	H,DBUF		;RESET BUFFER..
	SHLD	SECPTR		;..POINTER
	RET
;
WRERR:
	CALL	ERXIT		;EXIT W/MSG:

	DB	'++ERROR WRITING CP/M FILE++',CR,LF,'$'
;
;
;
;---->	PROCOPT: PROCESS COMMAND OPTIONS
; 
;SAVES THE PROGRAM OPTION IN 'OPTION';
;
PROCOPT:
	LXI	D,FCB+1		;TO PROGRAM OPT.
	LDAX	D		;GET OPTION
	STA	OPTION		;SAVE IT
	RET			;FROM 'PROCOPT'
;
;DONE - CLOSE UP SHOP
;
DONE:
	CALL	ILPRT
	DB	CR,LF,'TRANSFER COMPLETE'
	DB	CR,LF,0
;
	JMP	EXIT		;DONE, GO BACK
;
;
;ROUTINE MOVES THE FILENAME FROM THE SECOND FCB
;TO THE FIRST
;
MOVEFCB:
	LXI	H,FCB+16 	;FROM
	LXI	D,FCB		;TO
	MVI	B,16		;LEN
	CALL	MOVE		;DO THE MOVE
	XRA	A		;GET 0
	STA	FCBSNO		;ZERO SECTOR #
	STA	FCB		;..AND DRIVE DESIGNATOR
	STA	FCBEXT		;..AND EXTENT
	RET
;
;
; MTX DOUBLE DENSITY FLOPPY DISK ACCESS ROUTINE FOR DRIVE B:
; ON AN ICOM 3812 RUNNING LIFEBOAT CP/M VER 1.4
;
;
;
;---->  MRDSEC: READS A GIVEN SECTOR ACCORDING
;		TO MTX CONVENTION. REGISTER PAIR (HL)
;		CONTAINS THE LOGICAL SECTOR NUMBER.
;
MRDSEC:
	CALL	MTXSCAL		;GET OURSELVES A TRACK AND SECTOR
	PUSH	H		;SAVE SECTOR NUMBER
	PUSH	B		;SAVE TRACK NUMBER
	MVI	C,01H		;LETS SELECT B: FOR MTX ACCESS
	CALL	SELDSK		;LET BIOS SELECT DISK
	POP	B		;GET TRACK NUMBER FOR NEXT CALL
	MOV	C,B
	CALL	SETTRK		;HAVE BIOS HANDLE TRACK SEEK
	LXI	B,MTXBUF	;SET DMA ADDRESS FOR BUFFER
	CALL	SETDMA
	POP	H		;GET SECTOR NUMBER
	MOV	C,L		;PUT FOR BIOS
	PUSH	B		;SAVE FOR 2ND 128 BYTES
	CALL	SETSEC		;BIOS POINT TO FIRST HALF
	CALL	RDSEC		;READ THE SECTOR INTO BUFFER
	ORA	A		;LOOK TO SEE IF READ ERROR
	JNZ	MRDER		;TELL OF MTX READ ERROR
	LXI	B,MTXBUF+128
	CALL	SETDMA		;FIX DMA ADDR FOR 2ND HALF
	POP	B		;FIX FOR SEC +1 TO GET 2ND HALF
	INR	C
	CALL	SETSEC
	CALL	RDSEC		;READ 2ND HALF
	ORA	A		;LOOK AGAIN TO SEE IF READ ERROR
	JNZ	MRDER
	RET			;GO BACK IF NO ERRORS DETECTED
;
;MTX DISK READ ERROR
;
MRDER:
	CALL	ERXIT
	DB	'++MTX DISK READ ERROR++$'
;
;
;---->  MWRSEC:	WRITES A GIVEN SECTOR ACCORDING 
;		TO MTX CONVENTION. REGISTER PAIR (HL)
;		CONTAINS THE LOGICAL SECTOR NUMBER.
;
;
MWRSEC:
	CALL	MTXSCAL		;GET OURSELVES A TRACK AND SECTOR
	PUSH	H		;SAVE SECTOR NUMBER
	PUSH	B		;SAVE TRACK NUMBER
	MVI	C,01H		;LETS SELECT B: FOR MTX ACCESS
	CALL	SELDSK		;LET BIOS SELECT DISK
	POP	B		;GET TRACK NUMBER FOR NEXT CALL
	MOV	C,B
	CALL	SETTRK		;HAVE BIOS HANDLE TRACK SEEK
	LXI	B,MTXBUF	;SET DMA ADDRESS FOR BUFFER
	CALL	SETDMA
	POP	H		;GET SECTOR NUMBER
	MOV	C,L		;PUT FOR BIOS
	PUSH	B		;SAVE FOR 2ND 128 BYTES
	CALL	SETSEC		;BIOS POINT TO FIRST HALF
	CALL	WRTSEC		;WRITE THE SECTOR FROM BUFFER
	ORA	A		;LOOK TO SEE IF WRITE ERROR
	JNZ	MWRER		;TELL OF MTX WRITE ERROR
	LXI	B,MTXBUF+128
	CALL	SETDMA		;FIX DMA ADDR FOR 2ND HALF
	POP	B		;FIX FOR SEC +1 TO GET 2ND HALF
	INR	C
	CALL	SETSEC
	CALL	WRTSEC		;WRITE 2ND HALF
	ORA	A		;LOOK AGAIN TO SEE IF WRITE ERROR
	JNZ	MWRER
	RET			;GO BACK IF NO ERRORS DETECTED
;
;MTX DISK WRITE ERROR
;
MWRER:
	CALL	ERXIT
	DB	'++MTX DISK WRITE ERROR++$'
;
;
;---->  MTXSCAL: TRANSLATES MTX LOGICAL SECTOR TO
;		 TRACK AND PHYSICAL SECTOR NUMBERS
;
;		 ENTRY WITH DOUBLE BYTE LOGICAL SECTOR
;		 	NUMBER IN (HL).
;		 EXIT WITH TRACK IN (B) AND PHYSICAL SECTOR
;			IN (L)
;
;		 NOTE THAT THE SUBSEQUENT ROUTINE WILL HAVE
;		 READ SECTORS (L) AND (L+1) TO OBTAIN ALL OF
;		 THE MTX SECTORS DATA.
;
MTXSCAL:
	LXI	D,-26		;SET TO CALCULATE TRACK NUMBER
	MVI	B,00H		;INITIAL TRACK COUNTER
TRCLP1:
	INR	B		;TRACK LOOP COUNTER INCREMENT
	DAD	D		;SUBTRACT SECTOR NUMBERS MOD 26		
	JC	TRCLP1		;LOOP IN SUBTRACT TILL WE UNDERFLOW
;
	LXI	D,26		;SET THE HL PAIR TO REAL LOGICAL SECTOR
				; ON THIS TRACK
	DAD	D
;
	XCHG			;PUT LOGICAL SECTOR IN DE FOR NOW
	LXI	H,TRANTBL	;GET BASE OF TRANSLATION TABLE
	DAD	D		;INDEX INTO TABLE
	MOV	L,M		;GET PHYSICAL NUMBER
	RET
;
;
;MTX LOGICAL TO PHYSICAL SECTOR TRANSLATION TABLE
;	TABLE ENTRIES ARE IN LOGICAL ORDER
;	TRACK LOGICAL SECTOR ZERO MAPS TO PHYSICAL #1
;	ALSO NOTE THAT THIS TABLE ASSUMES THAT DISK
;	SOFTWARE THINKS TRACK HAS 52 128 BYTE SECTORS
;
TRANTBL:
	DB	01H
	DB	21H
	DB	0DH
	DB	2DH
	DB	19H
	DB	05H
	DB	25H
	DB	11H
	DB	31H
	DB	1DH
	DB	09H
	DB	29H
	DB	15H
	DB	2BH
	DB	17H
	DB	03H
	DB	23H
	DB	0FH
	DB	2FH
	DB	1BH
	DB	07H
	DB	27H
	DB	13H
	DB	33H
	DB	1FH
	DB	0BH
;
;
;---->	INITADR: INIT'S CP/M BDOS ADDRESSES
;
;THIS ROUTINE FILLS IN THE ADDRESSES OF VARIOUS
;JUMP VECTOR ENTRY POINTS SO THAT CP/M BDOS
;IS BYPASSED WHILE ACCESSING THE MTX DISK
;IN DRIVE B: SO THAT THE MODIFIED LOGICAL
;SECTORING MAY BE USED.
;
INITADR:
	LHLD	1		;GET WARM BOOT ADDR
	LXI	D,015H		;OFFSET TO HOME VECTOR
	DAD	D		;TO HOME DISK ROUTINE
	SHLD	HOME+1  	;SET INTERNAL HOME VECTOR
	LXI	D,003H		;OFFSET TO NEXT VECTOR
	DAD	D		;TO SELECT DISK ROUTINE
	SHLD 	SELDSK+1	;SET INTERNAL SELDSK VECTOR
	DAD	D		;TO SET TRACK ROUTINE
	SHLD	SETTRK+1	;SET INTERNAL SETTRK VECTOR
	DAD	D		;TO SET SECTOR ROUTINE
	SHLD	SETSEC+1	;SET INTERNAL SETSEC VECTOR
	DAD	D		;TO SET DMA ADDRESS ROUTINE
	SHLD	SETDMA+1	;SET INTERNAL SETDMA VECTOR
	DAD	D		;TO READ SECTOR ROUTINE
	SHLD	RDSEC+1		;SET INTERNAL READ VECTOR
	DAD	D		;TO WRITE SECTOR ROUTINE
	SHLD	WRTSEC+1	;SET INTERNAL WRITE VECTOR
;
	RET
;
;
;----> ENTRY POINTS FOR BIOS DISK ACCESS PRIMATIVES-ADDRESSES SETUP AT INIT
;
HOME:
	JMP	$-$		;HOME DISK INTERNAL VECTOR
SELDSK:
	JMP	$-$		;SELECT DISK INTERNAL VECTOR
SETTRK:
	JMP	$-$		;SET TRACK INTERNAL VECTOR
SETSEC:
	JMP	$-$		;SET SECTOR INTERNAL VECTOR
SETDMA:
	JMP	$-$		;SET DMA ADDRESS INTERNAL VECTOR
RDSEC:
	JMP	$-$		;READ SECTOR INTERNAL VECTOR
WRTSEC:
	JMP	$-$		;WRITE SECTOR INTERNAL VECTOR
;
;
;
;SETUP A DATA BUFFER FOR MTX SECTOR WHICH IS 256 BYTES LONG
;IT TAKES TWO CP/M 1.4 128 BYTE SECTOR READS TO FILL THE
;MTX LOGICAL SECTOR BUFFER.
;
	DS	20		;DUMMY BUFFER PAD
MTXBUF:
	DS	256		;MTX READ DATA BUFFER
	DS	20		;BUFFER PAD
;
;
;
;
;---->  KEYIN: GETS A KEY CODE IN FROM CONSOLE
;
KEYIN:
	PUSH	B		;SAVE..
	PUSH	D		;..ALL..
	PUSH	H		;..REGS
	MVI	C,RDCON		;GET CONSOLE CHARACTER FUNCTION CODE
	CALL	BDOS		;GET CHARACTER
	MOV	A,E
	POP	H		;RESTORE..
	POP	D		;..ALL..
	POP	B		;..REGS
	RET
;
;
;---->	CTYPE: TYPES VIA CP/M SO TABS ARE EXPANDED
;
CTYPE:
	PUSH	B		;SAVE..
	PUSH	D		;..ALL..
	PUSH	H		;..REGS
	MOV	E,A		;CHAR TO E
	MVI	C,WRCON		;GET BDOS FNC
	CALL	BDOS		;PRIN THE CHR
	POP	H		;RESTORE..
	POP	D		;..ALL..
	POP	B		;..REGS
	RET			;FROM "CTYPE"
;
CRLF:
	MVI	A,CR
	CALL	CTYPE
	MVI	A,LF
	CALL	CTYPE
	RET
;
;
;HEX OUTPUT
;
HEXO:
	PUSH	PSW		;SAVE FOR RIGHT DIGIT
	RAR			;RIGHT..
	RAR			;..JUSTIFY..
	RAR			;..LEFT..
	RAR			;..DIGIT..
	CALL	NIBBL		;PRINT LEFT DIGIT
	POP	PSW		;RESTORE RIGHT
	CALL	NIBBL		;PRINT RIGHT DIGIT
	RET
;
;
NIBBL:
	ANI	0FH		;ISOLATE DIGIT
	CPI	10		;IS IS <10?
	JC	ISNUM		;YES, NOT ALPHA
	ADI	7		;ADD ALPHA BIAS
ISNUM:
	ADI	'0'		;MAKE PRINTABLE
	CALL	CTYPE		;..THEN TYPE IT
	RET
;
;
;---->	ILPRT: INLINE PRINT OF MSG
;
;THE CALL TO ILPRT IS FOLLOWED BY A MESSAGE,
;BINARY 0 AS THE END.  BINARY 1 MAY BE USED TO
;PAUSE (MESSAGE 'PRESS RETURN TO CONTINUE')
;
ILPRT:
	XTHL			;SAVE HL, GET HL=MSG
ILPLP:
	MOV	A,M		;GET CHAR
	ORA	A		;END OF MSG?
	JZ	ILPRET		;..YES, RETURN
	CPI	1		;PAUSE?
	JZ	ILPAUSE		;..YES
	CALL	CTYPE		;TYPE THE CHARACTER OF MESSAGE
ILPNEXT:
	INX	H		;TO NEXT CHAR
	JMP	ILPLP		;LOOP
;
;PAUSE WHILE TYPING HELP SO INFO DOESN'T
;	SCROLL OFF OF VIDEO SCREENS
;
ILPAUSE:
	CALL	ILPRT		;PRINT:

	DB	CR,LF,'PRESS RETURN TO CONTINUE OR ^C TO EXIT'
	DB	CR,LF,0
	CALL	KEYIN		;GET ANY CHAR
	CPI	'C'-40H		;REBOOT?
	JZ	EXIT		;YES.
	JMP	ILPNEXT		;LOOP
;
ILPRET:
	XTHL			;RESTORE HL
	RET			; & RETURN ADDR PAST MESSAGE
;
;---->	PRTMSG: PRINTS MSG POINTED TO BY (DE)
;
;A '$' IS THE ENDING DELIMITER FOR THE PRINT.
;NO REGISTERS SAVED.
;
PRTMSG:
	MVI	C,PRINT		;GET BDOS FNC
	JMP	BDOS		;PRINT MESSAGE, RETURN
;
;---->	ERXIT: EXIT PRINTING MSG FOLLOWING CALL
;
ERXIT:
	POP	D		;GET MESSAGE
	CALL	PRTMSG		;PRINT IT
;
EXIT:
	MVI	C,00H		;SET SELECTED UNIT BACK TO A:
	CALL	SELDSK
	LXI	D,080H		;RESET DEFAULT DMA ADDRESS FOR EXIT
	MVI	C,STDMA
	CALL	BDOS
	LHLD	STACK		;GET ORIGINAL STACK
	SPHL			;RESTORE IT
	JMP	WBOOT		;GO DO A WARM BOOT OF CP/M SO THIS 
				;THING WILL WORK IN A SUBMIT FILE
;
;MOVE 128 CHARACTERS
;
MOVE128:
	MVI	B,128		;SET MOVE COUNT
;
;MOVE FROM (HL) TO (DE) LENGTH IN (B)
;
MOVE:
	MOV	A,M		;GET A CHAR
	STAX	D		;STORE IT
	INX	H		;TO NEXT "FROM"
	INX	D		;TO NEXT "TO"
	DCR	B		;MORE?
	JNZ	MOVE		;..YES, LOOP
	RET			;..NO, RETURN
;
;
;COMPARE FROM (HL) TO (DE) FOR (B) BYTES
;RETURN WITH A REGISTER:
;	=00 IF NO COMPARE
;	=FF IF VALID COMPARE
;
COMPARE:
	LDAX	D		;GET A CHAR
	CMP	M		;CHECK AGAINST OTHER
	JNZ	COMFAL		;GO EXIT FOR FAIL
	INX	H		;INCREMENT COMPARE POINTERS
	INX	D
	DCR	B		;DECREMENT CHAR COUNT
	JNZ	COMPARE		;MORE?
	MVI	A,0FFH		;EXIT FOR GOOD COMPARE
	RET
COMFAL:
	XRA	A		;EXIT FOR NON COMPARE
	RET
;
;
;----> BINASC: CONVERTS A 16 BIT BINARY NUMBER TO ASCII
;		INPUT NUMBER ASSUMED TO BE IN HL AND
;		OUTPUT ASCII 5 CHARACTER BUFFER IS POINTED
;		TO BY DE. MAXIMUM NUMBER FOR CONVERSION IS
;		THE EQUIVALENT OF 16 BITS BINARY.
;
BINASC:
	PUSH	PSW		;SAVE REGISTERS
	PUSH	B
	PUSH	D
	LXI	B,004H		;FIX DE TO POINT TO END OF BUFFER
	XCHG
	DAD	B
	XCHG
	PUSH	D		;SAVE ASCII POINTER
	MVI	A,05H		;GET A DIGIT COUNT
	STA	DIGCNT
NDIG:
	LXI	B,-10		;RADIX FOR CONVERSION
	LXI	D,-1		;THIS BECOMES NO DIVIDED BY RADIX
DX:
	DAD	B		;SUBTRACT 10
	INX	D
	JC	DX
	LXI	B,10
	DAD	B		;ADD RADIX BACK IN ONCE
	XCHG
	MOV	A,E
	ADI	'0'		;CONVERT FROM BCD TO ASCII
	POP	D		;GET ASCII BUFFER POINTER
	STAX	D		;STORE ASCII CHARACTER
	DCX	D		;ADJUST ASCII POINTER
	LDA	DIGCNT		;CHECK IF CONVERSION DONE
	DCR	A
	JZ	DDIG		;EXIT IF DONE
	STA	DIGCNT
	PUSH	D		;SAVE ASCII POINTER
	JMP	NDIG		;GO DO NEXT DIGIT
DDIG:
	POP	D		;RESTORE REGISTERS
	POP	B
	POP	PSW
	RET
;
;
;BINASC ROUTINE STORAGE ALLOCATIONS
;
DIGCNT	DB	00		;PLACE TO STORE ASCII DIGIT COUNT
;
;
;
;
;	----------------
;
;PROGRAM DATA AREA SPACE ALLOCATIONS
;
OPTION	DB	0		;PROGRAM OPTION
;
;
;FOLLOWING 3 USED BY THE CP/M DISK BUFFERING ROUTINES
;
EOFLG	DB	0		;EOF FLAG (1=TRUE)
SECPTR	DW	DBUF
SECINBF	DB	0		;# OF SECTORS IN BUFFER
;
;
;MTX FILE HANDLER PARAMETER STORAGE LOCATIONS
;
	DS	20
MDENT	DS	16		;TEMPORARY STORAGE FOR A DIRECTORY ENTRY
	DS	20
;
;
;SETUP A STACK AREA
;
	DS	400		;STACK AREA
STACK	DS	2		;STACK POINTER
;
;+++++++++
;+++++++++
;++
;++ NOTE: THE FOLLOWING DISK DATA BUFFERS ARE ALLOCATED
;++       OVER THE HELP FILE AND NON DISK I/O SOFTWARE
;++
;+++++++++
;+++++++++
;
;
;16 SECTOR CP/M DISK BUFFER 
;
	DS	20
DBUF	EQU	$+1		;16 SECTOR CP/M DISK BUFFER
;
;INVALID COMMAND
;
BADOPT:
	PUSH	PSW		;SAVE BAD OPTION
	MVI	A,'['
	CALL	CTYPE
	POP	PSW
	CALL	CTYPE
	CALL	ILPRT		;EXIT W/ERROR
	DB	'] INVALID OPTION FOR PROGRAM SELECTION '
	DB	'COMMAND - ',CR,LF
	DB	'PRESS CTL-C TO ABORT',1,0
;
HELP:
	CALL	ILPRT
	DB	CR,LF,CR,LF,'FORMAT FOR COMMAND IS:',CR,LF,CR,LF
	DB	'MTX # FILENAME',CR,LF,CR,LF
	DB	'WHERE # IS A 1 CHARACTER PROGRAM OPTION,',CR,LF
	DB	'PROGRAM OPTIONS:',CR,LF
	DB	'	R TO READ AN MTX FILE FROM B:',CR,LF
	DB	'	W TO WRITE AN MTX FILE TO B:',CR,LF
	DB	'	D TO VIEW THE MTX DIRECTORY ON B:',CR,LF
	DB	'	H TO PRINT THIS HELP FILE',CR,LF,CR,LF,0
	JMP	EXIT
;
;
; BDOS EQUATES (VERSION 2)
;
RDCON	EQU	1
WRCON	EQU	2
PRINT	EQU	9
CONST	EQU	11		;CONSOLE STAT
OPEN	EQU	15		;0FFH=NOT FOUND
CLOSE	EQU	16		;	"	"
SRCHF	EQU	17		;	"	"
SRCHN	EQU	18		;	"	"
ERASE	EQU	19		;NO RET CODE
READ	EQU	20		;0=OK, 1=EOF
WRITE	EQU	21		;0=OK, 1ERR, 2=?, 0FFH=NO DIR SPC
MAKE	EQU	22		;0FFH=BAD
REN	EQU	23		;0FFH=BAD
STDMA	EQU	26		;SET DMA
BDOS	EQU	5
REIPL	EQU	0
FCB	EQU	5CH		;SYSTEM FCB
FCBEXT	EQU	FCB+12		;FILE EXTENT
FCBSNO	EQU	FCB+32		;SECTOR #
FCB2	EQU	6CH		;SECOND FCB
	END
