;=================================================================== ; MICRO RESOURCES CP/M -- MTX FILE TRANSFER PROGRAM ; FOR USE WITH ICOM 3812 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 LIFEBOAT IMPLEMENTATION OF CP/M 1.4 ON AN ICOM 3812 ; DOUBLE DENSITY DISK SYSTEM. ; ; 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,' ICOM 3812 CP/M <----> PCC 2000 MTX' DB CR,LF,' VER 1.1 11/7/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 1 DB 5 DB 9 DB 13 DB 17 DB 21 DB 25 DB 29 DB 33 DB 37 DB 41 DB 45 DB 49 DB 3 DB 7 DB 11 DB 15 DB 19 DB 23 DB 27 DB 31 DB 35 DB 39 DB 43 DB 47 DB 51 ; ; ;----> 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