; ; UKM7 ;This program was originally written in 1977 by Ward Christensen. ;Ward's comments were removed, Terminal File and Batch Mode were ;added in 1980 by Mark Zeiger and James Mills. The original CRC ;checks were added by Paul Hansknecht in June 1981. ;Improved terminal file facilities, menus and a general tidy ;up of a very untidy program, removal of all modem dependent ;features, many bugs and adaptation for the UK CP/M Users ;Group Library by David Back. 4 May 1983. ;Version 1.5 includes many additional facilities, including a ;printer buffer, active toggling of terminal and secondary ;options, faster CRC's etc. David Back. 21 February 1984 ; ;The file exchange protocols are compatible with the MODEMX ;series of programs in the US CP/M UG Library. ;UKM7 is compatible with CP/M 1.4 and CP/M 2.2. ; FALSE EQU 0 TRUE EQU 0FFH ORG 100H ;********* SYSTEM DEPENDENT OVERLAY ************** ;**** This is a general purpose overlay applicable to many systems **** JMP 300H ;absolute start of program MODCTLP EQU 0ECH ;MODEM CONTROL PORT MODDATP EQU 0EDH ;MODEM DATA PORT FOR SEND MODDRCV EQU 0EDH ;MODEM DATA PORT FOR RECEIVE MSNDB EQU 1 ;MODEM SEND BIT (XMIT BUFF EMPTY) MSNDR EQU 1 ;MODEM SEND READY MRCVB EQU 2 ;MODEM RECEIVE BIT (DAV) MRCVR EQU 2 ;MODEM RECEIVE READY BAUD EQU 2 ;enter 1 for 1X, or 2 for 16X, or 3 for 64X ;It is important not to alter the addresses of labels below OLID: DB 1 ;overlay identifier TWIDTH: DB 70 ;max. terminal columns FASTCLK: DB TRUE ;4 MHz or greater processor speed BAKUPBYTE: DB TRUE ;true=make .BAK file XPRFLG: DB TRUE ;true=menu initially off SAVCCP: DB TRUE ;true=do not overwrite CCP SAVEFLG: DB TRUE ;true=terminal filesave initially on ECHOFLG: DB FALSE ;true=terminal echo initially on INITFLG: DB FALSE ;true=modem port already initialised ANSBAK: DB TRUE ;true=answerback on ^E INMODCTLP: IN MODCTLP ;get port status RET DB 0 ;space for memory mapped I/O's OUTCTLP: OUT MODCTLP ;control RET DB 0 OUTMODDATP: OUT MODDATP ;send data RET DB 0 ANISND: ANI MSNDB ;bit to test for send ready RET CPISND: CPI MSNDR ;value of send bit when ready RET INMODDATP: IN MODDRCV ;get data RET DB 0 ANIRCV: ANI MRCVB ;bit to test for receive ready RET CPIRCV: CPI MRCVR ;value of receive bit when ready RET LOGMSSG: DB 'David;Back;Shepperton Middx',CR,LF,0 ORG 150H ;do not alter this org ;The routine below should work in most systems which use an 8251 USART ;Modem port must be initialised for 8 data bits and no parity INITMOD:LXI D,CPMS ;finish signon message MVI C,9 CALL BDOS LDA INMODCTLP+1 CALL HEXPRT LXI D,DPMS MVI C,9 CALL BDOS LDA OUTMODDATP+1 CALL HEXPRT LXI D,HMS MVI C,9 CALL BDOS LDA INITFLG ORA A ;return if already initialsed RNZ MVI A,0EH CALL OUTCTLP ;force command instruction PUSH PSW POP PSW MVI A,40H CALL OUTCTLP ;internal reset PUSH PSW POP PSW MVI A,6CH OR (BAUD AND 3) CALL OUTCTLP ;8 bits no parity PUSH PSW POP PSW MVI A,37H ;modem connected CALL OUTCTLP CALL INMODDATP CALL INMODDATP ;clear buffers RET ;necessary because location of HEXO is not fixed HEXPRT: PUSH PSW RAR RAR RAR RAR CALL NIB POP PSW NIB: ANI 0FH CPI 10 JC NU ADI 7 NU: ADI '0' MOV E,A MVI C,2 CALL BDOS RET CPMS: DB 'Control port=0$' DPMS: DB 'H Data port=0$' HMS: DB 'H',CR,LF,'$' ;********** END OF OVERLAY AREA ************ DBUFSIZ EQU 2 ;S & R BUFFER SIZE IN KBYTES ;2K is the normally accepted optimum RING EQU 8 ;print ring buffer size, K bytes ERRLIM EQU 10 ;NUMBER OF TIMES TO RETRY ;SEND/RECEIVE ERRORS BEFORE QUIT COMPUT EQU 'C'-40H ; ^C = Computer mode DMENU EQU 'D'-40H ; ^D = display terminal menu EXITCHR EQU 'E'-40H ; ^E = exit terminal mode TRANCHR EQU 'T'-40H ; ^T = TRANSFER FILE SAVECHR EQU 'Y'-40H ; ^Y = memory save toggle PRNCHR EQU 'P'-40H ; ^P = printer toggle EXTCHR EQU '^'-40H ;SEND NEXT CHAR LITERALLY SAVON EQU 12H ;^R Terminal filesave on SAVOFF EQU 14H ;^T Terminal filesave off ; ;PROGRAM FOLLOWING IS NOT SYSTEM DEPENDENT, ;PLEASE DO NOT INTRODUCE ANY SYSTEM DEPENDENT FEATURES BELOW ; XOFF EQU 'S'-40H ; ^S = XOFF CHARACTER XON EQU 'Q'-40H ; ^Q = XON CHARACTER CAN EQU 'X'-40H ; ^X = CANCEL SEND/RECEIVE EOFCHAR EQU 'Z'-40H ; ^Z = END OF FILE ENQ EQU 5 ; ^E Auto ID SOH EQU 1 ; START OF HEADER EOT EQU 4 ; END OF TEXT ACK EQU 6 ; ACKNOWLEDGE NAK EQU 15H ; NOT ACKNOWLEDGE CRC EQU 'C' ;USED TO RQST CRC INSTEAD OF CKSUM BDNMCH EQU 75H ; BAD NAME MATCH LF EQU 10 ; LINEFEED CR EQU 13 ; CARRIAGE RETURN BELL EQU 7 ; BELL CHARACTER WRCON EQU 2 LIST EQU 5 ;printer o/p PRINT EQU 9 ;string o/p to console OPEN EQU 15 CLOSE EQU 16 SRCHF EQU 17 SRCHN EQU 18 ERASE EQU 19 READ EQU 20 WRITE EQU 21 MAKE EQU 22 REN EQU 23 STDMA EQU 26 FILSIZ EQU 35 BDOS EQU 5 FCB EQU 5CH FCBEXT EQU FCB+12 FCBRNO EQU FCB+32 ORG 300H ;do not alter this org LXI H,0 DAD SP ;GET CP/M'S STACK SHLD STAK ;SAVE IT LXI SP,STAK ;LOCAL STACK CALL INITADR ;INITIALIZE BIOS ADDRESSES CALL INITCRC ;initialise crc table CALL ILPRT DB 'UK MODEM7 D.R. Back Version 1.5',CR,LF,0 CALL INITMOD ;INITIALISE MODEM PORTS LXI H,80H LXI D,CMDBUF+1 MVI B,80H CALL MOVE ;default buffer to cmdbuf MVI A,TRUE STA NFILFLG CALL PROCOPT ;PROCESS CONTROL OPTIONS RESTART:LDA OPTION ;GET MAIN OPTION CPI ' ' ;NO OPTION SPEC'D? JZ MENU CPI 'M' ;MENU JZ MENU2 CALL MOVEFCB ;MOVE 2ND HALF FCB TO FIRST HALF CALL INMODDATP ;GOBBLE UP GARBAGE.. CALL INMODDATP ;..CHARACTERS ON LINE LDA OPTION ;PROCESS MAIN OPTION CPI 'T' ;TERMINAL MODE? JZ DSKSAVE CPI 'S' ;SEND A FILE? JZ SENDFIL CPI 'R' ;RECEIVE A FILE? JZ RCVFIL MENU: LXI SP,STAK ;RESTORE STACK LXI H,RESTSN ;RESTORE SECTOR NUMBERS.. LXI D,SECNOB ;..FOR NEW FILE TRANSFER. MVI B,SECNOE-SECNOB CALL MOVE LXI H,RESTROPT ;RESTORE OPTION TABLE LXI D,OPTBL MVI B,OPTBE-OPTBL CALL MOVE LDA LSTFLG STA LSTRET ;save print toggle MVI A,0 STA LSTFLG ;printer off STA MFFLG1 ;RESET MFACCESS ROUTINE.. CMA ;..AND MULTI TRANS IN CASE.. STA FSTFLG ;..OF ABORT. MENU1: LDA XPRFLG ;TEST IF MENU SHOULD BE SHOWN ORA A JNZ XPRT MENU2: CALL ILPRT DB CR,LF DB 'SYNTAX: primaryoption[secondaryoptions] [d:][filename] [ afn]' DB CR,LF,CR,LF DB ' PRIMARY OPTIONS:',CR,LF DB ' S Send binary files, afn list',CR,LF DB ' R Receive binary files, drive:',CR,LF DB ' T Terminal mode. Terminal filename optional',CR,LF DB ' DEL Delete Terminal file',CR,LF DB ' DIR Directory list, afn optional',CR,LF DB ' CPM Exit to CP/M.',CR,LF DB ' X Expert, toggle menus on/off',CR,LF DB ' M Menu display',CR,LF,CR,LF DB ' SECONDARY OPTIONS: (for primary options S and R)' DB CR,LF DB ' N Non batch mode, send or receive file' DB CR,LF DB ' Q Quiet mode, remote system Send/Receive',CR,LF DB 'S,R,V Monitor data Sent, Received or View file',CR,LF DB ' T Go to Terminal mode after file transfers',CR,LF,0 XPRT: CALL ILPRT DB CR,LF,0 MVI C,25 ;CURRENT DISK FUNCTION CALL BDOS ADI 41H ;MAKE ASCII CALL CTYPE CALL ILPRT DB ' ==>>',0 LXI D,CMDBUF ;ENTER COMMAND CALL INBUFF CALL CRLF LXI D,CMDBUF+2 ;POINT TO COMMAND CALL ILCOMP DB 'CPM',0 JNC EXIT CALL ILCOMP DB 'DIR',0 JNC DIR CALL ILCOMP DB 'DEL',0 JNC NEWFILE LXI D,CMDBUF LXI H,FCB CALL CPMLINE ;LOAD FCB CALL PROCOPT JMP RESTART DIR: CALL DIRLST JMP XPRT EXIT: LDA NFILFLG CPI TRUE CNZ TFILWR ;write and close terminal file LXI D,80H MVI C,STDMA CALL BDOS LHLD STAK SPHL LDA SAVCCP ORA A JZ 0 ;WARM BOOT RET ;to CCP NEWFILE:LDA NFILFLG CPI TRUE JZ MENU1 ;IF NO FILE, DON'T ERASE LXI D,FCB3 MVI C,ERASE CALL BDOSRT MVI A,TRUE ;DO NOT ALLOW TERMINAL.. STA NFILFLG ;..SAVE SINCE NO FILE.. JMP MENU1 ;====================================== ;TERMINAL ROUTINE ALLOWING MEMORY SAVE DSKSAVE:LDA FCB+1 ;FIRST CHAR OF FILENAME CPI ' ' ;FILE SPEC'D LHLD HLSAVE JZ TERM1 LDA NFILFLG CPI TRUE CNZ TFILWR ;write & close existing file CALL TFLERAS CALL MOVE2 ;move FCB to FCB3 LXI D,FCB3 MVI C,MAKE CALL BDOS INR A JNZ TERM0 CALL ILPRT DB 'Can''t make file',CR,LF,0 JMP TERM1 TERM0: LXI H,BOTTRAM SHLD HLSAVE MVI A,FALSE STA NFILFLG STA CTRLR ;cancel any previous ^R TERM1: LDA LSTRET STA LSTFLG ;restore printer toggle LDA XPRFLG ORA A JNZ TERM3 TERM2: LDA NFILFLG CPI TRUE JZ NOTFIL PUSH H LXI H,FCB3 LXI D,TFILE MOV A,M ORA A JNZ PUTDRV MVI A,' ' STAX D INX D JMP NAME1 PUTDRV: ADI 40H STAX D INX D MVI A,':' NAME1: STAX D INX D INX H MVI B,8 CALL MOVE INX D MVI B,3 CALL MOVE POP H CALL ILPRT DB CR,LF,' ^Y Terminal file ' TFILE: DB ' toggle save on/off '':'' =on',0 NOTFIL: CALL ILPRT DB CR,LF DB ' ^P Printer, toggle on/off',CR,LF DB ' ^T Transfer (Send) ASCII file without checks',CR,LF DB ' ^X Abort transfer initiated above',CR,LF DB ' ^C Computer mode, toggle echo on/off',CR,LF DB ' ^^ Send following character literally',CR,LF DB ' ^E Exit to command menu',CR,LF DB ' ^D Display terminal menu',CR,LF,0 TERM3: LDA NFILFLG ORA A JNZ TERM LDA SAVEFLG ORA A JZ TERM MVI A,':' ;indicate filesave is on CALL TYPE ;dont print or inc col count TERM: CALL STAT ;o/p to print & check keypress JZ TERML ;NO, CHECK LINE CALL KEYIN ;GET CHAR FROM KBD MOV B,A LDA EXACFL ORA A MVI A,FALSE STA EXACFL MOV A,B JNZ NOTOG CPI EXITCHR ;^E? JZ MENU ;YES, RETURN TO MENU CPI COMPUT ;^C Computer mode with echo JNZ NOECH LDA ECHOFLG CMA STA ECHOFLG JMP TERML NOECH: CPI DMENU ;^D display terminal menu JZ TERM2 CPI EXTCHR ;literal JZ EXTFLG CPI TRANCHR ;TEST FOR TRANSFER REQUEST (^T) CZ TRANSFER ;SEND-A-FILE JZ TERM3 ;LOOP CPI PRNCHR JNZ NOTLST LDA LSTFLG CMA STA LSTFLG JMP TERML NOTLST: CPI SAVECHR JNZ NOTOG LDA NFILFLG ;DO NOT ALLOW SAVE IF.. CPI TRUE ;..THIS FLAG IS SET. JZ TERML LDA SAVEFLG CMA STA SAVEFLG JMP TERM3 EXTFLG: MVI A,TRUE STA EXACFL JMP TERML NOTOG: MOV B,A LDA ECHOFLG ORA A MOV A,B JZ TSEND CPI LF JZ TERML ;ignore LF CALL OUTMODDATP CALL CTYPE ;local echo PUSH PSW CALL MSAVE ;local save in terminal file POP PSW CPI CR JNZ TERML MVI A,LF CALL CHRSND ;send to remote CALL CTYPE ;append LF CALL MSAVE JMP TERML TSEND: CALL OUTMODDATP TERML: CALL INMODCTLP CALL ANIRCV CALL CPIRCV JNZ TERM CALL INMODDATP ANI 7FH ;strip parity JZ TERM ;ignore null CPI ENQ ;auto logon JNZ NOLOG LDA ANSBAK ORA A JZ TERM LXI D,LOGMSSG NXCHAR: LDAX D INX D ORA A JZ TERM CALL CHRSND ;send to remote MOV B,A LDA ECHOFLG ORA A MOV A,B JNZ CHRSAV PUSH D MVI D,0 ;init count CALL INMODEM ;wait 100ms for echo DCR D POP D JNZ NXCHAR CHRSAV: CALL CTYPE CALL MSAVE JMP NXCHAR NOLOG: CPI SAVON ;auto filesave on JNZ TRYT LDA SAVEFLG ORA A JNZ TERM ;save already on MVI A,TRUE STA SAVEFLG ;turn on save STA CTRLR ;remember JMP TERM3 TRYT: CPI SAVOFF ;auto save off JNZ ONWRD LDA CTRLR ORA A JZ TERM ;turn save off only MVI A,FALSE ;if it was turned on STA SAVEFLG ;by ^R from remote STA CTRLR JMP TERM ONWRD: MOV B,A LDA ECHOFLG ORA A MOV A,B JZ TERM5 CPI LF JZ TERM ;ignore LF CALL OUTMODDATP ;echo to distant terminal CPI CR JNZ TERM5 CALL CTYPE CALL MSAVE ;save in terminal file MVI A,LF CALL CHRSND ;send LF TERM5: CALL CTYPE CALL MSAVE ;save in terminal file JMP TERM CTRLR: DB FALSE LASTB1: DB 0 LASTB2: DB 0 ;========================================= ;SEND A CP/M FILE SENDFIL:LDA BATCHFLG ;CHECK IF MULTIPLE FILE.. ORA A ;..MODE IS SET. JZ SENDC1 MVI A,TRUE ;INDICATE BATCH SEND STA SENDFLG LDA FSTFLG ;IF FIRST TIME THRU.. ORA A ;..SCAN THE COMMAND LINE.. CNZ TNMBUF ;..FOR MULTIPLE NAMES. CALL SENDFN ;SENDS FILE NAME TO RECEIVER JNC SENDC2 ;CARRY SET MEANS NO MORE FILES. MVI A,0 ;STOP BATCH.. STA BATCHFLG ;..MODE OPTION. MVI A,EOT ;FINAL XFER END CALL SEND JMP DONE SENDC1: LDA FCB+1 CPI ' ' JZ BLKFILE CALL AMBGTS ;test for ambiguous filename SENDC2: CALL CNREC ;GET NUMBER OF RECORDS CALL OPENFIL MVI E,80 CALL WAITNAK ;if a 'C' is received instead of NAK SENDLP: CALL RDSECT ;then CRC mode is enabled JC SENDEOF CALL INCRSNO XRA A STA ERRCT SENDRPT:CALL SENDHDR CALL SENDSEC LDA CRCFLG ORA A CZ SENDCRC CNZ SENDCKS CALL GETACK JC SENDRPT JMP SENDLP SENDEOF:MVI A,EOT CALL SEND CALL GETACK JC SENDEOF JMP DONE ;=============================== ;RECEIVE A FILE RCVFIL: XRA A ;default to CRC mode STA CRCFLG RCV1FIL:LDA BATCHFLG ;CHECK IF MULT.. ORA A ;..FILE MODE. JZ RCVC1 MVI A,FALSE ;FLAG WHERE TO RETURN.. STA SENDFLG ;..FOR NEXT FILE TRANS. CALL GETFN ;GET THE FILE NAME. JNC RCVC2 ;CARRY SET MEANS NO MORE FILES. MVI A,0 ;STOP BATCH.. STA BATCHFLG ;..MODE OPTION. JMP DONE RCVC1: LDA FCB+1 ;MAKE SURE FILE IS NAMED CPI ' ' JZ BLKFILE JMP RCVC3 RCVC2: CALL CKCPM2 CALL CKBAKUP RCVC3: CALL ERASFIL CALL MAKEFIL LDA QFLG ORA A JZ RCVFST RCVC4: CALL ILPRT ;first comment DB 'File open, ready to receive',CR,LF,0 RCVFST: LDA CRCFLG ORA A MVI A,NAK JNZ RCV2FIL MVI A,CRC ;indicate to Tx that CRC is wanted RCV2FIL:CALL SEND ;by sending a 'C' instead of NAK LDA QFLG ORA A JZ RCVLP LDA CRCFLG ORA A JNZ RCVNAKM ;if in CRC mode CALL ILPRT ;then say so DB 'CRC in effect',cr,lf,0 JMP RCVLP RCVNAKM:CALL ILPRT ;else say checksum mode DB 'Checksum in effect',cr,lf,0 RCVLP: CALL RCVSECT JC RCVEOT CALL WRSECT ;sends CAN if error CALL INCRSNO CALL SENDACK JMP RCVLP RCVEOT: CALL WRBLOCK ;sends CAN if error CALL SENDACK CALL CLOSFIL JMP DONE ;=================================== BLKFILE:CALL ILPRT ;fatal error DB CR,LF,'No file specified',CR,LF,BELL,0 JMP MENU ;=============================== DONE: LDA BATCHFLG ORA A JZ DONETB LDA QFLG ORA A JZ NMSTRNS LXI H,FCB+1 ;PUT FILE NAME IN.. LXI D,FTRNMSG ;..SPACES IN MESSAGE.. MVI B,8 ;..BELOW. CALL MOVE INX D ;PUT FILE TYPE AFTER.. MVI B,3 ;..SKIPPING ONE SPACE.. CALL MOVE ;..BELOW. CALL ILPRT ;final comment DB CR,LF FTRNMSG:DB ' transferred',CR,LF,CR,LF,0 ;13 SPACES NMSTRNS:LDA FCB ;SAVE DRIVE NO. STA DISKNO LXI H,FCB ;BLANK OUT FILE CONTROL BLOCKS CALL INITFCBS LDA DISKNO ;PUT DRIVE NUMBER BACK STA FCB LXI H,RESTSN ;RESTORE SECTOR NUMBERS.. LXI D,SECNOB ;..FOR NEW FILE TRANSFER. MVI B,SECNOE-SECNOB ;ROUTINE ALSO DONE IN MENU. CALL MOVE LDA SENDFLG ;GOES TO EITHER SEND OR.. ORA A ;..RECEIVE FILE, DEPENDING.. JNZ SENDFIL ;..UPON WHICH ROUTINE SET.. JMP RCV1FIL ;..THE FLAG IN MULTI-FILE MODE. DONETB: MVI A,TRUE ;INDICATE NO FILES BEING.. STA FSTFLG ;RESET MULTIFILE TRANS LDA QFLG ORA A JZ DONETA CALL ILPRT ;final comment DB CR,LF,'All transfers completed' DB CR,LF,BELL,0 DONETA: LXI SP,STAK ;restore stack MVI A,CRC STA CRCFLG ;turn off CRC option MVI A,0FFH STA FIRSTME ;set first-time flag LDA TERMFLG ;SEE IF RETURN TO.. ORA A ;..TERMINAL MODE.. JNZ MENU ;..AFTER X'FER. CALL CRLF MVI A,'T' STA OPTION MVI A,' ' STA FCB+1 ;too late to specify filename CALL INMODDATP CALL INMODDATP;clear usart JMP DSKSAVE ;============================= SUBROUTINES =============== MSAVE: PUSH PSW LDA NFILFLG CPI TRUE JZ NOSAVE ;CANT SAVE IF NO FILE LDA SAVEFLG CPI FALSE JZ NOSAVE POP PSW CPI EOFCHAR ;dont save EOF's in file RZ ;CP/M doesn't like them LHLD HLSAVE MOV M,A INX H SHLD HLSAVE ;MENU COMMAND DESTROYS HL-REG.. CPI LF JNZ NOCOLON ;TYPE ":" AFTER EACH LINE FEED.. MVI A,':' ;..WHEN MEMORY SAVE ACTIVE. CALL TYPE ;dont increment col count NOCOLON:LDA SAVCCP ORA A JZ SUB1 LDA 7 SBI 8 ;..PAGE BELOW CCP .. JMP SUB1A SUB1: LDA 7 SUB1A: DCR A ;..OR BDOS HAS BEEN.. CMP H ;..REACHED AND DISKSAVE IS NEEDED. CZ INTDSKSV RET NOSAVE: POP PSW RET ;================================== PROCOPT:LXI D,FCB+1 LDAX D STA OPTION ;primary option OPTLP: INX D LDAX D CPI ' ' JZ CKPRI LXI H,OPTBL MVI B,OPTBE-OPTBL OPTCK: CMP M JNZ OPTNO MVI M,0 ;INSERT SECONDARY OPTION JMP OPTLP OPTNO: INX H DCR B JNZ OPTCK JMP BDOPT CKPRI: LDA FCB+1 ;CHECK ON THE PRIMARY OPTION CPI 'X' JZ EXPRT CPI ' ' RZ CPI 'M' RZ CPI 'T' RZ CPI 'S' JZ CKFILE CPI 'R' JNZ BDOPT LDA BATCHFLG ;IF MULT FILE MODE, THEN.. ORA A ;..RECV OPT MUST NOT BE NAMED JZ CKFILE LDA FCB+17 CPI ' ' RZ BDOPT: CALL ILPRT ;fatal error DB '++Bad Syntax++',CR,LF,0 JMP MENU EXPRT: LDA XPRFLG CMA STA XPRFLG JMP MENU CKFILE: LDA FCB+17 ;IF OPTION THAT NEEDS FILE NAME,.. CPI ' ' ;..THEN CHECK TO SEE IF NAME.. RNZ ;..EXISTS. JMP BDOPT ;================================== TFILWR: LHLD HLSAVE CALL NUMRECS ;DISK WRITE ROUTINE AS USED IN.. CALL WRTDSK ;..IN THE INTDSKSV ROUTINE. LXI D,FCB3 MVI C,CLOSE CALL BDOS MVI A,TRUE STA NFILFLG RET ;================================ INTDSKSV: MVI A,XOFF ;SEND A CTRL-S TO STOP.. CALL OUTMODDATP ;..REMOTE COMPUTER OUTPUT. MVI D,0 ;D IS THE BUFFER COUNT CALL INMODEM ;GET LAST BYTES SENT.. STA LASTB1 ;..AFTER CTRL-S. CALL INMODEM ;ADD MORE CALLS TO INMODEM.. STA LASTB2 ;..AND STA LASTBYT# IF YOU ARE.. ;..LOSING BYTES WHEN MEMORY IS FULL. PUSH D CALL NUM1REC CALL WRTDSK ;WRITE THE RECORDS POP D LXI H,BOTTRAM INR D DCR D ;TEST BUFFER COUNT FOR ZERO JZ CTRLQ LDA LASTB1 ;GET THE LAST BYTES THAT WERE.. MOV M,A ;..SAVED AND PUT THEM IN.. INX H ;..BOTTRAM. CALL CTYPE DCR D JZ CTRLQ LDA LASTB2 MOV M,A INX H CALL CTYPE CTRLQ: SHLD HLSAVE MVI A,XON ;SEND START CHARACTER.. CALL OUTMODDATP ;..TO REMOTE COMPUTER. RET ;====================================== ;SUBROUTINE LOOPS UNTIL THE MODEM RECEIVES A CHARACTER OR 100ms ;RETURNS BYTE COUNT IN D OR ZERO FOR TIMEOUT ;if ^S is received it waits until ^Q is received INMODEM:LDA FASTCLK ORA A LXI B,1250 JZ CHKMOD LXI B,2500 CHKMOD: CALL INMODCTLP CALL ANIRCV CALL CPIRCV JZ GETBYTE DCX B MOV A,B ORA C JNZ CHKMOD RET GETBYTE:CALL INMODDATP ANI 7FH ;clear parity CPI XOFF JNZ TWAIT4 PUSH H CALL ILPRT DB CR,LF,'XOFF received, type ^Q to force continuation',CR,LF,0 TWAIT1: CALL INMODCTLP CALL ANIRCV CALL CPIRCV JZ TWAIT2 CALL STAT ;local key? JZ TWAIT1 CALL KEYIN ;get char CPI XON JZ TWAIT3 JMP TWAIT1 TWAIT2: CALL INMODDATP ANI 7FH CPI XON ;remote XON? JNZ TWAIT1 CALL ILPRT DB 'XON received',CR,LF,0 TWAIT3: POP H JMP INMODEM TWAIT4: INR D RET ;================================ NUMRECS:MVI M,EOFCHAR INX H LXI D,127 DAD D NUM1REC:LXI D,-(BOTTRAM) DAD D MOV A,L ;DIVIDE HL BY 128.. ORA A RAL ;..TO GET THE.. MOV L,H ;..NUMBER OF SECTORS MVI H,0 PUSH PSW DAD H POP PSW MVI A,0 ADC L MOV L,A ;RETNS WITH NUMBER OF.. RET ;..128 BYTE RECORDS IN HL. ;====================================== WRTDSK: LXI D,BOTTRAM NEXTWRT:MVI C,STDMA CALL BDOSRT PUSH D LXI D,FCB3 MVI C,WRITE CALL BDOSRT POP D INR A JZ WRTERR XCHG PUSH D LXI D,128 DAD D POP D XCHG DCX H MOV A,H ORA L JNZ NEXTWRT CALL RSDMA ;for CP/M 1.4 RET WRTERR: CALL ILPRT DB 'Terminal file write error',CR,LF,0 RET ;========================== BDOSRT: PUSH B PUSH D PUSH H CALL BDOS POP H POP D POP B RET ;============================ MOVE2: LXI H,FCB3 CALL INITFCBS LXI H,FCB LXI D,FCB3 MVI B,12 CALL MOVE RET ;============================= ;FILE TRANSFER ROUTINE - CALLED WITH ;CONTROL-T FROM TERMINAL ROUTINE. ;TRANSFER MAY BE CANCELLED WHILE SENDING BY USING CONTROL-X. TRANSFER: SHLD HLSAVE PUSH D PUSH B PUSH PSW LXI H,FCB4 CALL INITFCBS ;INITIALIZES FCBS POINTED.. LXI H,FCB+16 ;..TO BY HL REG. CALL INITFCBS GET: CALL ILPRT DB CR,LF,'Enter file name to be transferred - C/R TO QUIT: ',0 LXI D,CMDBUF CALL INBUFF CALL CRLF LDA CMDBUF+2 ;WAS FILE ENTERED CPI 20H JZ TRANCAN LXI D,CMDBUF LXI H,FCB4 CALL CPMLINE LXI D,FCB4 MVI C,OPEN CALL BDOS CPI 0FFH ;RETURN WITH 0FFH MEANS JNZ CONTIN ;FILE DOES NOT EXIST CALL ILPRT DB '++File does not exist++',CR,LF,0 TRANS2L:CALL ILPRT DB 'Type "^X" to cancel transfer',CR,LF DB 'Type "A" to re-enter name: ',BELL,0 CALL KEYIN CALL UCASE CALL CTYPE ;ECHO RESPONSE CALL CRLF CPI 'A' JZ GET CPI CAN JZ TRANCAN JMP TRANS2L CONTIN: LXI D,80H MVI C,STDMA CALL BDOS LDA ECHOFLG ;computer mode ? ORA A JZ READMR MVI A,SAVON ;activate remote save CALL CHRSND ;computer mode only READMR: LXI D,FCB4 MVI C,READ CALL BDOS ORA A JNZ RETNS CALL SEND80C CPI EOFCHAR ;END OF FILE JZ RETNS CPI CAN ;CANCELLATION? JZ TRANC1 JMP READMR RETNS: LDA ECHOFLG ORA A JZ RETN1 MVI A,SAVOFF CALL CHRSND ;deactivate remote save RETN1: CALL ILPRT DB CR,LF,'++File transfer completed++',CR,LF,BELL,0 JMP RETURN TRANC1: LDA ECHOFLG ORA A JZ TRANCAN MVI A,SAVOFF CALL CHRSND ;deactivate remote save TRANCAN:CALL ILPRT DB CR,LF,'++ Transfer cancelled ++',CR,LF,BELL,0 RETURN: POP PSW POP B POP D LHLD HLSAVE RET ;============================= INITFCBS: ;ENTRY AT +2 WILL LEAVE.. MVI M,0 ;..DRIVE NO. INTACT. INX H ;WILL INITIALIZE AN FCB.. MVI B,11 ;..POINTED TO BY HL-REG. FILLS 1ST POS LOOP10: MVI M,' ' ;..WITH 0, NEXT 11 WITH.. INX H ;..WITH BLANKS, AND LAST.. DCR B ;..21 WITH NULLS. JNZ LOOP10 MVI B,21 LOOP11: MVI M,0 INX H DCR B JNZ LOOP11 RET ;=========================== CHRSND: PUSH PSW REDY: CALL INMODCTLP CALL ANISND CALL CPISND JNZ REDY POP PSW CALL OUTMODDATP RET ;=============================== ;used by ^T transfer SEND80C:MVI B,80H LXI H,80H SEND81: MOV A,M ;get next char ANI 7FH ;remove top bit CPI EOFCHAR ;end of file? RZ CALL FILTER ;ignore non printing ASCII JC SEND85 MOV C,A LDA ECHOFLG ORA A MOV A,C JNZ SEND83 CPI LF ;ignore lf's in terminal mode JZ SEND85 MOV A,C CALL CHRSND ;send to remote CPI CR JZ SEND82 PUSH B MVI D,0 CALL INMODEM ;wait 100ms for echo POP B JZ SEND85 ;zero indicates no echo JMP SEND84 SEND83: CALL CHRSND PUSH PSW PUSH B LXI B,1 ;zero delay CALL CHKMOD ;check for XOFF from remote POP B POP PSW SEND84: CALL CTYPE ;local console PUSH B PUSH H CALL MSAVE ;save to memory POP H POP B JMP SEND85 SEND82: PUSH B MVI D,0 CALL INMODEM ;wait 100ms for echo POP B JZ SEND85 ;loop until timeout PUSH B CALL CTYPE PUSH PSW ;save inmodem flag PUSH H CALL MSAVE POP H POP PSW POP B JMP SEND82 SEND85: CALL STAT ;TEST TO SEE IF ORA A ;CANCELLATION REQUESTED JZ SKIP12 CALL KEYIN CPI CAN ;cancel? RZ CPI SAVECHR ;memory save toggle JNZ SEND86 LDA NFILFLG CPI TRUE ;cant save if no file JZ SEND86 LDA SAVEFLG CMA STA SAVEFLG SEND86: CPI PRNCHR JNZ SKIP12 LDA LSTFLG CMA STA LSTFLG ;print toggle SKIP12: INX H DCR B JNZ SEND81 RET ;=================================- ;send filename SENDFN: LDA QFLG ORA A JZ SWNAK CALL ILPRT ;first comment DB 'Awaiting name NAK',CR,LF,0 SWNAK: MVI E,80 ;80 second timeout CALL WAITNLP MVI A,ACK ;GOT NAK, SEND ACK CALL SEND LXI H,FILECT DCR M JM NOMRNM LHLD NBSAVE ;GET FILE NAME.. LXI D,FCB ;..IN FCB MVI B,12 CALL MOVE SHLD NBSAVE CALL SENDNM ;SEND IT ORA A ;CLEAR CARRY RET NOMRNM: MVI A,EOT CALL SEND STC RET ;====================================== SENDNM: PUSH H SEND1NM:MVI D,11 ;COUNT CHARS IN NAME MVI C,0 ;INIT CHECKSUM LXI H,FCB+1 ;ADDRESS NAME NAMLPS: MOV A,M ;SEND NAME ANI 7FH ;STRIP HIGH ORDER BIT SO CP/M 2.. CALL SEND ;..WON'T SEND R/O FILE DESIGNATION. LDA SSEEFLG ORA A JZ ACKLP ;already typed by SEND LDA QFLG ;SHOW NAME IF.. ORA A ;..QFLG NOT SET. MOV A,M CNZ TYPE ;first comment ACKLP: PUSH B ;SAVE CKSUM MVI B,1 ;WAIT FOR RECEIVER.. CALL RECV ;..TO ACKNOWLEDGE.. POP B ;..GETTING LETTER. JC SCKSER CPI ACK JNZ ACKLP INX H ;NEXT CHAR DCR D JNZ NAMLPS MVI A,EOFCHAR ;TELL RECEIVER END OF NAME CALL SEND LDA QFLG ORA A CNZ CRLF ;first comment MOV D,C ;SAVE CHECKSUM MVI B,1 CALL RECV ;GET CHECKSUM.. CMP D ;..FROM RECEIVER. JZ NAMEOK SCKSER: MVI A,BDNMCH ;BAD NAME-TELL RECEIVER CALL SEND LDA QFLG ORA A JZ SKCSER1 CALL ILPRT ;error message DB 'Checksum error',CR,LF,0 SKCSER1:MVI E,80 ;DO HANDSHAKING OVER CALL WAITNLP ;DON'T PRINT "AWAITING NAK" MSG MVI A,ACK CALL SEND JMP SEND1NM NAMEOK: MVI A,ACK ;GOOD NAME-TELL RECEIVER CALL SEND POP H RET ;============================ ;get filename GETFN: LXI H,FCB CALL INITFCBS+2 ;DOES NOT INITIALIZE DRIVE LDA QFLG ORA A JZ GNAMELP CALL ILPRT ;first comment DB 'Awaiting file name',CR,LF,0 GNAMELP:CALL HSNAK JC GNAMELP CALL GETNM ;GET THE NAME CPI EOT ;IF EOT, THEN NO MORE FILES JZ NOMRNG ORA A ;CLEAR CARRY RET NOMRNG: STC RET ;================================ GETNM: PUSH H GETNM1: MVI C,0 ;INIT CHECKSUM LXI H,FCB+1 NAMELPG:MVI B,5 CALL RECV ;GET CHAR JNC GETNM3 LDA QFLG ORA A JZ GETNM2 CALL ILPRT ;error message DB 'Time out receiving filename',CR,LF,0 GETNM2: JMP GCKSER GETNM3: CPI EOT ;IF EOT, THEN NO MORE FILES JZ GNRET CPI EOFCHAR ;GOT END OF NAME JZ ENDNAME MOV M,A ;PUT NAME IN FCB LDA RSEEFLG ORA A JZ GETNM4 ;already typed by RECV LDA QFLG ;TYPE IT IF NO QFLG ORA A MOV A,M CNZ CTYPE ;first comment GETNM4: PUSH B ;SAVE CKSUM MVI A,ACK ;ACK GETTING LETTER CALL SEND POP B INX H ;GET NEXT CHAR MOV A,L ;DON'T LET NOISE... CPI 7FH ;..CAUSE OVERFLOW.. JZ GCKSER ;..INTO PROGRAM AREA. JMP NAMELPG ENDNAME:LDA QFLG ORA A CNZ CRLF ;first comment MOV A,C ;SEND CHECKSUM CALL SEND MVI B,1 CALL RECV ;CHECKSUM GOOD? CPI ACK ;YES IF ACK SENT.. JZ GNRET ;..ELSE DO OVER. GCKSER: LXI H,FCB ;CLEAR FCB (EXCEPT DRIVE).. CALL INITFCBS+2 ;..SINCE IT MIGHT BE DAMAGED.. LDA QFLG ;..BY TOO MANY CHARS. ORA A JZ GCK1SER CALL ILPRT ;error message DB 'Checksum error',CR,LF,0 GCK1SER:CALL HSNAK ;DO HANDSHAKING OVER JC GCK1SER JMP GETNM1 GNRET: POP H RET ;============================== HSNAK: MVI A,NAK ;SEND NAK UNTIL.. CALL SEND ;..RECEIVING ACK. CALL CKABORT ;DON'T GET HUNG UP HERE MVI B,2 ;WAIT 2 SECONDS.. CALL RECV ;..IN RECEIVE. CPI ACK ;IF ACK,RETURN WITH.. RZ ;..CARRY CLEAR. STC RET ;============================ TNMBUF: MVI A,FALSE ;CALL FROM SENDFIL ONLY ONCE. STA FSTFLG STA FILECT CALL SCAN LXI H,NAMEBUF SHLD NBSAVE ;SAVE ADDR OF 1ST NAME TNLP1: CALL TRTOBUF LXI H,FCB LXI D,FCBBUF CALL CPMLINE ;PARSE NAME TO CP/M FORMAT TNLP2: CALL MFNAME ;SEARCH FOR NAMES (* FORMAT) JC NEXTNM LDA FCB+10 ;IF CP/M 2 $SYS FILE.. ANI 80H ;..DON'T SEND JNZ TNLP2 LHLD NBSAVE ;GET NAME LXI D,FCB ;MOVE IT TO FCB XCHG MVI B,12 CALL MOVE XCHG SHLD NBSAVE ;ADDR OF NEXT NAME LXI H,FILECT ;COUNT FILES FOUND INR M JMP TNLP2 ; NEXTNM: LXI H,NAMECT ;COUNT NAMES FOUND DCR M JNZ TNLP1 LXI H,NAMEBUF ;SAVE START OF BUFFER SHLD NBSAVE LDA FILECT ORA A JZ NOBFILE CPI 65 ;NO MORE THAN 64 TRANSFERS RC MVI A,64 ;ONLY X'FER FIRST 64 STA FILECT RET NOBFILE:CALL ILPRT ;fatal error DB 'No file',CR,LF,0 JMP MENU ;==============================================- ;SCANS CMDBUF COUNTING NAMES AND PUTTING DELIMITER (SPACE) ;AFTER LAST NAME SCAN: PUSH H LXI H,NAMECT MVI M,0 LXI H,CMDBUF+1 ;FIND END OF CMD LINE.. MOV C,M ;..AND PUT SPACE THERE. MVI B,0 LXI H,CMDBUF+2 DAD B MVI M,20H LXI H,CMDBUF+1 MOV B,M INR B INR B CALL EAT ;eat spaces JZ DNSCAN SCAN1LP:INX H DCR B JZ DNSCAN MOV A,M CPI 20H JNZ SCAN1LP CALL EAT JZ DNSCAN SHLD BGNMS ;SAVE START OF NAMES IN CMDBUF INR B DCX H SCAN3LP:INX H DCR B JZ DNSCAN MOV A,M CPI 20H JNZ SCAN3LP LDA NAMECT ;COUNTS NAMES INR A STA NAMECT CALL EAT JZ DNSCAN JMP SCAN3LP ; DNSCAN: MVI M,20H ;SPACE AFTER LAST CHAR POP H RET ;================================== ;Space eater EAT: INX H DCR B RZ MOV A,M CPI ' ' JZ EAT RET ;========================================== ;PLACES NEXT NAME IN BUFFER SO CPMLINE MAY PARSE IT TRTOBUF:LHLD BGNMS MVI B,0 LXI D,FCBBUF+2 TBLP: MOV A,M CPI 20H JZ TRBFEND STAX D INX H INX D INR B ;COUNT CHARS IN NAME JMP TBLP ; TRBFEND:INX H MOV A,M ;EAT EXTRA SPACES CPI 20H JZ TRBFEND SHLD BGNMS LXI H,FCBBUF+1 ;PUT # CHARS BEFORE NAME MOV M,B RET ;==================================== ;IN CP/M V.2, IF FILE IS R/O OR SYS, IT IS CHANGED TO 'BAK'. CKCPM2: MVI C,12 CALL BDOS ORA A ;RETURN 0 MEANS CP/M 1 RZ MVI C,STDMA LXI D,80H CALL BDOS MVI C,SRCHF ;SEARCH FOR FILE LXI D,FCB CALL BDOS CPI 0FFH RZ ADD A ADD A ;MULT A-REG BY.. ADD A ADD A ;..32 TO FIND.. ADD A ;..NAME IN DMA. LXI H,80H ADD L MOV L,A ;HL POINTS TO DIR NAME LXI D,9 DAD D ;POINT TO R/O ATTRIB BYTE MOV A,M ANI 80H ;TEST MSB JNZ MKCHG ;IF SET, MAKE CHANGE INX H ;CHECK SYSTEM ATTRIB BYTE MOV A,M ANI 80H RZ ;NOT $SYS OR $R/O DCX H MKCHG: LXI D,-8 DAD D ;POINT HL TO FILENAME + 1 LXI D,FCB+1 ;MOVE DIR NAME TO FCB.. MVI B,11 ;..WITHOUT CHANGING DRIVE. CALL MOVE LXI H,FCB+9 ;R/O ATTRIB MOV A,M ANI 7FH ;STRIP R/O ATTRIB MOV M,A INX H ;SYS ATTRIB MOV A,M ANI 7FH MOV M,A LXI D,FCB MVI C,30 ;SET NEW ATTRIBS IN DIR CALL BDOS ;MAY BE CALLED BY CKBAKUP BELOW. ITS RETURN DONE HERE PLANCHG:LXI H,FCB ;CHANGE NAME TO TYPE "BAK" LXI D,6CH MVI B,9 ;MOVE DRIVE AND NAME (NOT TYPE) CALL MOVE LXI H,75H ;START OF TYPE IN FCB2 MVI M,'B' INX H MVI M,'A' INX H MVI M,'K' LXI D,6CH MVI C,ERASE ;ERASE ANY PREV BACKUPS CALL BDOS LXI H,6CH ;FCB2 DR FIELD SHOULD.. MVI M,0 ;..0 FOR RENAME. LXI D,FCB MVI C,REN CALL BDOS RET CKBAKUP:LDA BAKUPBYTE ORA A RZ MVI C,SRCHF LXI D,FCB CALL BDOS INR A RZ ;FILE NOT FOUND JMP PLANCHG ;IN "CKCPM2" - RET DONE THERE ;==================================== ;receive a sector RCVSECT:XRA A STA ERRCT RCVRPT: CALL VORQ JZ RCVSQ CALL CRLF ;clear col count CALL ILPRT ;comment message DB 'Awaiting # ',0 PUSH H ;SAVE IT LHLD SECNO ;GET SECTOR NUMBER INX H ;BUMP IT CALL DECOUT ;PRINT SECTOR NUMBER IN DECIMAL CALL ILPRT DB ' (', 0 CALL DHXOUT ;16 BIT HEX CONVERSION & OUTPUT CALL ILPRT DB 'H)',0 POP H ;RESTORE IT ; If CRC is in effect, there is only a 7 second wait ; for the first SOH. If the SOH is not received within ; this time, then a NAK is sent which tells the sender ; to use checksum checking instead of CRC. This allows ; automatic compatability with versions of MODEM that ; do not implement Cyclic Redundancy Checking(CRC). RCVSQ: LDA FIRSTME ;first SOH... ORA A ;...been received? JZ RCVSQ2 ;yes, go get next SOH XRA A ;turn off... STA FIRSTME ;...first soh recvd switch LDA CRCFLG ;CRC in... ORA A ;...effect? JNZ RCVSQ2 ;no, do long wait for first SOH MVI B,7 ;wait for upto 7 seconds CALL RECV ;get a character from modem JNC RCVSQ3 ;got a char, go see if SOH LDA QFLG ORA A JZ CRCM CALL ILPRT ;first comment DB CR,LF,'++Switching to CHECKSUM MODE++',CR,LF,0 CRCM: MVI A,'C' ;turn off... STA CRCFLG ;...CRC mode. MVI A,NAK ;send NAK to tell sender checksum CALL SEND ;...is in effect & to start sending. JMP RCVSECT ;go start receiving sector ; RCVSQ2: MVI B,7 ;10 IN ORIG PROG CALL RECV JC RCVSTOT RCVSQ3: CPI SOH JZ RCVSOH ORA A ;IGNORE NULLS JZ RCVSQ CPI EOT STC RZ MOV B,A LDA QFLG ORA A JZ RCVSERR MOV A,B CALL CRLF CALL HEXO CALL ILPRT ;error message DB 'H recv''d, not SOH',CR,LF,0 RCVSERR: MVI B,1 CALL RECV JNC RCVSERR MVI A,NAK CALL SEND LDA ERRCT INR A STA ERRCT CPI ERRLIM JC RCVRPT LDA QFLG ORA A JZ RCVSABT CALL CKQUIT JZ RCVSECT RCVSABT:CALL CLOSFIL CALL ERXIT ;fatal error DB CR,LF,'++Unable to receive block - Aborting++$' RCVSTOT:LDA QFLG ORA A JZ RCVSERR CALL ILPRT ;error message DB CR,LF,'++ Timeout ++ ',0 RCVPRN: LDA ERRCT CALL HEXO CALL CRLF JMP RCVSERR RCVSOH: MVI B,1 CALL RECV JC RCVSTOT MOV D,A MVI B,1 CALL RECV JC RCVSTOT CMA CMP D JZ RCVDATA LDA QFLG ORA A JZ RCVSERR CALL ILPRT ;error message DB CR,LF,'++ Bad sector # in Hdr',CR,LF,0 JMP RCVSERR RCVDATA:MOV A,D STA RCVSNO MVI A,1 STA DATAFLG MVI C,0 ;clear checksum CALL CLRCRC ;clear crc counter LXI H,80H RCVCHR: MVI B,1 CALL RECV JC RCVSTOT MOV M,A INR L JNZ RCVCHR XRA A STA DATAFLG LDA CRCFLG ORA A JZ RCVCRC MOV D,C MVI B,1 CALL RECV JC RCVSTOT CMP D JNZ RCVCERR CHKSNUM:LDA RCVSNO MOV B,A LDA SECNO CMP B JZ RECVACK INR A CMP B JNZ ABORT RET RCVCRC: MVI E,2 ;nr of crc bytes RCV2CRC:MVI B,1 CALL RECV JC RCVSTOT DCR E JNZ RCV2CRC CALL CHKCRC ORA A JZ CHKSNUM LDA QFLG ORA A JZ RCVSERR CALL ILPRT ;error message DB CR,LF,'++CRC error++',0 JMP RCVPRN RCVCERR:LDA QFLG ORA A JZ RCVSERR CALL ILPRT ;error message DB '++Cksum error++ ',0 JMP RCVPRN RECVACK:CALL SENDACK JMP RCVSECT ;=============================== SENDACK:MVI A,ACK CALL SEND RET ;=========================== ;send header SENDHDR:CALL VORQ JZ SENDHNM CALL CRLF ;clear col count CALL ILPRT ;comment message DB 'Send # ',0 PUSH H LHLD SECNO ;GET SECTOR NUMBER CALL DECOUT ;PRINT IT IN DECIMAL CALL ILPRT DB ' (',0 CALL DHXOUT ;16 BIT HEX CONVERSION & OUTPUT CALL ILPRT DB 'H)',0 POP H SENDHNM:MVI A,SOH CALL SEND LDA SECNO CALL SEND LDA SECNO CMA CALL SEND RET ;======================== SENDSEC:MVI A,1 STA DATAFLG MVI C,0 CALL CLRCRC LXI H,80H SENDC: MOV A,M CALL SEND INR L JNZ SENDC XRA A STA DATAFLG RET ;=============================== SENDCKS:MOV A,C CALL SEND RET ;============================== SENDCRC:PUSH H LHLD CRCVAL MOV A,H CALL SEND MOV A,L CALL SEND POP H XRA A RET ;=============================== GETACK: MVI B,7 ;10 IN ORIG PROG CALL RECVDG JC GETATOT CPI ACK RZ MOV B,A LDA QFLG ORA A JZ ACKERR MOV A,B CALL CRLF CALL HEXO CALL ILPRT ;error message DB 'H Recv''d, not ACK',CR,LF,0 ACKERR: LDA ERRCT INR A STA ERRCT CPI ERRLIM RC LDA QFLG ORA A JZ CSABORT CALL CKQUIT STC RZ CSABORT:CALL ERXIT ;fatal error DB CR,LF,'Can''t send sector -- Aborting$' GETATOT:LDA QFLG ORA A JZ ACKERR CALL ILPRT ;error message DB CR,LF,'Timeout on ACK',CR,LF,0 JMP ACKERR ;================================ CKABORT:LDA QFLG ORA A RZ CALL STAT RZ CALL KEYIN CPI CAN JZ ABORT ;local abort RET ERXIT: POP D ;print message and abort MVI C,PRINT CALL BDOS ;does not update col count CALL CRLF ;reset col count ABORT: LXI SP,STAK ABORTL: MVI B,1 CALL RECV ;wait until sender finishes JNC ABORTL MVI A,CAN CALL SEND ABORTW: MVI B,1 CALL RECV ;wait until sender finishes JNC ABORTW MVI A,' ' ;send a space to clear out ^X CALL SEND CALL ILPRT DB CR,LF,'Routine cancelled',CR,LF,BELL,0 LXI H,RESTROPT LXI D,OPTBL MVI B,OPTBE-OPTBL-2 CALL MOVE ;clear options except T XRA A STA BATCHFLG;clear N option JMP DONETA ;=================================== INCRSNO:PUSH H LHLD SECNO ;GET SECTOR NUMBER INX H ;BUMP IT SHLD SECNO ;STORE IT POP H RET ;=============================== AMBGTS: LXI H,FCB+1 MVI C,11 AMBIG: MOV A,M INX H CPI '?' ;test for ambiguous name JNZ NOTAMB CALL ILPRT ;fatal error DB 'Ambiguous filename not allowed',CR,LF,0 JMP MENU NOTAMB: DCR C JNZ AMBIG RET ;============================= ERASFIL:LDA BATCHFLG ;DON'T ASK FOR ERASE.. ORA A ;..IN MULTI-FILE MODE,.. JNZ NOASK LDA QFLG ORA A JZ NOASK ;dont hang up in Q mode TFLERAS:CALL AMBGTS ;ambiguous name ? LXI D,FCB MVI C,SRCHF ;see if file exists already CALL BDOS INR A RZ CALL ILPRT DB 'File exists -- Type ''Y'' to erase: ',BELL,0 CALL KEYIN PUSH PSW CALL CTYPE POP PSW CALL UCASE CPI 'Y' CALL CRLF JNZ MENU NOASK: LXI D,FCB MVI C,ERASE CALL BDOS RET ;=========================== MAKEFIL:LXI D,FCB MVI C,MAKE CALL BDOS INR A RNZ CALL ERXIT ;fatal error DB 'Error - Can''t make file',CR,LF DB 'Directory must be full$' ;================================== CNREC: MVI C,12 CALL BDOS ORA A ;0 means CP/M 1 JZ CNREC14 MVI C,FILSIZ ;COMPUTE FILE SIZE FUNCTION IN CP/M 2.x LXI D,FCB ;POINT TO FILE CONTROL BLOCK CALL BDOS LHLD FCB+33 ;GET RECORD COUNT SHLD RCNT ;STORE IT LXI H,0 ;ZERO HL SHLD FCB+33 ;RESET RANDOM RECORD IN FCB RET CNREC14:MVI A,'?' ;MATCH ALL EXTENTS STA FCBEXT MVI A,0FFH STA MAXEXT ;INIT MAX EXT NO. MVI C,SRCHF ;GET 'SEARCH FIRST' FNC LXI D,FCB CALL BDOS ;READ FIRST INR A ;WERE THERE ANY? JNZ SOME ;GOT SOME CALL ERXIT ;fatal error DB '++File not found++$' ;POINT TO DIRECTORY ENTRY SOME: DCR A ;UNDO PREV 'INR A' ANI 3 ;MAKE MODULUS 4 ADD A ;MULTIPLY... ADD A ;..BY 32 BECAUSE ADD A ;..EACH DIRECTORY ADD A ;..ENTRY IS 32 ADD A ;..BYTES LONG LXI H,80H ;POINT TO BUFFER ADD L ;POINT TO ENTRY ADI 15 ;OFFSET TO RECORD COUNT MOV L,A ;HL NOW POINTS TO REC COUNT MOV B,M ;GET RECORD COUNT DCX H DCX H ;BACK DOWN TO EXTENT NUMBER DCX H LDA MAXEXT ;COMPARE WITH CURRENT MAX. ORA A ;IF NO MAX YET JM BIGGER ;THEN SAVE RECORD COUNT ANYWAY CMP M JNC MOREDIR BIGGER: MOV A,B ;SAVE NEW RECORD COUNT STA RCNT MOV A,M ;SAVE NEW MAX. EXTENT NO. STA MAXEXT MOREDIR:MVI C,SRCHN ;SEARCH NEXT LXI D,FCB CALL BDOS ;READ DIR ENTRY INR A ;CHECK FOR END (0FFH) JNZ SOME ;NOT END OF DIR...PROCESS EXTENT LDA MAXEXT ;HIT END...GET HIGHEST EXTENT NO. SEEN MOV L,A ;WHICH GIVES EXTENT COUNT -1 MVI H,0 MOV D,H LDA RCNT ;GET RECORD COUNT OF MAX EXTENT SEEN MOV E,A ;SAVE IT IN DE DAD H DAD H ;MULTIPLY # OF EXTENTS -1 DAD H ; TIMES 128 DAD H DAD H DAD H DAD H DAD D ;ADD IN SIZE OF LAST EXTENT SHLD RCNT ;SAVE TOTAL RECORD COUNT RET ;================================== OPENFIL:XRA A STA FCBEXT LXI D,FCB MVI C,OPEN CALL BDOS INR A JNZ OPENOK CALL ERXIT ;fatal error DB 'Can''t open file$' OPENOK: CALL VORQ RZ CALL ILPRT ;comment message DB 'File open, size: ',0 LHLD RCNT ;GET RECORD COUNT CALL DECOUT ;PRINT NUMBER OF SECTORS IN DECIMAL CALL ILPRT ;PRINT DB ' (',0 CALL DHXOUT CALL ILPRT DB 'H) sectors',CR,LF,0 RET ;============================= CLOSFIL:LXI D,FCB MVI C,CLOSE CALL BDOS INR A RNZ CALL ERXIT ;fatal error DB 'Can''t close file$' ;================================ RDSECT: LDA SECINBF DCR A STA SECINBF JM RDBLOCK LHLD SECPTR LXI D,80H CALL MOVE128 SHLD SECPTR RET RDBLOCK:LDA EOFLG CPI 1 STC RZ MVI C,0 LXI D,DBUF RDSECLP:PUSH B PUSH D MVI C,STDMA CALL BDOS LXI D,FCB MVI C,READ CALL BDOS POP D POP B ORA A JNZ REOF LXI H,80H DAD D XCHG INR C MOV A,C CPI DBUFSIZ*8 ;BUFFER SIZE IN 128 BYTE SECTORS JZ RDBFULL JMP RDSECLP REOF: MVI A,1 STA EOFLG MOV A,C RDBFULL:STA SECINBF LXI H,DBUF SHLD SECPTR LXI D,80H MVI C,STDMA CALL BDOS JMP RDSECT ;================================== WRSECT: LHLD SECPTR XCHG LXI H,80H CALL MOVE128 XCHG SHLD SECPTR LDA SECINBF INR A STA SECINBF CPI DBUFSIZ*8 ;BUFFER SIZE IN 128 BYTE SECTORS RNZ WRBLOCK:LDA SECINBF ORA A RZ MOV C,A LXI D,DBUF DKWRLP: PUSH H PUSH D PUSH B MVI C,STDMA CALL BDOS LXI D,FCB MVI C,WRITE CALL BDOS POP B POP D POP H ORA A JNZ WRERR LXI H,80H DAD D XCHG DCR C JNZ DKWRLP XRA A STA SECINBF LXI H,DBUF SHLD SECPTR RSDMA: LXI D,80H ;required by CP/M 1.4 MVI C,STDMA CALL BDOS RET WRERR: CALL RSDMA MVI A,CAN CALL SEND CALL ERXIT ;fatal error DB 'Error writing file$' ;=========================================== ;RECV: Receive a character ;Timeout time is in B, in seconds. Entry via 'RECVDG' deletes garbage ;characters on the line. For example, having just sent a sector, calling ;RECVDG will delete any line noise induced characters LONG before the ;ACK/NAK would be received. RECVDG: CALL INMODDATP CALL INMODDATP RECV: PUSH D LDA FASTCLK ORA A JZ MSEC MOV A,B ADD A MOV B,A MSEC: LXI D,15000 ;60% OF ORIG 50000 CALL CKABORT MWTI: CALL STAT ;check keyboard ORA A JZ MWTJ CALL KEYIN ;get char CALL UCASE CALL FLGTGL ;toggle appropriate flag MWTJ: CALL INMODCTLP CALL ANIRCV CALL CPIRCV ;wait for modem char JZ MCHAR DCR E JNZ MWTI DCR D JNZ MWTI DCR B JNZ MSEC POP D STC RET MCHAR: CALL INMODDATP POP D PUSH PSW CALL UPDCRC ;calc crc ADD C MOV C,A LDA RSEEFLG ORA A JZ MONIN LDA VSEEFLG ORA A JNZ NOMONIN LDA DATAFLG ORA A JZ NOMONIN MONIN: POP PSW PUSH PSW CALL SHOW NOMONIN:POP PSW ORA A RET ;============================== ;set/reset secondary options FLGTGL: PUSH B PUSH D PUSH H LXI H,OPTBL ;flag LXI D,RESTROPT MVI C,5 ;do Q,R,S,V,T but NOT N MOV B,A DOCMP: LDAX D ;get reference CMP B JNZ DOCMP1 MOV A,B CMP M ;current setting MOV M,A ;clear flag JNZ DOCMP1 XRA A MOV M,A ;set flag DOCMP1: INX D INX H DCR C JNZ DOCMP POP H POP D POP B RET ;================================= SEND: PUSH PSW LDA SSEEFLG ORA A JZ MONOUT LDA VSEEFLG ORA A JNZ NOMONOT LDA DATAFLG ORA A JZ NOMONOT MONOUT: POP PSW PUSH PSW CALL SHOW NOMONOT:POP PSW PUSH PSW CALL UPDCRC ;calc crc ADD C MOV C,A POP PSW CALL CHRSND ;send to remote CALL STAT ;check for keypress ORA A RZ CALL KEYIN ;get char CALL UCASE CALL FLGTGL ;toggle appropriate flag RET ;================================== WAITNAK:LDA QFLG ORA A JZ WAITNLP CALL ILPRT ;first comment DB 'Awaiting initial NAK',CR,LF,0 WAITNLP:CALL CKABORT MVI B,1 CALL RECV CPI NAK RZ CPI CRC ;crc request? JZ WAITCRC ;yes, go set crc flag DCR E JZ ABORT JMP WAITNLP WAITCRC:XRA A STA CRCFLG LDA QFLG ORA A RZ CALL ILPRT ;first comment DB 'CRC request received',CR,LF,0 RET ;=============================== ;return zero = no message display ;use ahead of comment messages except first and final VORQ: LDA VSEEFLG ORA A RZ LDA QFLG ORA A RET ;================================== INITADR:LHLD 1 LXI D,3 DAD D SHLD VSTAT+1 DAD D SHLD VKEYIN+1 DAD D SHLD VTYPE+1 LXI D,33 DAD D SHLD LISTST+1 RET ;================================= MOVEFCB:LXI H,FCB+16 LXI D,FCB MVI B,16 CALL MOVE XRA A STA FCBRNO STA FCBEXT RET ;========================= CMPDEHL:MOV A,E CMP L RNZ MOV A,D CMP H RET ;============================= ;carry set for non printing ASCII chars + VT & FF FILTER: CPI 8 ;BS RC ;Byte is less than 08H CPI LF+1 ; CMC ;invert carry flag RNC ;get rid of VT & HT, invert BS & LF carry flag CPI CR ; RC ;Byte is less than 0D RZ ;byte = LF,CR or BS, no carry CPI ' ' ;CPI space RC ;Byte is between 0E & 0F, reject CPI 7FH ;Del char CMC ;printable chars to no carry, DEL to carry RET ;============================== ;put C into print buffer, if buffer full byte is lost LISTOUT:MOV A,C ANI 7FH ;remove top bit CALL FILTER ;ignore non printing chars RC LSTBUF: LHLD BUFEND XCHG LHLD BUFRIN INX H CALL CMPDEHL JNZ LBUFF1 ;test for end of buffer LHLD BUFBEG ;reset to start LBUFF1: XCHG LHLD BUFROUT CALL CMPDEHL RZ ;buffer full XCHG SHLD BUFRIN MOV M,C ;save byte in buffer RET ;========================== ;Check printer status, if busy return, else print one char LISTST: CALL $-$ ORA A RZ ;see if printer is busy LHLD BUFRIN XCHG LHLD BUFROUT CALL CMPDEHL RZ ;return if buffer empty INX H PUSH H LHLD BUFEND XCHG POP H CALL CMPDEHL JNZ PRBUF2 LHLD BUFBEG ;reset to start PRBUF2: SHLD BUFROUT MOV E,M MVI C,LIST ;CP/M print function CALL BDOS RET ;============================= CONO: MOV C,A CPI 9 JNZ CONSOP TAB: MVI A,' ' CALL CONSOP ;expand tabs LDA COLCNT ANI 7 JNZ TAB RET CONSOP: MOV C,A LDA LSTFLG ORA A CNZ LISTOUT ;no tabs left by now MOV A,C CALL TYPE MOV C,A LXI H,COLCNT CPI 7FH RZ INR M CPI ' ' RNC DCR M MOV A,M ORA A RZ MOV A,C CPI 8 JNZ TRYCR DCR M RET TRYCR: CPI CR RNZ MVI M,0 RET ;============================= SHOW: CPI LF JZ CTYPE CPI CR JZ CTYPE PUSH PSW PUSH H LXI H,TWIDTH LDA COLCNT ;check terminal width CMP M CNC CRLF ;force CR,LF POP H POP PSW CPI 9 JZ CTYPE CPI ' ' JC SHOWHEX CPI 7FH JC CTYPE SHOWHEX:PUSH PSW MVI A,'(' CALL CTYPE POP PSW CALL HEXO MVI A,')' CTYPE: PUSH PSW PUSH B PUSH D PUSH H ANI 7FH ;ensure parity bits are suppressed CALL CONO ;type with tabs expanded POP H POP D POP B POP PSW RET ;============================= CRLF: PUSH PSW MVI A,CR CALL CTYPE MVI A,LF CALL CTYPE POP PSW RET ;========================== TYPE: PUSH PSW PUSH B PUSH D PUSH H ANI 7FH ;ensure parity bits are suppressed MOV C,A VTYPE: CALL $-$ POP H POP D POP B POP PSW RET ;========================= STAT: PUSH B PUSH D PUSH H CALL LISTST ;o/p next char to printer VSTAT: CALL $-$ ;get keyboard status POP H POP D POP B ORA A RET ;=============================== KEYIN: PUSH B PUSH D PUSH H VKEYIN: CALL $-$ POP H POP D POP B RET ;=========================== UCASE: CPI 61H ;CHANGES LOWER CASE CHARACTER.. RC ;..IN A-REG TO UPPER CASE. CPI 7BH RNC ANI 5FH RET ;============================ DECOUT: PUSH PSW PUSH B PUSH D PUSH H LXI B,-10 LXI D,-1 DECOU2: DAD B INX D JC DECOU2 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOUT MOV A,E ADI '0' CALL CTYPE POP H POP D POP B POP PSW RET ;====================================== ; - double precision hex output routine. DHXOUT: PUSH H PUSH PSW MOV A,H ;GET MS BYTE ORA A CNZ HEXO ;suppress high order if zero MOV A,L ;GET LS BYTE CALL HEXO ;OUTPUT LOW ORDER BYTE POP PSW POP H RET ;=============================== HEXO: PUSH PSW RAR RAR RAR RAR CALL NIBBL POP PSW NIBBL: ANI 0FH CPI 10 JC ISNUM ADI 7 ISNUM: ADI '0' JMP CTYPE ;===================================== ;RETNS W/ ZERO SET IF RETRY ASKED. IF MULTI-FILE MODE, THEN ;NO QUESTIONS ASKED, JUST QUIT CKQUIT: LDA BATCHFLG ORA A JZ CKQTASK ;ASK FOR RETRY INR A ;RESET ZERO FLG RET CKQTASK:XRA A STA ERRCT CALL ILPRT DB 'Multiple errors encountered.',CR,LF DB 'Type Q to quit, R to retry: ',BELL,0 CALL KEYIN PUSH PSW CALL CTYPE CALL CRLF POP PSW CALL UCASE CPI 'R' RZ CPI 'Q' JNZ CKQUIT ORA A RET ;============================== ILPRT: XTHL ILPLP: MOV A,M ORA A JZ ILPRET CALL TYPE INX H JMP ILPLP ILPRET: XTHL RET ;========================= MOVE128:MVI B,128 MOVE: MOV A,M STAX D INX H INX D DCR B JNZ MOVE RET ;================================== ;Cyclic Redundancy Check Subroutines. ;The generator is X^16 + X^12 + X^5 +1 as ;recommended by CCITT ;Generate table for fast CRC calculation INITCRC:LXI H,CRCTBL MVI C,0 GLOOP: XCHG LXI H,0 MOV A,C PUSH B MVI B,8 XRA H MOV H,A LLOOP: DAD H JNC LSKIP MVI A,10H XRA H MOV H,A MVI A,21H XRA L MOV L,A LSKIP: DCR B JNZ LLOOP POP B XCHG MOV M,D INR H MOV M,E DCR H INX H INR C JNZ GLOOP RET ;================================= ;Reset CRC Accumulator for a new message. CLRCRC: PUSH H LXI H,0 SHLD CRCVAL POP H RET ;==================================== ;Update CRC Accumulator using byte in (A). UPDCRC: PUSH PSW PUSH B PUSH D PUSH H LHLD CRCVAL XCHG MVI B,0 XRA D MOV C,A LXI H,CRCTBL DAD B MOV A,M XRA E MOV D,A INR H MOV E,M XCHG SHLD CRCVAL POP H POP D POP B POP PSW RET ;================================== ; Check CRC bytes of received message. CHKCRC: PUSH H LHLD CRCVAL MOV A,H ORA L POP H RZ MVI A,0FFH RET ; CRCVAL: DW 0 ;====================================== ;INBUFF - DUPLICATES READ BUFFER ROUTINE ;SAME AS CP/M FUNCTION 10, BUT DOES ;NOT USE CTRL-C (REASON FOR ROUTINE). ;DOES ALLOW CONTROLS U, R, E, AND H (BACKSPACE). INBUFF: PUSH PSW PUSH H PUSH B PUSH D ;DE REGISTERS MUST BE PUSHED LAST ISTART: CALL ICLEAR ;CLEAR THE BUFFER AREA POP D ;GET ADDRESS OF BUFFER ON RETRIES PUSH D ;RESTORE STACK XRA A INX D ;ADDRESS COUNT FIELD STAX D ;INITIALIZE WITH A ZERO IN COUNT BYTE INX D XCHG ;ADDRESS FIRST BUFFER BYTE WITH HL INBUFA: CALL CONIN CPI 0DH ;IS IT A RETURN? JZ INBUFR ;IF SO, THEN RETURN CPI 7FH ;IS IT A DELETE? JZ DELETE CPI 8 ;CTRL-H WILL BACKSPACE.. JZ DELETE ;..OVER DELETED CHAR. CPI 'U'-40H ;IS IT A CTRL-U JZ INBUFO ;OUTPUT # CR LF AND START OVER CPI 'R'-40H ;CTRL-R RETYPES LINE JZ RETYPE CPI 'E'-40H JZ PCRLF CPI 20H ;NO CONTROL CHARACTERS OTHER.. JC INBUFA ;..THAN ABOVE ALLOWED. MOV B,A ;SAVE INPUTTED CHARACTER XCHG ;SAVE HL IN DE POP H ;GET ADDRESS OF BUFFER IN HL PUSH H ;RESTORE STACK INX H ;ADDRESS COUNT BYTE INR M ;INCREASE COUNT BYTE DCX H ;ADDRESS MAXIMUM MOV A,M ;PUT MAXIMUM IN A INX H ;ADDRESS COUNT CMP M ;COMPARE COUNT TO MAXIMUM JC ALERT ;IF MAXIMUM, RING BELL AND WAIT FOR CR XCHG ;RESTORE BUFFER POINTER TO HL MOV M,B ;PUT INPUTTED CHARACTER IN BUFFER MOV A,B ;OUTPUT IT CALL TYPE INX H ;BUMP POINTER JMP INBUFA ;GET NEXT CHARACTER DELETE: XCHG ;SAVE BUFFER POINTER IN DE POP H ;ADDRESS BEGINNING OF BUFFER PUSH H ;RESTORE STACK INX H ;ADDRESS COUNT FIELD MOV B,A ;SAVE DELETE CHAR - 7FH OR 08H MOV A,M SUI 1 ;DECREASE COUNT MOV M,A JC NODEL ;DON'T DELETE PAST BEGINING OF BUFFER. XCHG ;RESTORE BUFFER POINTER TO HL DCX H ;POINT TO LAST BYTE INPUTTED MOV A,B ;GET BACK EITHER 7FH OR 08H MOV B,M ;GET CHARACTER BEING DELETED MVI M,20H ;RESTORE BLANK CPI 8 JZ BKSPC CPI 7FH JZ BKSPC0 JMP INBUFA ;GET NEXT CHARACTER NODEL: INR M ;DON'T LEAVE COUNT NEGATIVE XCHG ;RESTORE POINTER TO HL JMP INBUFA BKSPC0: MVI A,08H BKSPC: CALL TYPE ;TRUE ERASE IF 08H MVI A,20H CALL TYPE MVI A,8 CALL TYPE JMP INBUFA INBUFO: MVI A,'#' CALL TYPE CALL CRLF JMP ISTART RETYPE: POP D PUSH D INX D ;POINT TO CURRENT NUMBER.. LDAX D ;..OF CHARACTERS. MOV B,A MVI A,'#' CALL TYPE CALL CRLF MOV A,B ;TEST IF ZERO INPUT ORA A JZ INBUFA CTLRLP: INX D LDAX D CALL TYPE DCR B JNZ CTLRLP JMP INBUFA ALERT: MVI A,7 CALL TYPE DCR M XCHG JMP INBUFA PCRLF: CALL CRLF JMP INBUFA INBUFR: CALL CRLF POP D POP B POP H POP PSW RET ICLEAR: POP D ;ACCOUNTS FOR CALL POP H ;ADDRESS BUFFER IN HL PUSH H ;RESTORE.. PUSH D ;..STACK MOV B,M ;SAVE MAXIMUM IN B INX H ;POINT TO FIRST.. INX H ;..BUFFER BYTE. MVI A,20H CLEARL: MOV M,A INX H DCR B JNZ CLEARL RET CONIN: PUSH H PUSH D PUSH B CONINLP:CALL STAT JZ CONINLP CALL KEYIN CALL UCASE POP B POP D POP H RET ;========================================== ;LOADS A COMMAND LINE ADDRESSED BY DE REGISTERS (MAX # CHARACTERS IN LINE ;IN DE, NUMBER OF CHARS IN LINE IN DE+1, LINE STARTS IN DE+2) INTO FCB ;ADDRESSED BY HL REGISTERS. THE FCB SHOULD BE AT LEAST 33 BYTES IN LENGTH. ;THE COMMAND LINE BUFFER MUST HAVE A MAXIMUM LENGTH OF AT LEAST ONE MORE ;THAN THE GREATEST NUMBER OF CHARACTERS THAT WILL BE NEEDED. CPMLINE:PUSH PSW PUSH B PUSH D PUSH H CALL INIT ;FILLS FCBS WITH BLANKS AND NULLS XCHG ;GET START OF COMMAND LINE IN HL. INX H ;ADDRESS # BYTES IN CMD LINE. MOV E,M ;LOAD DE PAIR WITH # BYTES. MVI D,0 INX H DAD D ;POINT TO BYTE AFTER LAST CHAR.. MVI M,0DH ;..IN CMD LINE AND STORE DELIMITER. POP H ;RESTORE HL AND DE. POP D PUSH D PUSH H INX D ;ADDRESS START OF COMMAND. INX D CALL CDRIVE MVI C,8 ;TRANSFER FIRST FILENAME TO FCB. CALL TRANS CPI 0DH JZ CDONE CPI 20H ;IF SPACE, THEN START OF.. JZ NAME2 ;..SECOND FILENAME. POP H ;FILETYPE MUST BE AFTER.. PUSH H ;..EIGHTH BYTE OF NAME. LXI B,9 DAD B MVI C,3 ;TRANSFER TYPE OF FIRST FILE CALL TRANS CPI 0DH JZ CDONE NAME2: LDAX D ;EAT MULTIPLE SPACES.. CPI 20H ;..BETWEEN NAMES. JNZ NAME2C INX D JMP NAME2 LDAX D CPI 0DH ;TEST IF FIRST NAME.. JZ CDONE ;..ONLY AND THEN SPACE. NAME2C: POP H ;SECOND NAME STARTS IN 16TH BYTE. PUSH H ;POINT HL TO THIS BYTE. LXI B,16 DAD B CALL CDRIVE MVI C,8 CALL TRANS CPI 0DH JZ CDONE POP H ;SECOND TYPE STARTS IN 25TH BYTE. PUSH H LXI B,25 DAD B MVI C,3 CALL TRANS CDONE: POP H PUSH H INX H ;POINT TO FIRST CHAR OF FIRST NAME IN FCB. CALL CSCAN ;CHECK FOR * (AMBIGUOUS NAMES). POP H PUSH H LXI B,17 ;POINT TO FIRST CHAR OF SECOND NAME IN FCB. DAD B CALL CSCAN POP H POP D POP B POP PSW RET INIT: PUSH H ;INITIALIZES FCB WITH 1 NULL (FOR FIRST DRIVE),.. PUSH B ;..11 BLANKS, 4 NULLS, 1 NULL (FOR 2ND DRIVE),.. MVI M,0 ;..11 BLANKS, AND 4 NULLS. INX H MVI B,11 MVI A,20H CALL INITFILL MVI B,5 MVI A,0 CALL INITFILL MVI B,11 MVI A,20H CALL INITFILL MVI B,4 MVI A,0 CALL INITFILL POP B POP H RET INITFILL: MOV M,A INX H DCR B JNZ INITFILL RET CDRIVE: INX D ;CHECK 2ND BYTE OF FILENAME. IF IT.. LDAX D ;..IS A ":", THEN DRIVE WAS SPECIFIED. DCX D CPI ':' JNZ DEFDR ;ELSE ZERO FOR DEFAULT DRIVE ('INIT' PUT ZERO) LDAX D ANI 5FH SUI 40H ;CALCULATE DRIVE (A=1, B=2,...).. MOV M,A ;..AND PLACE IT IN FCB. INX D ;ADDRESS FIRST BYTE OF.. INX D ;..IN CMD LINE,.. DEFDR: INX H ;..AND NAME FIELD IN FCB. RET TRANS: LDAX D ;TRANSFER FROM CMD LINE TO FCB.. INX D ;..UP TO NUMBER OF CHARS SPECIFIED.. CPI 0DH ;..BY C-REG. KEEP SCANNING FIELD.. RZ ;..WITHOUT TRANSFER UNTIL A DELIMITING.. CPI '.' ;..FIELD CHAR SUCH AS '.', BLANK, OR.. RZ ;..C/R (FOR END OF CMD LINE). CPI 20H RZ DCR C JM TRANS ;ONCE C-REG IS LESS THAN ZERO, KEEP READING.. MOV M,A ;..CMD LINE BUT DO NOT TRANSFER TO FCB. INX H JMP TRANS CSCAN: MVI B,8 ;SCAN FILE NAME ADDRESSED BY HL. TSTNAM: MOV A,M CPI '*' ;IF '*' FOUND, FILL IN REST OF FIELD.. JZ FILL1 ;..WITH '?' FOR AMBIGUOUS NAME. INX H DCR B JNZ TSTNAM JMP TSTTYP FILL1: CALL FILL TSTTYP: MVI B,3 ;SCAN AND FILL TYPE FIELD FOR NAME.. TSTLTYP:MOV A,M ;..SPECIFIED ABOVE. CPI '*' JZ FILL2 INX H DCR B RZ JMP TSTLTYP FILL2: CALL FILL RET FILL: MVI M,'?' ;ROUTINE TRANSFERS '?'. INX H DCR B JNZ FILL RET ;====================================== ;IN-LINE COMPARE. COMPARES STRING ADDRESSED BY DE-REG TO STRING ;AFTER CALL (ENDS WITH ZERO). RETURN WITH CARRY SET MEANS STRINGS ;NOT THE SAME. ALL REGISTERS EXCEPT A-REG ARE UNAFFECTED. ILCOMP: XTHL ;POINT HL TO 1ST CHAR. PUSH D ILCMPL: MOV A,M ;HL POINTS TO IN-LINE STRING. ORA A ;END OF STRING IF ZERO. JZ SAME LDAX D CMP M JNZ NOTSAME INX H INX D JMP ILCMPL NOTSAME:MVI A,0 ;IF NOT SAME, FINISH THRU.. NSLP: INX H ;..STRING SO RETURN WILL.. CMP M ;..GO TO INSTRUCTION AFTER.. JNZ NSLP ;..STRING AND NOT REMAINDER OF STRING. STC SAME: POP D INX H ;AVOIDS A NOP INSTRUCTION.. XTHL ;..WHEN RETURNING. RET ;================================== ;MULTI-FILE ACCESS SUBROUTINE. ALLOWS PROCESSING ;OF MULTIPLE FILES (E.G. *.ASM) FROM DISK. THIS ;ROUTINE BUILDS THE PROPER NAME IN THE FCB EACH ;TIME IT IS CALLED. ;THE FCB WILL BE SET UP WITH THE NEXT NAME, READY TO ;DO NORMAL PROCESSING (OPEN, READ, ETC.) WHEN ROUTINE IS CALLED. ;CARRY IS SET IF NO MORE NAMES CAN BE FOUND MFNAME: PUSH B PUSH D PUSH H MVI C,STDMA LXI D,80H CALL BDOS POP H POP D POP B XRA A STA FCBEXT LDA MFFLG1 ORA A JNZ MFN01 MVI A,1 STA MFFLG1 LXI H,FCB LXI D,MFREQ LXI B,12 CALL MOVER LDA FCB STA MFCUR ;SAVE DISK IN CURR FCB LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVER PUSH B PUSH D PUSH H MVI C,SRCHF LXI D,FCB CALL BDOS POP H POP D POP B JMP MFN02 MFN01: LXI H,MFCUR LXI D,FCB LXI B,12 CALL MOVER PUSH B PUSH D PUSH H MVI C,SRCHF LXI D,FCB CALL BDOS POP H POP D POP B LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVER PUSH B PUSH D PUSH H MVI C,SRCHN LXI D,FCB CALL BDOS POP H POP D POP B MFN02: INR A STC JNZ MFFIX1 STA MFFLG1 RET MFFIX1: DCR A ANI 3 ADD A ADD A ADD A ADD A ADD A ADI 81H MOV L,A MVI H,0 PUSH H ;SAVE NAME POINTER LXI D,MFCUR+1 LXI B,11 CALL MOVER POP H LXI D,FCB+1 LXI B,11 CALL MOVER XRA A STA FCBEXT STA FCBRNO RET ; ;MULTI-FILE ACCESS WORK AREA ; MFFLG1: DB 0 ;1ST TIME SW MFREQ: DS 12 ;REQ NAME MFCUR: DS 12 ;CURR NAME ;===================================== ;MOVE SUBROUTINE MOVER: MOV A,M STAX D INX H INX D DCX B MOV A,B ORA C JNZ MOVER RET ;================================ DIRLST: LXI D,CMDBUF ;PUT COMMAND LINE IN FCB LXI H,5CH CALL CPMLINE LXI H,SRCHFCB CALL INITFCBS LDA 6CH ;GET DRIVE # STA SRCHFCB LDA 6DH CPI 20H ;IF BLANK GET ALL NAMES PUSH PSW CZ QSTMARK POP PSW CNZ MOVENAME ;ELSE MOVE NAME INTO FCB CALL DRIVE LXI D,80H MVI C,STDMA CALL BDOS XRA A STA DNAMECT ;CR AFTER 4 NAMES LXI D,SRCHFCB MVI C,SRCHF ;DO FIRST SEARCH CALL BDOS CPI 0FFH JZ NOFILE DIRLP: CALL GETADD LXI D,15 ;OFFSET FOR RECORD COUNT DAD D MOV A,M ORA A JZ NEXTSR ;NO LIST IF FILE IS ZERO LENGTH LXI D,-5 DAD D ;POINT TO $SYS ATTRIB BYTE MOV A,M ANI 80H JNZ NEXTSR ;NO LIST IF $SYS FILE LXI D,-10 DAD D ;POINT TO BEGINNING OF NAME INX H ;POINT TO FIRST LETTER LXI D,PRNTNAME MVI B,8 CALL MOVE INX D MVI B,3 CALL MOVE CALL ILPRT PRNTNAME: DB ' ',' : ',0 ;12 SPACES LDA DNAMECT INR A STA DNAMECT ANI 03H ORA A CZ CRLF NEXTSR: LXI D,SRCHFCB MVI C,SRCHN ;DO NEXT SEARCH CALL BDOS CPI 0FFH JZ DIRDONE JMP DIRLP NOFILE: CALL ILPRT DB 'NOT FOUND',0 DIRDONE:CALL CRLF RET QSTMARK:MVI A,'?' ;IF BLANK IN FCB, PUT IN 11 ?'s MVI B,11 LXI H,SRCHFCB+1 QSTLP: MOV M,A INX H DCR B JNZ QSTLP RET MOVENAME: LXI H,6DH LXI D,SRCHFCB+1 MVI B,11 CALL MOVE ;MOVE IN CP/M PROGRAM RET GETADD: ANI 03H ;GET MOD4 FOR CP/M 1.4 ADD A ADD A ADD A ;ADD 32 ADD A ADD A MOV E,A MVI D,0 LXI H,80H ;ADD DMA OFFSET DAD D RET DRIVE: LDA SRCHFCB ;IF NO DRIVE, CAL ORA A ;LOGGED IN DRIVE JZ CALCDR ADI 40H JMP PRNTHD CALCDR: MVI C,25 CALL BDOS ADI 41H PRNTHD: STA DRNAME CALL ILPRT DB CR,LF,'DRIVE ' DRNAME: DB ' ',CR,LF,0 RET SRCHFCB:DS 33 DNAMECT:DS 1 ;================================ NFILFLG:DB FALSE ;Terminal file open flag OPTION: DB 0 ;primary option OPTBL EQU $ QFLG: DB 'Q' ;secondary option flags RSEEFLG:DB 'R' SSEEFLG:DB 'S' VSEEFLG:DB 'V' TERMFLG:DB 'T' BATCHFLG:DB 'N' ;0=Non-batch OPTBE EQU $ RESTROPT: ;MUST BE IN SAME ORDER AS TABLE ABOVE DB 'Q','R','S','V','T','N' CRCFLG: DB 'C' ;use CRC instead of cksum RESTSN: DB 0,0,0,0,0 DW DBUF DB 0,0,0,0,0,0,0 SECNOB EQU $ RCVSNO: DB 0 SECNO: DW 0 ;sector no. ERRCT: DB 0 EOFLG: DB 0 ;end of file flag SECPTR: DW DBUF SECINBF: DB 0 MAXEXT: DB 0 RCNT: DW 0 ;no. of records to send DATAFLG: DB 0 EXACFL: DB 0 ;literalise next char flag COLCNT: DB 0 ;column counter SECNOE EQU $ FSTFLG: DB TRUE ;multiple file transmit flag FIRSTME:DB 0FFH ;first SOH received switch(it is zero after 1st SOH) LSTFLG: DB FALSE ;true=print echo on LSTRET: DB FALSE ;print toggle save BUFBEG: DW PRTBUF ;start of prtbuf BUFRIN: DW PRTBUF ;i/p pointer BUFROUT:DW PRTBUF ;o/p pointer BUFEND: DW PRTBUF+(RING*1024) ;end of prtbuf CMDBUF: DB 80H,0 DS 80H HLSAVE: DS 2 ;memory pointer, terminal mode DISKNO: DS 1 SENDFLG:DS 1 NBSAVE: DS 2 BGNMS: DS 2 FILECT: DS 1 NAMECT: DS 1 DS 64 STAK: DS 2 FCB3: DS 33 ;terminal file fcb FCB4: DS 33 ;file transfer fcb FCBBUF: DS 15 DBUF: DS DBUFSIZ*1024 ;FILE BUFFER FOR SEND & RECIEVE MODES NAMEBUF:DS 64*12 ;BUFFER FOR FILE NAMES IN BATCH MODE. ORG $ + 100H AND 0FF00H ;must be on page boundary CRCTBL: DS 512 ;crc lookup table PRTBUF: DS RING*1024 ;print buffer BOTTRAM:DS 1 ;MEMORY BUFFER FOR TERMINAL FILE END 100H