TITLE 'CP/M MODEM PROGRAM Version 7.3' ; ;THE FOLLOWING IS AN EXTENSIVE REVISION OF THE CP/M MODEM PROGRAM ;CREATED BY WARD CHRISTENSEN FOR THE CP/M USERS LIBRARY. ;IT ALSO INCORPORATES ROUTINES FOUND IN THE POTOMAC MICRO-MAGIC MODEM ;MANUAL WHICH MAY BE USED IF YOU HAVE A PMMI MODEM BOARD. ;THE ADDITIONAL ROUTINES ARE COPYRIGHTED (1980) BY: ;Mark M. Zeiger and James K. Mills ;198-01B 67th Ave. 824 Jordan Place ;Flushing, N.Y. 11365 Rockford, IL 61108 ;(212) 454-6985 (815) 398-0579 ;Permission is granted to use, but not to sell, these routines. ;10/18/81 Added CRC option. This is another secondary option ;that is specified by giving a 'C'. ; MODEM RC.600 fn.ft ; MODEM ROC.300 fn.ft etc. ; note: cannot have more than 6 secondary options. ;When the file receive cmd. specifies CRC, the ltr. 'C' is ;sent in place of the initial NAK. This signals the sender ;(XMODEM54 or equiv.) that CRC is in effect. The sending ;program will repalce the checksum with the CRC 2 bytes. ;CRC will give better than a 99.99% probability that there ;are no data errors. Code copied from MODEM213, thanks to ;John Mahr and Paul Hansknecht for the implementation. (WDE) ;10/11/81 Add first NAK to RCVFIL to speed up start ;Removed monitor scroll from good block messages ;CTL-^ forces send of next char in T mode (for ctl-E,ctl-D) (WDE) ;07/05/81 Added BRR ctrl char chgs, my number list (Bill Earnest) ;06/05/81 Deleted some unneeded messages in the dial routines. (Bob Clyne) ;05/31/81 Added detection of framing, overrun, and parity errors for ; Receive file routine. (A modified version of the routines in ; MODEM V2.06) ; Added provisions to send and receive either even or odd parity ; bit with PMMI modem in the 'S'end or 'R'eceive file modes - resets ; to no parity in other modes. Use of the parity feature will slow ; transfers slightly due to the extra (parity) bit being sent with ; each character. Also this is the only program that I KNOW OF that ; actually sends, or sets up the PMMI to receive, the parity bit. ; Both ends must be set to the same parity for it to work. Parity ; is invoked by adding a '0' (ASCII) for even parity or a '1' (ASCII) ; for odd parity to the 'S'end or 'R'eceive command string eg. R0.600. ; Changed timing for sending 'B'reak in the terminal mode. ; Changed the code so that the 'M'enu command works from the keyboard ; even when in XPR (expert) mode. ; Added display of hex in addition to decimal numbers for file length ; and sector numbers. ; Removed provision for remote cancel of file transfers in the 'S'end ; and 'R'eceive modes to prevent line noise from aborting a transfer. ; (Bob Clyne) ;02/15/81 Patched in the ringback routines from DIAL6/23. It doesn't ; seem to be able to recognize when the other phone is ringing ; though so it is a little shakey. ; Put in routines to calculate file sizes and sector numbers in ; decimal. ; Put in code to transmit a "BREAK" with a PMMI for use with ; computers which use BREAK instead of Control S to suspend ; output. Control P is now the baudrate change request key ; and Control @ is the BREAK key.. (Bob Clyne) ;12/18/80 Changed disconnect timing. ;10/26/80 Minor revision to allow 25-second 'wait' after PMMI ; autodial -- longer time required for Chicago CBBS*. Jim Mills. ; * CBBS is a trademark of Ward Christensen and Randy Suess. MACLIB MODEM7 ;CONTAINS CMDLINE, INBUF, INLNCOMP, ;DIR, AND MFACCESS ROUTINES ;changed to MODEM.LIB by Jim Mills ;to differentiate from other 'MACROS.LIB' TRUE EQU 0FFH FALSE EQU 0 CPM2X EQU TRUE ;true if CP/M 2.X DBUFSIZ EQU 16 ;BUFFER SIZE IN KBYTES ; PMMI EQUATES PORT EQU 0C0H ;PMMI BASE ADDRESS MODCTLP EQU PORT ;MODEM CONTROL PORT MODSNDB EQU 1 ;MODEM SEND BIT (XMIT BUFF EMPTY) MODSNDR EQU 1 ;MODEM SEND READY MODRCVB EQU 2 ;MODEM RECEIVE BIT (DAV) MODRCVR EQU 2 ;MODEM RECEIVE READY MODDATP EQU PORT+1 ;MODEM DATA PORT BAUDRP EQU PORT+2 ;BAUD RATE PORT MODCTL2 EQU PORT+3 ;2ND MODEM CONTROL PORT ORIGMOD EQU 1DH ;ORIGINATE MODE ANSWMOD EQU 1EH ;ANSWER MODE BRKMSK EQU 0FBH ;MASK TO SET BREAK FRMER EQU 20H ;FRAMING ERROR MASK ORUNER EQU 10H ;OVERRUN ERROR MASK PARER EQU 08H ;PARITY ERROR MASK ODPARMSK EQU 0CFH ;MASK TO SET ODD PARITY EVPARMSK EQU 20H ;MASK TO SET EVEN PARITY NOPARMSK EQU 10H ;MASK TO RESET TO NO PARITY ERRCDMSK EQU 38H ;MASK TO BLOCK ALL BITS EXCEPT ERROR CODES WAITCTS EQU 255 ;number of seconds X 10 to wait for computer ;tone after pmmi auto-dial function, 255 MAX. CHGBAUD EQU 'P'-40H ;USED IN TERMINAL MODE TO CHANGE ;BAUD RATE 'ON THE FLY' ERRLIM EQU 10 ;NUMBER OF TIMES TO RETRY ;SEND/RECEIVE ERRORS BEFORE QUIT BRKCHR EQU '@'-40H ; ^@ = TRANSMIT "BREAK" WITH PMMI EXITCHR EQU 'E'-40H ; ^E = EXIT WITHOUT DISCONNECT DISCCHR EQU 'D'-40H ; ^D = DISCONNECT TRANCHR EQU 'T'-40H ; ^T = TRANSFER CHARACTER CAN EQU 'X'-40H ; ^X = CANCEL SEND/RECEIVE EOFCHAR EQU 'Z'-40H ; ^Z = END OF FILE SAVECHR EQU 'Y'-40H ; ^Y = SAVE CHARACTER XOFF EQU 'S'-40H ; ^S = XOFF CHARACTER XON EQU 'Q'-40H ; ^Q = XON CHARACTER EXTCHR EQU '^'-40H ; ^^ = SEND NXT CHR 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 CLSUM BDNMCH EQU 75H ; BAD NAME MATCH OKNMCH EQU ACK ; OKAY NAME MATCH LF EQU 10 ; LINEFEED CR EQU 13 ; CARRIAGE RETURN BELL EQU 7 ; BELL CHARACTER FRONTPAN EQU 0FFH ; IMSAI FRONT PANEL BOTTRAM SET LAST+100H AND 0FF00H ORG 100H JMP START ;THESE ROUTINES ARE AT THE BEGINNING OF THE PROGRAM SO ;THEY CAN BE PATCHED BY A MONITER WITHOUT RE-ASSEMBLING ;THE PROGRAM. PMMIBYTE DB TRUE ;true=pmmi modem IMSAIBYTE DB FALSE ;true=imsai front panel FASTCLK DB TRUE ;4 MHz or greater BAKUPBYTE DB FALSE ;true=make .BAK file XPRFLG DB TRUE ;true=no menu, false=print menu PULSERATE DB 125 ;125 FOR 20PPS, 250 FOR 10PPS dialing IN$MODCTLP IN MODCTLP ! RET ;in modem control port OUT$MODDATP OUT MODDATP ! RET ;out modem data port ANI$MODSNDB ANI MODSNDB ! RET ;bit to test for send ready CPI$MODSNDR CPI MODSNDR ! RET ;value of send bit when ready IN$MODDATP IN MODDATP ! RET ;in modem data port ANI$MODRCVB ANI MODRCVB ! RET ;bit to test for receive ready CPI$MODRCVR CPI MODRCVR ! RET ;value of receive bit when ready JMP$INITMOD JMP INITMOD ;to initialize port, if necessary IN$BAUDRP IN BAUDRP ! RET ;in baudrate port OUT$BAUDRP OUT BAUDRP ! RET ;out baudrate port OUT$MODCTL2 OUT MODCTL2 ! RET ;out modem control port #2 OUT$MODCTLP OUT MODCTLP ! STA UARTCTLB ! RET ;out modem control port ;and store control byte CRFLAG DB 0 ;CONTINUOUS REDIAL FLAG ; PHONE NUMBER LIBRARY TABLE FOR DIALING FROM LIBRARY ; OF NUMBERS STORED IN THESE DB'S AT ASSEMBLY-TIME. ; EACH DB MUST BE 30 CHARACTERS LONG FOR PROPER OPERATION. ; A 'DB 0' INDICATES NO DIALING, PROGRAM WILL DISCONNECT ; AND RETURN TO COMMAND MODE. LAST DB MUST BE DB 0. UP TO ; 26 NUMBERS ARE ALLOWED. ; '----5---10---15---20---25---30' NUMBLIB DB 'A=Atlanta CBBS 404-394-4220' ;'A' DB 'B=Ben Bronson 312-955-4493' ;'B' DB 'C=CBBS - Detroit 313-288-0335' ;'C' DB 'D=C.Cliff C.C. 312-234-9257' ;'D' DB 'E=Ron Fowler 313-729-1905R' ;'E' DB 'F=FORUM Union NJ 688-7117' ;'F' DB 'G= ' ;'G' DB 'H= ' ;'H' DB 'I= ' ;'I' DB 'J= ' ;'J' DB 'K=David Kozinn 216-334-4604' ;'K' DB 'L= ' ;'L' DB 'M=Kelly Smith 805-527-9321' ;'M' DB 'N=Tim Nicholas 516-698-8619R' ;'N' DB 'O=SYSOP Sys 313-885-0506' ;'O' DB 'P=K.Petersen 313-588-7054R' ;'P' DB 'Q= ' ;'Q' DB 'R=Bruce Ratoff 272-1874' ;'R' DB 'S=ACGNJ ABBS 968-1074' ;'S' DB 'T=Tech. CBBS 313-846-6127' ;'T' DB 'U= ' ;'U' DB 'V= ' ;'V' DB 'W= ' ;'W' DB 'X= ' ;'X' DB 'Y= ' ;'Y' DB 'Z= ' ;'Z' DB 0 ; end START LXI H,0 DAD SP ;GET CP/M'S STACK SHLD STACK ;SAVE IT LXI SP,STACK ;START LOCAL STACK CALL START1 DB CR,LF,'MODEM 7.3 as of 10/18/81',cr,lf DB 'Originally Written by Ward Christensen',cr,lf DB 'Revisions by Mark M. Zeiger, Jim Mills',cr,lf,'$' START1 POP D ;GET ADDRESS OF ABOVE MESSAGE MVI C,PRINT ; 9 CALL BDOS CALL INITADR ;INITIALIZE ADDRESSES MVI A,TRUE ; 0FFH STA NFILFLG CMA ; 0 STA SAVEFLG OUT FRONTPAN ; IMSAI CALL PROCOPT ;PROCESS CONTROL OPTIONS LDA OPTION ;GET MAIN OPTION CPI 'X' ;EXPERT FLAG? JNZ RESTART ;NO MVI A,TRUE ;YES STA XPRFLG ;MAKE EXPERT JMP MENU RESTART LDA OPTION ;GET MAIN OPTION MOV B,A ;SAVE IT LDA PMMIBYTE ;PMMI? ORA A ;SET FLAGS MOV A,B ;GET OPTION BACK JZ S1 ;NOT PMMI CPI 'C' ;CALL (DIAL) FUNCTION? JZ DIALPL ;YES, GO TO IT S1 CPI ' ' ;NO OPTION SPEC'D? JZ MENU ;TRUE, GO MENU CPI 'M' ;MENU ASKED FOR? JZ MENU2 ;YES, GO MENU CALL JMP$INITMOD ; CALL MOVEFCB MVI A,FALSE STA NFILFLG CALL IN$MODDATP ;GOBBLE UP GARBAGE.. CALL IN$MODDATP ;..CHARACTERS ON LINE LDA OPTION ;PROCESS MAIN OPTION CPI 'E' ;ECHO MODE? JZ TRMECHO ;YES CPI 'T' ;TERMINAL MODE? JZ DSKSAVE ;YES CPI 'S' ;SEND A FILE? JZ SENDFIL ;YES CPI 'R' ;RECEIVE A FILE? JZ RCVFIL ;YES CPI 'D' ;DISCONNECT? JZ DISCON1 ;YES, DISCONNECT & GO MENU JMP MENU ;NO OPTION SPEC'D, GO MENU ;REVISED TERMINAL ROUTINE ALLOWING MEMORY SAVE DSKSAVE LDA NFILFLG ;NEW FILE FLAG CPI TRUE ;OFFH? (TRUE=NORMAL TERMINAL MODE) JZ TERM ;YES LDA FCB+1 ;FIRST CHAR OF FILENAME CPI ' ' ;FILE SPEC'D JNZ GOODNM ;YES, GOOD NAME MVI A,TRUE ;0FFH STA NFILFLG ; OUT FRONTPAN ;0FFH PORT FOR IMSAI FRONT PANEL CMA ; 0 STA SAVEFLG ; JMP TERM ; GOODNM CALL ERASFIL CALL MOVE2 LXI D,FCB3 MVI C,MAKE CALL BDOS LXI D,FCB3 MVI C,OPEN CALL BDOS LXI H,BOTTRAM SHLD HLSAVE MVI A,FALSE STA NFILFLG TERM CALL STAT ;KEYPRESS? JZ TERML ;NO, CHECK LINE CALL KEYIN ;GET CHAR FROM KBD MOV B,A ;SAVE LDA EXACFL ORA A ;EXACT? MVI A,0 STA EXACFL ;CLR FOR NEXT TIME MOV A,B ;RESTORE JNZ NOTOG CPI EXITCHR ;^E? JZ MENU ;YES, RETURN TO MENU CPI DISCCHR ;^D? JZ DISCON1 ;YES, DISCONNECT & RETURN TO MENU CPI EXTCHR ;^^? JZ EXTFLG ;YES, SET FLAG FOR NXT CHAR CPI TRANCHR ;TEST FOR TRANSFER REQUEST (^T) CZ TRANSFER ;SEND-A-FILE (BLIND SEND) JZ TERM ;LOOP MOV B,A LDA PMMIBYTE ORA A MOV A,B JZ S2 CPI BRKCHR ;BREAK? JZ BREAK CPI CHGBAUD PUSH PSW PUSH H CZ NEWBAUD POP H POP PSW JZ TERML S2 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 TERML EXTFLG MVI A,TRUE STA EXACFL JMP TERML NOTOG CALL OUT$MODDATP TERML CALL IN$MODCTLP CALL ANI$MODRCVB CALL CPI$MODRCVR JNZ TERM CALL IN$MODDATP CPI 0 ;CHECK FOR NULLS JZ TERM ;DON'T PROCESS THEM ANI 7FH ;STRIP PARITY CALL TYPE PUSH PSW LDA SAVEFLG CPI FALSE JZ NOSAVE POP PSW MOV M,A INX H SHLD HLSAVE ;MENU COMMAND DESTROYS HL-REG.. ;..GET HL WHEN ENTERING VIA 'RET' CMD. MOV B,A LDA IMSAIBYTE ORA A MOV A,B JZ COLON CMA ;FRONT PANEL SHOWS CHARS WHEN.. OUT FRONTPAN ;..MEMORY SAVE IS ACTIVE. JMP NOCOLON COLON CPI LF ;IF NO FRONT PANEL, THEN.. JNZ NOCOLON ;..TYPE ":" AFTER EACH LINE FEED.. MVI A,':' ;..WHEN MEMORY SAVE ACTIVE. CALL TYPE NOCOLON LDA 7 ;CHECK TO SEE IF.. DCR A ;..PAGE BELOW BDOS HAS BEEN.. CMP H ;..REACHED AND DISKSAVE IS NEEDED. CZ INTDSKSV JMP TERM NOSAVE POP PSW JMP TERM SAVEFLG DB FALSE LASTBYT1 DB 0 LASTBYT2 DB 0 INTDSKSV MVI A,XOFF ;SEND A CTRL-S TO STOP.. CALL OUT$MODDATP ;..REMOTE COMPUTER OUTPUT. MVI D,0 ;D IS THE BUFFER COUNT CALL INMODEM ;GET LAST BYTES SENT.. STA LASTBYT1 ;..AFTER CTRL-S. CALL INMODEM ;ADD MORE CALLS TO INMODEM.. STA LASTBYT2 ;..AND STA LASTBYT# IF YOU ARE.. ;..LOSING BYTES WHEN MEMORY IS FULL. PUSH D CALL NUMREC1 CALL WRTDSK ;WRITE THE RECORDS POP D LXI H,BOTTRAM INR D DCR D ;TEST BUFFER COUNT FOR ZERO JZ CTRLQ LDA LASTBYT1 ;GET THE LAST BYTES THAT WERE.. MOV M,A ;..SAVED AND PUT THEM IN.. INX H ;..BOTTRAM. CALL TYPE DCR D JZ CTRLQ LDA LASTBYT2 MOV M,A INX H CALL TYPE CTRLQ MVI A,XON ;SEND START CHARACTER.. CALL OUT$MODDATP ;..TO REMOTE COMPUTER. RET BREAK: PUSH D ;SAVE IT LXI D,0 ;ZERO IT LDA MODCTLB ;GET THE LAST MODEM CONTROL BYTE ANI 0FBH ;SET THE TRANSMIT BREAK BIT LOW - ACTIVE LOW CALL OUT$MODCTL2 ;SEND IT TO THE MODEM LDA FASTCLK ;GET FAST CLOCK FLAG ORA A ;SET FLAGS LXI B,450 ;BREAK DELAY COUNTER FOR SLOW CLOCK JZ BRK1 ;JUMP IF NOT FAST CLOCK LXI B,900 ;BREAK DELAY COUNTER FOR FAST CLOCK BRK1: CALL TIMERL JZ BRK2 ;IF TIME IS UP RESET BREAK CPI 0 ;CHECK FOR NULLS JZ BRK1 ;DON'T PROCESS THEM ANI 7FH ;STRIP PARITY CALL TYPE PUSH PSW LDA SAVEFLG CPI FALSE JZ NOSAVEB POP PSW MOV M,A INX H SHLD HLSAVE ;MENU COMMAND DESTROYS HL-REG.. ;..GET HL WHEN ENTERING VIA 'RET' CMD. PUSH D MOV D,A LDA IMSAIBYTE ORA A MOV A,D POP D JZ COLONB CMA ;FRONT PANEL SHOWS CHARS WHEN.. OUT FRONTPAN ;..MEMORY SAVE IS ACTIVE. JMP BRK1 COLONB: CPI LF ;IF NO FRONT PANEL, THEN.. JNZ BRK1 ;..TYPE ":" AFTER EACH LINE FEED.. MVI A,':' ;..WHEN MEMORY SAVE ACTIVE. CALL TYPE NOSAVEB:POP PSW ;RESTORE IT JMP BRK1 BRK2: LDA MODCTLB ;GET MODEM CONTROL BYTE CALL OUT$MODCTL2 POP D LHLD HLSAVE ;LAST ADDRESS WRITTEN IF DATA BEING SAVED LDA 7 DCR A ;CHECK IF WRITING IN LAST PAGE BELOW BDOS CMP H JNZ TERM ;NO PROBLEM - GO BACK TO NORMAL ROUTINE CALL ILPRT DB CR,LF,'MEMORY SAVE BUFFER FULL',CR,LF,BELL,0 JMP TERM ;THIS SUBROUTINE WILL LOOP UNTIL THE MODEM RECEIVES A CHARACTER ;OR 100 MILLISECONDS. IF A CHARACTER IS RECEIVED, A FLAG IS SET ;TO STORE THE CHARACTER. A MAXIMUM OF TWO CHARACTERS ARE STORED, ;BUT MORE MAY BE STORED IF DESIRED (SEE COMMENT IN "INTDSKSV" ;ABOVE). INMODEM LDA FASTCLK ORA A LXI B,1250 JZ TIMERL LXI B,2500 TIMERL CALL IN$MODCTLP CALL ANI$MODRCVB CALL CPI$MODRCVR JZ GETBYTE DCX B MOV A,B ORA C JNZ TIMERL RET GETBYTE CALL IN$MODDATP INR D RET NUMRECS MVI M,EOFCHAR INX H LXI D,127 DAD D NUMREC1 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 ;RETURNS 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 XCHG PUSH D LXI D,128 DAD D POP D XCHG DCX H MOV A,H ORA L JNZ NEXTWRT RET CLOSE3 LXI D,FCB3 MVI C,CLOSE CALL BDOS RET BDOSRT PUSH B ! PUSH D ! PUSH H ! PUSH PSW CALL BDOS POP PSW ! 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 PUSH H ! 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 GETNAME LDA CMDBUF+2 ;WAS FILE ENTERED CPI 20H JZ TRANSL2 CALL MOVE4 CALL OPEN4 CPI 0FFH ;RETURN WITH 0FFH MEANS JNZ CONTIN ;FILE DOES NOT EXIST TRANSL1 CALL ILPRT DB CR,LF,'++FILE DOES NOT EXIST++',CR,LF,0 TRANSL2 CALL ILPRT DB 'TYPE "R" TO RETURN TO MODEM',CR,LF DB 'TYPE "A" TO RE-ENTER NAME: ',BELL,0 CALL KEYIN CALL UCASE CALL TYPE ;ECHO RESPONSE CALL CRLF CPI 'A' JZ GET CPI 'R' JZ RETURN JMP TRANSL2 CONTIN LXI D,80H MVI C,STDMA CALL BDOS READMR CALL READ80 CPI 1 ;END OF FILE JZ RETURNS CPI 2 ;BAD READ JZ RETURNU CALL SEND80C CPI EOFCHAR ;END OF FILE - OMIT IF OBJECT.. JZ RETURNS ;..CODE IS TO BE SENT. CPI CAN ;CANCELLATION? JZ TRANCAN JMP READMR RETURNS CALL ILPRT DB CR,LF,'++FILE TRANSFER COMPLETED++',CR,LF,BELL,0 JMP RETURN RETURNU CALL ILPRT DB CR,LF,'++FILE TRANSFER UNSUCCESSFUL++',CR,LF,BELL,0 JMP RETURN TRANCAN CALL ILPRT DB CR,LF,CR,LF,'++ TRANSFER CANCELLED ++',CR,LF,BELL,0 RETURN POP PSW ! POP B ! POP D ! POP H 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 GETNAME CALL ILPRT DB CR,LF,'ENTER FILE NAME TO BE TRANSFERRED - C/R TO QUIT: ',0 LXI D,CMDBUF CALL INBUFF CALL CRLF RET MOVE4 LXI D,CMDBUF LXI H,FCB4 CALL CPMLINE RET OPEN4 LXI D,FCB4 MVI C,OPEN CALL BDOS RET READ80 LXI D,FCB4 MVI C,READ CALL BDOS RET SEND80C MVI B,80H LXI H,80H SENDCH1 MOV A,M CALL MODOUT CPI EOFCHAR RZ CALL STAT ;TEST TO SEE IF ORA A ;CANCELLATION REQUESTED JZ SKIP12 CALL KEYIN CPI CAN RZ SKIP12 INX H DCR B JNZ SENDCH1 RET MODOUT PUSH PSW MODOUTL CALL IN$MODCTLP CALL ANI$MODSNDB CALL CPI$MODSNDR JNZ MODOUTL POP PSW CALL OUT$MODDATP CALL TYPE RET FCB4 DS 33 ;TERMINAL ECHO MODE TRMECHO CALL IN$MODCTLP CALL ANI$MODRCVB CALL CPI$MODRCVR JZ LINECHR CALL STAT JZ TRMECHO CALL KEYIN CPI EXITCHR JZ MENU MOV B,A LDA PMMIBYTE ORA A MOV A,B JZ S3 CPI CHGBAUD ;SAME ROUTINE AS IN TERMINAL MODE PUSH PSW CZ NEWBAUD POP PSW CPI CHGBAUD JZ TRMECHO S3 CALL OUT$MODDATP CALL TYPE JMP TRMECHO LINECHR CALL IN$MODDATP CALL OUT$MODDATP CALL TYPE JMP TRMECHO ;UNCOMMENTED LINES ARE THOSE OF ORIGINAL MODEM PROGRAM. ;COMMENTS DENOTE MY ADDITIONS. ; SEND A CP/M FILE SENDFIL:CALL PARITY ;SET PARITY IF REQUESTED LDA BATCHFLG ;CHECK IF MULTIPLE FILE.. ORA A ;..MODE IS SET. JNZ 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,'B' ;STOP BATCH.. STA BATCHFLG ;..MODE OPTION. MVI A,EOT ;FINAL XFER END CALL SEND JMP DONE SENDC1 LDA FCB+1 CPI ' ' JZ BLKFILE SENDC2 CALL CNREC ;GET NUMBER OF RECORDS CALL OPENFIL MVI E,80 CALL WAITNAK SENDLP CALL RDSECT 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: CALL PARITY ;SET PARITY IF REQUESTED LDA BATCHFLG ;CHECK IF MULT.. ORA A ;..FILE MODE. JNZ 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,'B' ;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 JNZ RCVFST LDA BATCHFLG ORA A ;DON'T PRINT MSSG IF.. JZ RCVFST ;..IN MULTI AND QUIET. CALL ILPRT DB 'FILE OPEN, READY TO RECEIVE',CR,LF,0 RCVFST lda crcflg ora a mvi a,nak jnz rcvfil2 mvi a,crc ; rcvfil2 CALL SEND RCVLP CALL RCVSECT JC RCVEOT CALL WRSECT CALL INCRSNO CALL SENDACK JMP RCVLP RCVEOT CALL WRBLOCK CALL SENDACK CALL CLOSFIL JMP DONE ;SUBROUTINES SENDFN LDA QFLG ORA A JZ SWNAK CALL ILPRT DB 'AWAITING NAME NAK',CR,LF,0 SWNAK MVI E,80 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 SENDNM1 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 QFLG ;SHOW NAME IF.. ORA A ;..QFLG NOT SET. MOV A,M CNZ TYPE 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 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 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 SENDNM1 NAMEOK MVI A,OKNMCH ;GOOD NAME-TELL RECEIVER CALL SEND POP H RET GETFN LXI H,FCB CALL INITFCBS+2 ;DOES NOT INITIALIZE DRIVE LDA QFLG ORA A JZ GNAMELP CALL ILPRT 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 NOMRNMG ORA A ;CLEAR CARRY RET NOMRNMG 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 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 QFLG ;TYPE IT IF NO QFLG ORA A MOV A,M CNZ TYPE 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 MOV A,C ;SEND CHECKSUM CALL SEND MVI B,1 CALL RECV ;CHECKSUM GOOD? CPI OKNMCH ;YES IF OKNMCH 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 GCKSER1 CALL ILPRT DB 'CHECKSUM ERROR',CR,LF,0 GCKSER1 CALL HSNAK ;DO HANDSHAKING OVER JC GCKSER1 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 CPI 65 ;NO MORE THAN 64 TRANSFERS RC MVI A,64 ;ONLY X'FER FIRST 64 STA FILECT RET ;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 SCANLP1 INX H DCR B JZ DNSCAN MOV A,M CPI 20H JNZ SCANLP1 SCANLP2 INX H ;EAT EXTRA SPACES DCR B JZ DNSCAN MOV A,M CPI 20H JZ SCANLP2 SHLD BGNMS ;SAVE START OF NAMES IN CMDBUF INR B DCX H SCANLP3 INX H DCR B JZ DNSCAN MOV A,M CPI 20H JNZ SCANLP3 LDA NAMECT ;COUNTS NAMES INR A STA NAMECT SCANLP4 INX H ;EAT SPACES DCR B JZ DNSCAN MOV A,M CPI 20H JZ SCANLP4 JMP SCANLP3 DNSCAN MVI M,20H ;SPACE AFTER LAST CHAR POP H 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,23 ;RENAME 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 ;MULTI-FILE ACCESS SUBROUTINE FROM CP/M USER'S GROUP ;FIXED BY MARK ZEIGER 8/17/80 ;CARRY IS SET IF NO MORE NAMES CAN BE FOUND MFNAME MFACCESS ;A MACRO IN MACROS.LIB RCVSECT XRA A STA ERRCT RCVRPT: XRA A ;ZERO ACCUM STA ERRCDE ;CLEAR RECEIVE ERROR CODE LDA QFLG ORA A JZ RCVSQ CALL ILPRT DB CR,'AWAITING # ',0 PUSH H ;SAVE IT LHLD SECTNO ;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 MOV A,L ;ONLY THE LOW BYTE IS USED BY THE PROGRAM POP H ;RESTORE IT RCVSQ MVI B,7 ;10 IN ORIG PROG CALL RECV JC RCVSTOT CALL RCVERR ;CHECK FOR ERRORS JC RCVDERR ;JUMP IF THERE WAS AN ERROR CPI SOH JZ RCVSOH ORA A JZ RCVSQ CPI EOT STC RZ MOV B,A LDA VSEEFLG ORA A JZ RCVSEH LDA QFLG ORA A JZ RCVSERR RCVSEH MOV A,B CALL HEXO CALL ILPRT DB CR,LF,'H RCD, 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 VSEEFLG ORA A JZ RCVCKQ LDA QFLG ORA A JZ RCVSABT RCVCKQ CALL CKQUIT JZ RCVSECT RCVSABT CALL CLOSFIL CALL ERXIT DB CR,LF,'++ UNABLE TO RECEIVE BLOCK -- ABORTING ++',CR,LF,'$' RCVSTOT LDA VSEEFLG ORA A JZ RCVSPT LDA QFLG ORA A JZ RCVSERR RCVSPT CALL ILPRT DB CR,LF,'++ TIMEOUT ++ ',0 RCVPRN LDA ERRCT CALL HEXO CALL CRLF JMP RCVSERR ;----> RCVERR: Checks for framing, overrun, and parity errors. Parity errors ; cannot be detected unless the parity option has been selected. ; 1. Error code (ERRCDE) was set in RECV routine. ; 2. ERRCDE=0 for no errors, ERRCDE<>0 for errors. ; 3. If there is an error this routine returns with carry flag set. RCVERR: PUSH PSW ;SAVE CHAR TRANSMITTED LDA ERRCDE ;GET RECEIVE ERROR CODE ANA A ;IS IT ZERO? JZ RCVERR2 ;YES, NO RECEIVE ERROR POP PSW ;RESTORE CHAR TRANSMITTED STC ;SET CARRY ON TO INDICATE AN ERROR RET RCVERR2:POP PSW ;RESTORE CHAR TRANSMITTED RET ;----> RCVDERR: Checks for a receive error and displays appropriate error ; message. Then goes to RCVSERR to purge the line and send a NAK. RCVDERR:LDA VSEEFLG ;VIEWING ORA A ;...MODE? JZ RCVDERRP ;YES,..PRT MSG LDA QFLG ;QUIET... ORA A ;...MODE? JZ RCVSERR ;YES, NO MSG RCVDERRP: CALL ILPRT DB CR,LF,0 LDA ERRCDE ;GET RECEIVE ERR CODE ANI FRMER ;WAS THERE A FRAMING ERROR? JZ RCVDERR2 ;NO, GO CHECK FOR OVERRUN CALL ILPRT DB '++FRAMING ERR++ ',0 CALL RCVDERR5 ;PRINT # OF ERROR RCVDERR2:LDA ERRCDE ;GET RECEIVE ERR CODE ANI ORUNER ;WAS THERE AN OVERRUN JZ RCVDERR3 ;NO, GO CHECK FOR PARITY ERROR CALL ILPRT DB '++OVERRUN ERR++ ',0 CALL RCVDERR5 RCVDERR3:LDA ERRCDE ;GET RECEIVE ERR CODE ANI PARER ;WAS THERE A PARITY ERROR? JZ RCVDERR4 ;NO, GO PURGE LINE CALL ILPRT DB '++PARITY ERR++ ',0 CALL RCVDERR5 RCVDERR4:JMP RCVSERR ;GO PURGE LINE, SEND NAK ;Display the number of the error, do a carriage return and line feed. RCVDERR5:LDA ERRCT ;GET ERROR NUMBER CALL HEXO ;DISPLAY IT CALL CRLF ;DO CR, LF RET RCVSOH MVI B,1 CALL RECV JC RCVSTOT CALL RCVERR ;CHECK FOR RECEIVE ERROR JC RCVDERR MOV D,A MVI B,1 CALL RECV JC RCVSTOT CALL RCVERR ;CHECK FOR RECEIVE ERROR JC RCVDERR CMA CMP D JZ RCVDATA LDA VSEEFLG ORA A JZ RCVBSE LDA QFLG ORA A JZ RCVSERR RCVBSE CALL ILPRT 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 call clrcrc ;clear crc counter LXI H,80H RCVCHR MVI B,1 CALL RECV JC RCVSTOT CALL RCVERR ;CHECK FOR RECEIVE ERROR JC RCVDERR MOV M,A INR L JNZ RCVCHR lda crcflg ora a jz rcvcrc MOV D,C XRA A STA DATAFLG MVI B,1 CALL RECV JC RCVSTOT CALL RCVERR ;CHECK FOR RECEIVE ERROR JC RCVDERR CMP D JNZ RCVCERR ; chksnum LDA RCVSNO MOV B,A LDA SECTNO CMP B JZ RECVACK INR A CMP B JNZ ABORT RET ; rcvcrc mvi e,2 ;nr of crc bytes ; rcvcrc2 mvi b,1 call recv jc rcvstot call rcverr jc rcvderr dcr e jnz rcvcrc2 call chkcrc ora a jz chksnum lda vseeflg ora a jz rcvcrer lda qflg ora a jz rcvserr ; rcvcrer call ilprt db cr,lf,'++CRC err++',0 jmp rcvprn ; RCVCERR LDA VSEEFLG ORA A JZ RCVCPR LDA QFLG ORA A JZ RCVSERR RCVCPR CALL ILPRT DB '++ CKSUM ++ ',0 JMP RCVPRN RECVACK CALL SENDACK JMP RCVSECT SENDACK MVI A,ACK CALL SEND RET SENDHDR LDA QFLG ORA A JZ SENDHNM CALL ILPRT DB CR,'SEND # ',0 PUSH H LHLD SECTNO ;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 SECTNO CALL SEND LDA SECTNO 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 call fincrc mov a,d call send mov a,e call send 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 HEXO CALL ILPRT DB CR,LF,'H RCD, NOT ACK',CR,LF,0 ACKERR LDA ERRCT INR A STA ERRCT CPI ERRLIM RC LDA VSEEFLG ORA A JZ GACKV LDA QFLG ORA A JZ CSABORT GACKV CALL CKQUIT STC RZ CSABORT CALL ERXIT DB CR,LF,'CAN''T SEND SECTOR -- ABORTING',CR,LF,'$' GETATOT LDA QFLG ORA A JZ ACKERR CALL ILPRT DB CR,LF,'TIMEOUT ON ACK',CR,LF,0 JMP ACKERR CKABORT: CKABGO: CALL STAT RZ CALL KEYIN CPI CAN RNZ ABORT LXI SP,STACK ABORTL MVI B,1 CALL RECV JNC ABORTL MVI A,CAN CALL SEND ABORTW MVI B,1 CALL RECV JNC ABORTW MVI A,' ' CALL SEND CALL ILPRT DB CR,LF,'ROUTINE CANCELLED',CR,LF,BELL,0 MVI A,'B' ;TURN MULTI-FILE MODE.. STA BATCHFLG ;..OFF SO ROUTINE ENDS. JMP DONETCE INCRSNO PUSH H LHLD SECTNO ;GET SECTOR NUMBER INX H ;BUMP IT SHLD SECTNO ;STORE IT MOV A,L POP H RET ERASFIL LDA BATCHFLG ;DON'T ASK FOR ERASE.. ORA A ;..IN MULTI-FILE MODE,.. JZ NOASK ;..JUST DO IT. LXI D,FCB MVI C,SRCHF CALL BDOS INR A RZ CALL ILPRT DB 'FILES EXISTS -- TYPE ''Y'' TO ERASE: ',BELL,0 CALL KEYIN PUSH PSW CALL TYPE POP PSW CALL UCASE CPI 'Y' JNZ MENU CALL CRLF NOASK LXI D,FCB MVI C,ERASE CALL BDOS RET BLKFILE CALL ILPRT ;ROUTINE IF NO FILE IS NAMED FOR "SEND" OR "RECEIVE" DB CR,LF,'No file specified',CR,LF,BELL,0 JMP MENU MAKEFIL LXI D,FCB MVI C,MAKE CALL BDOS INR A RNZ CALL ERXIT DB 'ERROR - CAN''T MAKE FILE',CR,LF DB 'DIRECTORY MUST BE FULL',CR,LF,'$' IF CPM2X CNREC: 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 ENDIF IF NOT CPM2X CNREC: 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 DB '++FILE NOT FOUND++$' ;READ MORE DIRECTORY ENTRIES 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 ;AND EXIT ;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 JMP MOREDIR ;GO FIND MORE EXTENTS ENDIF OPENFIL LXI D,FCB MVI C,OPEN CALL BDOS INR A JNZ OPENOK CALL ERXIT DB 'CAN''T OPEN FILE$' OPENOK LDA BATCHFLG ORA A JNZ OPENOK1 LDA QFLG ORA A RZ OPENOK1 CALL ILPRT 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 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 JZ RDSECOK DCR A JZ REOF CALL ERXIT DB '++ FILE READ ERROR ++$' RDSECOK 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 RET WRERR MVI C,CAN CALL SEND CALL ERXIT DB 'ERROR WRITING FILE',CR,LF,'$' ;----> 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 EQU $ CALL IN$MODDATP CALL IN$MODDATP 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 IN$MODCTLP CALL ANI$MODRCVB CALL CPI$MODRCVR JZ MCHAR DCR E JNZ MWTI DCR D JNZ MWTI DCR B JNZ MSEC POP D STC RET MCHAR: LDA PMMIBYTE ;IS THE MODEM A PMMI? ORA A ;SET FLAGS JZ MCHAR1 ;YES, JUMP CALL IN$MODCTLP ;GET ERROR-STATUS BYTE ANI ERRCDMSK ;MASK OUT ALL EXCEPT ERROR BITS (3-5) STA ERRCDE ;SAVE THE ERROR CODE MCHAR1: CALL IN$MODDATP 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 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 SENDW CALL IN$MODCTLP CALL ANI$MODSNDB CALL CPI$MODSNDR JNZ SENDW POP PSW CALL OUT$MODDATP RET WAITNAK LDA VSEEFLG ORA A JZ WAITNPR LDA QFLG ORA A JZ WAITNLP WAITNPR CALL ILPRT 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 call ilprt db 'CRC request received',cr,lf,0 xra a sta crcflg ret ; ;----> PARITY: Routine to setup the PMMI for odd or even parity. PARITY: LDA PMMIBYTE ;IS MODEM A PMMI? ORA A ;SET FLAGS RZ ;NO, RETURN LDA OPARITY ;GET ODD PARITY REQUEST BYTE ORA A ;SET FLAGS JNZ EVENPAR ;IF NOT ODD SEE IF IT IS EVEN LDA UARTCTLB ;GET UART/MODEM CONTROL BYTE ANI ODPARMSK JMP PARITY1 EVENPAR:LDA EPARITY ;GET EVEN PARITY REQUEST BYTE ORA A ;SET FLAGS RNZ ;IF EVEN PARITY NOT SPECIFIED RETURN LDA UARTCTLB ;GET UART/MODEM CONTROL BYTE ANI ODPARMSK ;SET FOR PARITY ORI EVPARMSK ;NOW SET FOR EVEN PARITY PARITY1:JMP OUT$MODCTLP ;SEND TO PMMI - WHEN OUT$MODCTLP DOES RET IT ;WILL GO BACK TO CALLING ROUTINE NOPARIT:LDA UARTCTLB ;GET UART/MODEM CONTROL BYTE ORI NOPARMSK ;RESET PARITY BIT ON PMMI JMP OUT$MODCTLP INITADR LHLD 1 LXI D,3 DAD D SHLD VSTAT+1 DAD D SHLD VKEYIN+1 DAD D SHLD VTYPE+1 LDA PMMIBYTE ORA A JZ JMP$INITMOD ;RETURN DONE FROM THIS ROUTINE.. LDA IN$MODCTLP+1 ;..IF NOT PMMI STA OUT$MODCTLP+1 INR A STA OUT$MODDATP+1 STA IN$MODDATP+1 INR A STA IN$BAUDRP+1 STA OUT$BAUDRP+1 INR A STA OUT$MODCTL2+1 RET PROCOPT LXI D,FCB+1 LDAX D STA OPTION OPTLP INX D LDAX D CPI ' ' JZ ENDOPT LXI H,OPTBL MVI B,OPTBE-OPTBL OPTCK CMP M JNZ OPTNO MVI M,0 JMP OPTLP OPTNO INX H DCR B JNZ OPTCK JMP BADOPT ENDOPT lda crcflg ora a jnz endopt2 lda option cpi 'R' jnz badopt ;crc only allowed for recv ; endopt2 LDA VSEEFLG ORA A RNZ STA QFLG RET DONE LDA BATCHFLG ORA A JNZ DONETCC 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 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 SECTORE NUMBERS.. LXI D,SECTNOB ;..FOR NEW FILE TRANSFER. MVI B,SECTNOE-SECTNOB ;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 RCVFIL ;..THE FLAG IN MULTI-FILE MODE. DONETCC MVI A,TRUE ;INDICATE NO FILES BEING.. STA FSTFLG ;RESET MULTIFILE TRANS STA NFILFLG ;..USED IN TERMINAL ROUTINE. CMA OUT FRONTPAN STA SAVEFLG ;STOP MEMORY SAVE IN TERM ROUTINE. LDA VSEEFLG ORA A JZ DONETC LDA QFLG ORA A JZ donetca DONETC CALL ILPRT DB CR,LF,'ALL TRANSFERS COMPLETED' DB CR,LF,BELL,0 donetca lda discflg ;see if disconnect when thru ora a jnz donetce ;no, don't disconnect donetcb call ilprt db cr,lf,'++PRESS RETURN TO DISCONNECT++',bell,cr,lf,0 mvi c,rdcon call bdos ;wait for response cpi 0dh ;carriage return jnz donetcb ;nope call ilprt db cr,lf,'++DISCONNECTED++',cr,lf,0 call disconnt ;hang-up the pmmi jmp exit ;go to CP/M DONETCE:CALL NOPARIT ;RESET TO NO PARITY LDA TERMFLG ;SEE IF RETURN TO.. ORA A ;..TERMINAL MODE.. JNZ MENU ;..AFTER X'FER. CALL CRLF JMP TERM INITMOD SETBAUD LDA PMMIBYTE ORA A RZ LDA ANSWFLG ;IF ANSWER OR ORIGINATE MODE.. ORA A ;..IS NOT REQUESTED OR NO.. JNZ SKIPB1 ;..BAUDRATE SPECIFIED, THEN.. CALL GETBAUD ;..ROUTINE RETURNS WITH CHANGE.. JMP FIXBAUD ;..OF BAUD. IF OPT REQUESTED,.. SKIPB1 LDA ORIGFLG ;..A BLANK FORCES 300 BAUD.. ORA A ;..ELSE A 0 FROM NEWBAUD.. JNZ SKIPB2 ;..FORCES 300 BAUD. CALL GETBAUD JMP FIXBAUD SKIPB2 LDA FCB+9 CPI 0 ;IF ZERO, NEWBAUD WANTS 300 JZ SKIPB3 CPI ' ' RZ JMP SKIPB4 SKIPB3 MVI A,' ' ;FORCE 300 BAUD STA FCB + 9 SKIPB4 CALL GETBAUD FIXBAUD CALL OUT$BAUDRP CPI 52 MVI A,5FH JC GT300 MVI A,7FH GT300 CALL OUT$MODCTL2 STA MODCTLB ;SAVE MODEM CONTROL BYTE LDA ORIGFLG ORA A MVI A,ORIGMOD JZ OFFHOOK LDA ANSWFLG ORA A MVI A,ANSWMOD RNZ OFFHOOK LXI H,4000 OFFDLY DCR L JNZ OFFDLY DCR H JNZ OFFDLY CALL OUT$MODCTLP RET GETBAUD LDA FCB+9 CPI ' ' MVI A,52 RZ LDA FCB+9 CPI 0 MVI A,52 RZ LXI D,FCB+9 LXI H,0 DECLP LDAX D INX D CPI ' ' JZ DECLP CPI '0' JC BADRATE CPI '9'+1 JNC BADRATE SUI '0' MOV B,H MOV C,L DAD H DAD H DAD B DAD H ADD L MOV L,A JNZ DIGNC INR H DIGNC MOV A,E CPI FCB+12 JNZ DECLP MOV A,H CMA MOV D,A MOV A,L CMA MOV E,A INX D LXI H,15625 LXI B,-1 DIVLP INX B DAD D JC DIVLP MOV A,B ORA A MOV A,C RZ BADRATE CALL ERXIT DB '++ INVALID BAUD RATE ++$' MOVEFCB LXI H,FCB+16 LXI D,FCB MVI B,16 CALL MOVE XRA A STA FCBSNO STA FCBEXT RET SHOW CPI LF JZ CTYPE CPI CR JZ CTYPE CPI 9 JZ CTYPE CPI ' ' JC SHOWHEX CPI 7FH JC CTYPE SHOWHEX PUSH PSW MVI A,'(' CALL CTYPE POP PSW CALL HEXO MVI A,')' JMP CTYPE CTYPE PUSH B PUSH D PUSH H MOV E,A MVI C,WRCON CALL BDOS POP H POP D POP B RET CRLF PUSH PSW MVI A,CR CALL TYPE MVI A,LF CALL TYPE POP PSW RET TYPE PUSH PSW PUSH B PUSH D PUSH H MOV C,A VTYPE CALL $-$ POP H POP D POP B POP PSW RET STAT PUSH B PUSH D PUSH H VSTAT CALL $-$ 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 ;----> DHXOUT: - double precision hex output routine. DHXOUT: PUSH H PUSH PSW MOV A,H ;GET MS BYTE CALL HEXO ;OUTPUT HIGH ORDER BYTE 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 TYPE ;RETURNS W/ ZERO SET IF RETRY ASKED. IF MULTI-FILE MODE, THEN ;NO QUESTIONS ASKED, JUST QUIT CKQUIT LDA BATCHFLG ORA A JNZ 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 CRLF POP PSW CALL UCASE ;INSTEAD OF "ANI 5FH" CPI 'R' RZ CPI 'Q' JNZ CKQUIT ORA A RET ILPRT XTHL ILPLP MOV A,M ORA A JZ ILPRET CALL CTYPE INX H JMP ILPLP ILPRET XTHL RET PRTMSG MVI C,PRINT JMP BDOS ERXIT POP D CALL PRTMSG CALL ILPRT DB BELL,0 LDA BATCHFLG ORA A JNZ DONETCE MVI A,'Q' ;RESET QFLG STA QFLG JMP ABORT ;ABORT OTHER COMPUTER EXIT LXI D,80H MVI C,STDMA CALL BDOS JMP 0 MOVE128 MVI B,128 MOVE MOV A,M STAX D INX H INX D DCR B JNZ MOVE RET ;DIALING ROUTINES TAKEN (AND GREATLY MODIFIED) FROM PMMI MANUAL. ;MODEM CONTROL COMMAND WORDS CLEAR EQU 3FH ;IDLE MODE MAKEM EQU 1 ;TELE LINE MAKE (OFF HOOK) BRKM EQU 0 ;TELE LINE ON HOOK (BREAK DURING DIALING) DTMSK EQU 1 ;DIAL TONE MASK RBLMT EQU 70 ;# OF SEC*10 TO WAIT BEFORE GIVING NO RING HEARG MSG RBWAIT EQU 50 ;# OF SEC*10 DELAY BEFORE REDIALING NUMBER TMPUL EQU 80H ;TIMER PULSES MASK BIT TRATE EQU 250 ;VALUE FOR 0.1 SECOND DIALPL LDA PMMIBYTE ;FLAG FOR PMMI OPERATION ORA A ;SET FLAGS RZ ;PMMI FALSE, RETURN XRA A ; 0 STA CRFLAG ;CONTINUOUS REDIAL FLAG CALL DIALPL0 ; DISCONNECT, RECONNECT, WAIT DIAL TONE JC DILAGN ;ASK IF TRY AGAIN LXI H,CMDBUF+1 ;POINT # OF CHARS IN BUFF MOV A,M ;GET # OF CHARS CPI 4 ;4 OR MORE CHARS TYPED BEFORE ? JC ENTNUM ;NO, ASK FOR NUMBER LXI H,CMDBUF+5 ;POINT TO NUMBER TO DIAL JMP DIAL10 ;CHECK IF LIB #, & DIAL DIALPL0 CALL DISCONNT CALL ILPRT DB CR,LF,'WAITING FOR DIAL TONE',CR,LF,0 MVI A,MAKEM ;MAKE MAKE (OFF-HOOK) CALL OUT$MODCTLP;DO IT MVI D,DTMSK ;DIAL TONE MASK MVI C,100 ;10 SECOND WAIT CALL WAIT ;WAIT FOR DIAL TONE NOP ;DELAY ; WAIT SUBROUTINE WILL RETURN WITH CARRY SET IF UNABLE TO ; GET DIALTONE, ELSE CARRY NOT SET MEANS DIALTONE RECEIVED RNC ;IF DIAL TONE WITHIN 10 SECONDS CALL ILPRT ;ELSE, MESSAGE AND RETURN WITH CARRY SET DB CR,LF DB '++NO DIAL TONE AFTER 10 SECONDS++',CR,LF,0 STC RET ENTNUM: ;this is all the set-up for the print at entnum2. mvi c,13 ;number of lines to move lxi h,numblib ;address of source memory lxi d,dbuf ;address of target memory call newline ;start with CRLF stax d ;+LF inx d ;and bump it entnum1: mvi b,30 ;number of bytes to move call move ;move to buffer call spaces ;2 entries + 3 spaces = 63 characters mvi b,30 call move call newline dcr c ;number of lines to print jz entnum2 jmp entnum1 newline: ;puts CR-LF at memory pointed by DE mvi a,cr ;CR stax d ;store it mvi a,lf ;LF inx d ;bump pointer stax d ;store LF inx d ;bump pointer ret spaces: mvi a,20H ;space stax d ! inx d ; 1 stax d ! inx d ; 2 stax d ! inx d ; 3 ret entnum2: mvi a,'$' stax d mvi c,print lxi d,dbuf ;point to table of numbers to print call bdos call crlf CALL ILPRT DB 'ENTER NUMBER OR LIBRARY LETTER - TYPE C/R WHEN FINISHED,',CR,LF DB 'CTRL-X CANCELS WHILE DIALING: ',0 LXI D,CMDBUF CALL INBUFF DIALLP1 LDA CMDBUF+1 ORA A ;NULL MEANS WAS TYPED JZ BORTIT ;ABORT DIALING, RETURN TO MENU LXI H,CMDBUF+2 ;FIRST TYPED CHAR OF NUMBER TO DIAL ; ; ENTER THIS ROUTINE WITH HL POINTING TO NUMBER TO DIAL ; DIAL10: MVI B,'A' ;FIRST LETTER OF ALPHABET MVI E,0 ;COUNTS NUMBER OF LETTERS TO MATCH MVI C,26 ;NUMBER OF LETTERS IN ALPHABET MOV A,M ;GET CHAR BUFFER DIAL11: CMP B ;NUMBER FROM TABLE? JZ LIBSET INR B ;MAKE NEXT LETTER (A-Z) INR E ;COUNT UP DCR C ;COUNT DOWN JZ DIALLPX ;NOT A LETTER JMP DIAL11 ;LOOP LIBSET: LXI H,NUMBLIB ;PHONE NUMBER LIBRARY LXI B,30 ;LENGTH OF LIBRARY ENTRY MOV A,E ;NUMBER OF TIMES TO ADD 30 TO HL ORA A ;SET FLAGS JZ DIAL13 DIAL12: MOV A,M ;GET FIRST CHAR OF SELECTED LIB ENTRY ORA A ;SET FLAGS JZ DIALLP2 ;SEND BADLIB MSG DAD B ;INCREMENT HL BY 30 DCR E ;COUNTDOWN JNZ DIAL12 ;NOT THERE YET, LOOP DIAL13: MVI B,30 ;NUMBER OF CHARACTERS TO GET FROM TABLE LXI D,CMDBUF+1 ;POINT TO BUFFER XCHG ;HL POINTS TO CMDBUF+1 MOV M,B ;STORE # OF BYTES IN A TABLE ENTRY XCHG ;RESTORE REG. INX D ;POINT TO FIRST CHAR POSITION IN BUFFER CALL MOVE ;MOVE TABLE ENTRY TO BUFFER DIALLPX LDA CMDBUF+1 MOV E,A ;NUMBER OF CHARS IN BUFF LXI H,CMDBUF+2 ;POINT FIRST CHAR DIALLP2 MOV A,M ;GET FIRST # FROM BUFFER ; ; ROUTINE TO PRINT 'BADLIB' MESSAGE AND ABORT IF NULL ENCOUNTERED ; ORA A ;SET FLAGS PUSH D ;SAVE DE REGISTERS LXI D,BADLIB ;BAD LIBRARY NUMBER IF NULL MVI C,PRINT ; 9 PUSH PSW ;SAVE A AND FLAGS CZ BDOS POP PSW ;RESTORE A AND FLAGS POP D ;RESTORE DE REGISTERS JZ BORTIT ;ABORT ; ; DIAL A DIGIT, CHECK KBD FOR ABORT ; CALL DIAL ;DIAL IT CALL STAT ; KEYPRESS? ORA A ;SET FLAGS CNZ KEYIN ;YES, GO GET IT CPI CAN ; ^X? JZ BORTIT ;YES, ABORT INX H ;BUMP POINTER PUSH D ;SAVE DE PUSH H ;SAVE HL MVI B,1 ;WAIT 1 TIME INTERVAL CALL TIMER POP H ;RESTORE HL POP D ;RESTORE DE DCR E ;COUNT DOWN CHARS IN BUFF JNZ DIALLP2 ;NOT DONE, LOOP JZ DIALDN ;DIALING DONE DISCONNT XRA A ;0 CALL OUT$MODCTL2 ;CLEAR DAV, ESD, ETC CALL OUT$MODCTLP ;HANG-UP PUSH B MVI B,8 ;wait for PMMI to disconnect CALL TIMER POP B RET TIMER MVI A,TRATE ;TRATE 250, VALUE FOR .1 SEC INTERVAL CALL OUT$BAUDRP ;B-REG CONTAINS NUMBER OF .1 SEC INTERVALS TIMES CALL IN$BAUDRP ;TO COUNT ANI TMPUL JZ TIMES ;WAIT FOR TIMER TO GO HIGH TIMEE CALL IN$BAUDRP ANI TMPUL JNZ TIMEE ;WAIT FOR TIMER TO GO LOW DCR B JNZ TIMES RET BORTIT CALL DISCONNT JMP MENU ;AUTO DIALER DIAL CALL TYPE ;PRINT WHATEVER CHARACTER, DASHES, ETC. CPI 30H RC ;DIGIT MUST BE AT LEAST 0.. CPI 'R' ;COULD IT BE A RINGBACK CHARACTER JNZ DIAL1 ;NO? - JUMP PUSH PSW ;SAVE ACCUMULATOR & FLAGS MOV A,E ;GET # OF CHAR LEFT INTO ACC. CPI 01H ;IS THIS THE LAST CHARACTER? JZ RINGBK ;IF SO, IT MUST BE RINGBACK CHAR - DO RINGBACK POP PSW ;EVERYTHING BACK AS IT WAS DIAL1: CPI 3AH RNC ;..AND NOT MORE THAN 9 ANI 0FH ;STRIP ASCII -- COULD ALSO DO SUI 30H ('0') JNZ DIALS MVI A,10 ;CONVERT ZERO TO 10 PULSES DIALS MOV C,A LDA PULSERATE ;CONTAINS VALUE FOR DIAL SPEED CALL OUT$BAUDRP DIALC CALL IN$BAUDRP ANI TMPUL JNZ DIALC DIALB CALL IN$BAUDRP ANI TMPUL JZ DIALB MAKEP MVI A,MAKEM CALL OUT$MODCTLP TIMEM CALL IN$BAUDRP ANI TMPUL JNZ TIMEM MVI A,BRKM CALL OUT$MODCTLP TIMEB CALL IN$BAUDRP ANI TMPUL JZ TIMEB DCR C JNZ MAKEP MVI A,MAKEM CALL OUT$MODCTLP MVI B,2 CALL TIMER RET RINGBK: POP PSW ;TO GET IT OFF THE STACK LDA CMDBUF+1 ;GET # OF CHAR IN BUFFER SUI 01 ;SUBTRACT 1 TO AVOID THE RINGBACK CHAR STA CMDBUF+1 ;STORE THE NEW VALUE MVI D,DTMSK ;LOAD TONE DETECT MASK MVI C,RBLMT ;SET TIMER FOR RBLMT NUMBER OF SECONDS CALL WAIT JC RBTIME ;JUMP IF NO RING DETECTED MVI B,25 ;WAIT 2.5 SEC CALL TIMER CALL IN$BAUDRP ;IS TONE STILL PRESENT? ANA D JNZ RNGBK1 JMP DILAGN ;YES, MUST BE BUSY RNGBK1: CALL HANGP ;HANG UP THE PHONE MVI B,RBWAIT ;WAIT X SEC CALL TIMER CALL DIALPL0 ;GO OFF HOOK & LISTEN FOR DIAL TONE JNC DIALLPX ;GO REDIAL NUMBER JMP DILAGN ;NO DIAL TONE HEARD RBTIME: CALL CRLF JMP RNGBK1 ;HANGUP, REDIAL, & LISTEN FOR CARRIER ;TIME OUT ROUTINE. MUST BE CALLED WITH MASK IN D REG FOR INPUT ;AT RELATIVE PORT 2 AND NUMBER OF SECONDS * 10 IN C REG. WAIT MVI B,1 CALL TIMER ;WAIT FOR TIMER TO GO HIGH THEN LOW CALL IN$BAUDRP ;PMMIADDR+2 (MODEM STATUS PORT) ANA D ;(CTS or DIALTONE MASK) RZ ;ACTIVE LOW, SO RETURN ON 0 PUSH B ;SAVE.. PUSH D ;..ACTIVE REG'S CALL STAT ;KEYPRESS? ORA A ;SET FLAGS CNZ KEYIN ;YES, GET CHAR CPI CAN ;^X? JZ WAIT1 ;YES, DISCONNECT, JMP TO MENU POP D ;RESTORE.. POP B ;..REGS DCR C ;COUNT-DOWN JNZ WAIT STC ;SET CARRY TO INDICATE MASK NOT SET RET WAIT1: POP D ;RESET.. POP B ;..STACK JMP DISCON1 ;DISCONNECT HANGP MVI A,CLEAR CALL OUT$MODCTL2 MVI A,0 CALL OUT$MODCTLP RET DIALDN CALL CRLF MVI A,07FH ;TURN ON DTR CALL OUT$MODCTL2 ;TIMER RATE? MVI B,1 CALL TIMER ;WAIT FOR MODEM TO TURN ON DTR MVI A,5DH ;2 STOP BITS, NO PARITY, 8 DATA BITS ;+ NO DISCONNECT AFTER 17 SECS CALL OUT$MODCTLP MVI D,4 ;CLEAR TO SEND MASK MVI C,waitcts ;wait time for cts (25.5 SEC MAX) CALL WAIT JNC CONMADE ;CONNECTION MADE CALL DISCONNT DILAGN: LDA CRFLAG ;CONTINUOUS REDIAL FLAG ORA A JNZ DILAGN0 CALL ILPRT DB CR,LF,'No answer after time-out. Redial? (Y/N/C): ',BELL,0 CALL KEYIN ;GET RESPONSE CALL TYPE ;ECHO IT CALL UCASE ;ANI 5FH CALL CRLF ;NEW LINE CPI 'N' ;REDIAL? JZ MENU ;NO, GO MENU CPI 'Y' ;REDIAL? JZ DILAGN0 ;YES, REDIAL CPI 'C' ;CONTINUOUS REDIAL? JNZ DILAGN ;INVALID RESPONSE, ASK AGAIN XRA A ! CMA ;0FFH STA CRFLAG ;CONTINUOUS REDIAL FLAG DILAGN0: mvi b,50 ;5 seconds wait for pmmi reset call timer ;else busy tone may be sensed as dialtone CALL DIALPL0 ;WAIT FOR DIAL TONE JNC DIALLP1 ;DIAL NUMBER JMP DILAGN ;NO DIAL TONE AFTER 10 SECS CONMADE CALL ILPRT DB CR,LF,'Connection established - Select options: ',BELL,0 DILAGN1 CALL STAT ;KEYPRESS? ORA A ;SET FLAGS JNZ GETCMD ;KEY PRESSED, GO GET OPTIONS MVI A,BELL CALL TYPE ;RING BELL JMP DILAGN1 ;LOOP ;INITIALIZES CP/M FILE CONTROL BLOCKS AT 5CH AND 6CH SETFCB LXI D,CMDBUF LXI H,FCB CALL CPMLINE CALL PROCOPT CHECKNM LDA FCB+1 ;CHECK ON THE PRIMARY OPTION CPI 'E' ;RETURN IF ECHO OPTION RZ CPI 'M' ;RETURN TO MENU RZ MOV B,A LDA PMMIBYTE ORA A MOV A,B JZ S4 CPI 'C' RZ S4 CPI 'T' JZ TERMSEL CPI 'S' JZ CKFILE CPI 'R' JNZ BDOPT LDA BATCHFLG ;IF MULT FILE MODE, THEN.. ORA A ;..RECV OPT DOES NOT NEED.. RZ ;..NAME. JMP CKFILE BDOPT CALL ILPRT DB CR,LF,'++Bad Option++',CR,LF,0 JMP REENT CKFILE LDA FCB+17 ;IF OPTION THAT NEEDS FILE NAME,.. CPI ' ' ;..THEN CHECK TO SEE IF NAME.. RNZ ;..EXISTS. IF NOT.. REENT CALL ILPRT ;..DO EVERYTHING OVER. DB CR,LF,'Re-enter PRIMARY option and file name only: ',BELL,0 LXI D,CMDBUF CALL INBUFF JMP SETFCB TERMSEL LDA FCB+17 CPI ' ' JNZ SAVAGN MVI A,FALSE STA SAVEFLG MVI A,TRUE STA NFILFLG CMA OUT FRONTPAN RET SAVAGN MVI A,FALSE STA NFILFLG RET NEWBAUD LDA PMMIBYTE ORA A RZ CALL ILPRT DB 'Enter New Baudrate: ',0 LXI H,FCB+9 MVI M,0 ;PUTS A ZERO IN FIRST POSITION SO AS TO LOOP5 CALL KEYIN ;FORCE THE DEFAULT OPTION OF 300 BAUD. CPI CR ;CARRIAGE RET ENTERS BAUD RATE JNZ CONNEWB ;GOES TO THE ESTABLISHED ROUTINE - RETURN TO MAIN CALL CRLF ;PROGRAM IS DONE THERE. JMP SETBAUD CONNEWB CPI 30H ;MAKE SURE IT'S.. JC LOOP5 ;..A DIGIT, ELSE.. CPI 3AH ;..DON'T ACCEPT IT. JNC LOOP5 MOV M,A MOV C,A CALL TYPE ;ECHO THE CHARACTER ENTERED INX H JMP LOOP5 ; ;************************************************************************ ;* CRCSUBS (Cyclic Redundancy Code Subroutines) version 1.20 * ;* 8080 Mnemonics * ;* * ;* These subroutines will compute and check a true 16-bit * ;* Cyclic Redundancy Code for a message of arbitrary length. * ;* * ;* The use of this scheme will guarantee detection of all * ;* single and double bit errors, all errors with an odd * ;* number of error bits, all burst errors of length 16 or * ;* less, 99.9969% of all 17-bit error bursts, and 99.9984% * ;* of all possible longer error bursts. (Ref: Computer * ;* Networks, Andrew S. Tanenbaum, Prentiss-Hall, 1981) * ;* * ;* Designed & coded by Paul Hansknecht, June 13, 1981 * ;* * ;* Copyright (c) 1981, Carpenter Associates * ;* Box 451 * ;* Bloomfield Hills, MI 48013 * ;* 313/855-3074 * ;* * ;* This program may be freely reproduced for non-profit use. * ;* * ;************************************************************************ ; ; ENTRY CLRCRC,UPDCRC,FINCRC,CHKCRC ; CLRCRC: EQU $ ; Reset CRC Accumulator for a new message. PUSH H LXI H,0 SHLD CRCVAL POP H RET ; UPDCRC: EQU $ ; Update CRC Accumulator using byte in (A). PUSH PSW PUSH B PUSH H MVI B,8 MOV C,A LHLD CRCVAL ; UPDLOOP:MOV A,C RLC MOV C,A MOV A,L RAL MOV L,A MOV A,H RAL MOV H,A JNC SKIPIT MOV A,H ; The generator is X^16 + X^12 + X^5 + 1 XRI 10H ; as recommended by CCITT. MOV H,A ; An alternate generator which is often MOV A,L ; used in synchronous transmission protocols XRI 21H ; is X^16 + X^15 + X^2 + 1. This may be MOV L,A ; used by substituting XOR 80H for XOR 10H SKIPIT: DCR B ; and XOR 05H for XOR 21H in the adjacent code. JNZ UPDLOOP SHLD CRCVAL POP H POP B POP PSW RET ; FINCRC: EQU $ ; Finish CRC calc for outbound message. PUSH PSW XRA A CALL UPDCRC CALL UPDCRC PUSH H LHLD CRCVAL MOV D,H MOV E,L POP H POP PSW RET ; CHKCRC: EQU $ ; Check CRC bytes of received message. PUSH H LHLD CRCVAL MOV A,H ORA L POP H RZ MVI A,0FFh RET ; CRCVAL DW 0 ; MENU LXI H,RESTSN ;RESTORE SECTORE NUMBERS.. LXI D,SECTNOB ;..FOR NEW FILE TRANSFER. MVI B,SECTNOE-SECTNOB CALL MOVE LXI H,RESTROPT ;RESTORE OPTION TABLE LXI D,OPTBL MVI B,OPTBE-OPTBL CALL MOVE MVI A,0 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,CR,LF DB 'WRT - Write file to disk (from terminal mode)',CR,LF DB 'DEL - Erase present file (from terminal mode)',CR,LF DB 'RET - Return to terminal mode with no loss of data',CR,LF,0 LDA PMMIBYTE ORA A JZ S5 CALL ILPRT DB 'DSC - Disconnect',CR,LF DB 'CAL - Dial number',CR,LF,0 S5 CALL ILPRT DB 'XPR - Toggle expert mode (Menu on/off)',CR,LF DB 'DIR - List directory (may specify drive)',CR,LF DB 'CPM - Exit to CP/M',CR,LF DB 'S - Send CP/M file',CR,LF DB 'R - Receive CP/M file',CR,LF DB 'T - Terminal mode (optional file name)',CR,LF DB 'E - Terminal mode with echo',CR,LF,0 XPRT CALL ILPRT DB CR,LF,CR,LF,'DEFAULT DRIVE: ',0 MVI C,25 ;CURRENT DISK FUNCTION CALL BDOS ADI 41H ;MAKE ASCII CALL TYPE CALL ILPRT DB CR,LF,CR,LF,'Command: ' DB 0 GETCMD 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 'RET',0 JC NXTOPT1 ;CARRY SET = NO MATCH LHLD HLSAVE ;RETURN TO TERMINAL.. JMP TERM ;..MODE WITH SAVE OPTION.. ;..IF PREVIOUSLY ENABLED. NXTOPT1 LDA PMMIBYTE ORA A JZ S6 CALL ILCOMP ;DE SET FROM 1ST ILCOMP CALL DB 'DSC',0 JNC DISCON1 S6 CALL ILCOMP DB 'WRT',0 JNC WRTFIL CALL ILCOMP DB 'XPR',0 JNC XPRMODE CALL ILCOMP DB 'DEL',0 JNC NEWFILE LDA PMMIBYTE ORA A JZ NXTOPT2 CALL ILCOMP DB 'CAL',0 JC NXTOPT2 MVI A,1 ;FORCE 1 IN CHAR COUNT OF.. STA CMDBUF+1 ;..CMDBUF SO THAT IT ONLY.. JMP DOOPT ;..LOOKS AT 'C' FOR DIAL NXTOPT2 PUSH H LDA CMDBUF+2 LXI H,COMPLIST CALL COMPARE ;COMPARES LIST POINTED TO BY HL.. POP H ;..TO CHAR IN A-REG. JC MENU1 ;CARRY SET = NO MATCH DOOPT PUSH H ;LOAD ORIGINAL FCB WITH TRANSFER.. CALL SETFCB ;..CMDS AND GO TO BEGINNING OF.. POP H ;..PROGRAM. WILL FOLLOW SAME LOGIC.. JMP RESTART ;..AS IF PROGRAM WERE CALLED WITH.. ;..CP/M COMMAND LINE. DISCON1 LDA PMMIBYTE ORA A JZ MENU CALL DISCONNT CALL ILPRT DB CR,LF,'++DISCONNECTED++',CR,LF,BELL,0 JMP MENU1 DIR CALL DIRLST JMP XPRT NEWFILE LDA FCB3+1 CPI ' ' 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.. CMA ;..SPECIFIED. STA SAVEFLG OUT FRONTPAN LXI H,FCB3 CALL INITFCBS JMP MENU1 WRTFIL LDA NFILFLG CPI TRUE JZ MENU1 LDA FCB3+1 ;CHECK THAT FILE WAS REQUESTED CPI ' ' JZ MENU1 LHLD HLSAVE CALL NUMRECS ;DISK WRITE ROUTINE AS USED IN.. CALL WRTDSK ;..IN THE INTDSKSV ROUTINE. CALL CLOSE3 MVI A,TRUE STA NFILFLG CMA STA SAVEFLG OUT FRONTPAN LXI H,FCB3 CALL INITFCBS ;BLANK OUT FCB SO WRITTEN FILE.. JMP MENU1 ;..CAN'T BE ERASED. XPRMODE LDA XPRFLG CMA STA XPRFLG JMP MENU1 COMPARE MOV B,M ;COMPARES A-REG WITH LIST.. COMPLP INX H ;..ADDRESSED BY HL. FIRST ELEMENT.. CMP M ;..OF LIST MUST BE NUMBER OF ELEMENTS.. JZ VALID ;..BEING COMPARED. RETURNS WITH.. DCR B ;..CARRY SET IF A-REG DOES NOT.. JNZ COMPLP ;.. CONTAIN AN ELEMENT IN LIST. STC VALID RET COMPLIST DB 5, 'S', 'R', 'T', 'E', 'M' ILCOMP INLNCOMP ;A MACRO IN MACROS.LIB INBUFF INBUF ;A MACRO IN "MACROS.LIB" ;IF ABOVE ROUTINE DOES NOT LET YOU EDIT IN A PROPER MANNER, ;THEN THE MACRO MAY BE SUBSTITUTED FOR THE FOLLOWING ROUTINE: ;INBUFF MVI C,RDBUF ; CALL BDOSRT ; RET ;BUT BE CAREFUL OF CONTROL-C CPMLINE CMDLINE ;A MACRO IN "MACROS.LIB" DIRLST DIRLIST ;A MACRO IN "MACROS.LIB" NFILFLG DB FALSE ;NORMALLY SET TO FALSE. ALLOWS WRITE TO.. ;..MEMORY IN TERMINAL MODE. OPTION DB 0 OPTBL EQU $ ANSWFLG DB 'A' DISCFLG DB 'D' ORIGFLG DB 'O' QFLG DB 'Q' RSEEFLG DB 'R' SSEEFLG DB 'S' VSEEFLG DB 'V' TERMFLG DB 'T' crcflg db 'C' ;use CRC instead of cksum EPARITY DB '0' ;EVEN PARITY SUB-OPTION - ONLY AVAILABLE IN 'S' AND 'R' MODES OPARITY DB '1' ;ODD PARITY SUB-OPTION - ONLY AVAILABLE IN 'S' AND 'R' MODES BATCHFLG DS 1 ;SET TO 'B' BY MENU. DOES NOT ALLOW MULTI-.. OPTBE EQU $ ;..FILE XFER WHEN PROGRAM INITIALLY CALLED. RESTROPT ;MUST BE IN SAME ORDER AS TABLE ABOVE DB 'A','D','O','Q','R','S','V','T','C','0','1','B' RESTSN DB 0,0,0,0,0,0 DW DBUF DB 0,0,0,0,0 SECTNOB EQU $ RCVSNO DB 0 SECTNO DW 0 ERRCT DB 0 ERRCDE DB 0 EOFLG DB 0 SECPTR DW DBUF SECINBF DB 0 MAXEXT DB 0 RCNT DW 0 DATAFLG DB 0 EXACFL DB 0 SECTNOE EQU $ BADOPT CALL ILPRT DB 'INVALID OPTION',CR,LF,BELL,0 JMP MENU FSTFLG DB TRUE CMDBUF DB 80H,0 DS 80H BADLIB DB CR,LF,'++BAD LIBRARY NUMBER CALLED++',CR,LF,'$' HLSAVE DS 2 DISKNO DS 1 SENDFLG DS 1 NBSAVE DS 2 BGNMS DS 2 FILECT DS 1 NAMECT DS 1 MODCTLB DB 07FH UARTCTLB DB ORIGMOD DS 100 STACK DS 2 FCB3 DS 33 FCBBUF DS 15 DBUF EQU $ NAMEBUF EQU DBUF+(DBUFSIZ*1024) ;BUFFER FOR NAMES IN BATCH MODE. OVERFLOWS.. ;..ABOVE PROGRAM CODE. ; BDOS EQUATES RDCON EQU 1 WRCON EQU 2 PRINT EQU 9 RDBUF EQU 10 CONST EQU 11 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 REIPL EQU 0 FCB EQU 5CH FCBEXT EQU FCB+12 FCBSNO EQU FCB+32 FCBRNO EQU FCB+32 FCB2 EQU 6CH LAST END 100H