;MODEM ROUTINE - SEND, RECV, COMPUTER, TERMINAL ; * * * * * * * * * * * * * * * * * * * * ;SENSE SWITCH CONTROLS: * ; * ; A12 UP TO DISPLAY INCOMING DATA * ; A13 UP TO DISPLAY OUTGOING DATA * ; * * * * * * * * * * * * * * * * * * * * ; ;09/23/77 FIRST WRITTEN BY WARD CHRISTENSEN ;09/25/77 FIRST TESTING COMPLETE ;09/26/77 ADD ERROR$LIMIT EQU ;10/01/77 CHANGE EXIT$CHAR FROM CTL-C TO ; CTL-E FOR USE W/TIMESHARING COMPUTERS ;10/10/77 CORRECT TO SEND ANY LENGTH FILE ; MODEM$CTL$PORT EQU 4 MODEM$SEND$MASK EQU 2 SEND$READY EQU 2 ;VALUE WHEN READY MODEM$RECV$MASK EQU 1 RECV$READY EQU 1 ;BIT ON WHEN READY MODEM$DATA$PORT EQU 5 KEY$CTL$PORT EQU 0 ;KEYBOARD STATUS KEY$READY$MASK EQU 2 KEY$READY EQU 2 ;VALUE WHEN KEYBOARD READY KEY$DATA$PORT EQU 1 INIT$REQD EQU 1 ;MODEM INIT. REQ'D? INIT$CHAR$1 EQU 3 ;FIRST INIT CHAR TO CTL PORT INIT$CHAR$2 EQU 15H ;2ND INIT CHAR TO CTL PORT ERROR$LIMIT EQU 10 ;MAX ALLOWABLE ERRORS EXIT$CHAR EQU 'E'-40H ;CHAR TO EXIT FROM T OR C ORG 100H CALL START ;GO PRINT ID DB 'MODEM PROGRAM AS OF ' DB '10/10/77',13,10,'$' ;FLAG FOR GENERATING TEST CODE TO USE KEYBOARD ;TO ECHO ACK/NAK WHILE TESTING: TEST EQU 0 ;GENERATE TEST CODE ; ;DEFINE ASCII CHARACTERS USED SOH EQU 1 EOT EQU 4 ACK EQU 6 NAK EQU 15H LF EQU 10 CR EQU 13 ; START POP D ;GET ID MESSAGE MVI C,PRINT CALL BDOS ;PRINT ID MESSAGE ;INIT PRIVATE STACK LXI H,0 ;HL=0 DAD SP ;HL=STACK FROM CP/M SHLD STACK ;..SAVE IT LXI SP,STACK ;SP=MY STACK ; CALL INIT$PORT ;GOBBLE UP GARBAGE CHARS FROM THE LINE MVI B,1 ;TIMEOUT DELAY CALL RECV MVI B,1 CALL RECV ; LDA FCB+1 ;GET OPTION (S R C T) PUSH PSW ;SAVE OPTION CALL MOVE$FCB ;MOVE FROM 6C TO 5C POP PSW ;GET OPTION CPI 'S' JZ SEND$FILE CPI 'R' JZ RECV$FILE CPI 'C' JZ COMPUTER CPI 'T' JZ TERMINAL ;INVALID OPTION CALL ERXIT ;EXIT W/ERROR DB '++INVALID OPTION ON MODEM COMMAND - ',CR,LF DB 'MUST BE ONE OF THE FOLLOWING:',CR,LF DB 'MODEM SEND FILENAME',CR,LF DB 'MODEM RECV FILENAME',CR,LF DB 'MODEM COMPUTER',CR,LF DB 'MODEM TERMINAL',CR,LF DB ' ABBREV. ALLOWED: S R C T$' ; ;****************COMPUTER**************** ; ;TERMINAL-TERMINAL W/ECHO SENT BY THIS PROGRAM ;'EXIT$CHAR' TYPED TO RE-BOOT ;IF ONE COMPUTER IS IN COMPUTER MODE, ;THE OTHER SHOULD BE IN TERMINAL MODE. ;AT NO TIME SHOULD BOTH BE IN COMPUTER ;MODE BECAUSE LINE ERRORS WILL BE PING-PONGED ;BACK AND FORTH AD INFINITUM. COMPUTER: IN MODEM$CTL$PORT ANI MODEM$RECV$MASK CPI RECV$READY JZ LINE$CHAR ;NOTHING FROM LINE, CHECK KEYBOARD MVI C,CONST ;CHECK STATUS CALL BDOS ORA A ;READY? JZ COMPUTER ;..NO MVI C,RDCON CALL BDOS ;GET CHAR CPI EXIT$CHAR ;END? JZ EXIT ;YES, EXIT OUT MODEM$DATA$PORT ;SEND CHAR JMP COMPUTER ;GOT CHAR FROM LINE LINE$CHAR: IN MODEM$DATA$PORT OUT MODEM$DATA$PORT ;ECHO CALL TYPE ;TYPE IT JMP COMPUTER ; ;**************TERMINAL**************** ; ;SEE NOTES UNDER 'COMPUTER' ; TERMINAL: IN KEY$CTL$PORT ANI KEY$READY$MASK CPI KEY$READY JNZ TRECV ;NOTHING FROM KEYBOARD IN KEY$DATA$PORT ANI 7FH CPI EXIT$CHAR ;TIME TO END? JZ EXIT OUT MODEM$DATA$PORT TRECV IN MODEM$CTL$PORT ANI MODEM$RECV$MASK CPI RECV$READY JNZ TERMINAL IN MODEM$DATA$PORT CALL TYPE JMP TERMINAL ;INIT SERIAL PORT INIT$PORT: LXI D,MSG$BAUD CALL PRINT$MESSAGE IF INIT$REQD MVI A,INIT$CHAR$1 OUT MODEM$CTL$PORT MVI A,INIT$CHAR$2 OUT MODEM$CTL$PORT ENDIF ;GET THE SPEED XRA A ;GET A ZERO CALL SEND ;SEND A CHAR XRA A ;GET ZERO CALL SEND ;SEND AGAIN ;WAIT, TIMING TO DETERMINE BAUD RATE LXI B,0 ;INIT COUNT INIT$WAIT: IN MODEM$CTL$PORT ANI MODEM$SEND$MASK CPI SEND$READY JZ INIT$WAIT$END DCR C JNZ INIT$WAIT DCR B JNZ INIT$WAIT CALL ERXIT DB '++TIME OUT DETERMINING BAUD RATE$' INIT$WAIT$END: MOV A,B ;GET COUNT CPI 0F5H ;110 BAUD = F0, JC BAUD$110 ;300 BAUD = FA ;BAUD RATE 300 LXI D,MSG$300 INIT$PRINT: CALL PRINT$MESSAGE RET ;FROM INIT$PORT BAUD$110: LXI D,MSG$110 JMP INIT$PRINT MSG$BAUD DB 'BAUD RATE IS $' MSG$110 DB '110',CR,LF,'$' MSG$300 DB '300',CR,LF,'$' ;MOVE FCB (SECOND OPERAND ON COMMAND) ; TO NORMAL FCB LOCATION MOVE$FCB: LXI H,FCB LXI D,FCB+16 MVI B,16 MOVE$LOOP: LDAX D MOV M,A INX D INX H DCR B JNZ MOVE$LOOP XRA A ;GET 0 STA FCB+32 ;ZERO RECORD # RET ; ;*****************SEND FILE*************** ; SEND$FILE: CALL OPEN$FILE ;OPEN THE FILE LXI D,OPENM CALL PRINT$MESSAGE SENDB XRA A ;GET A ZERO STA ERRCT ;ZERO ERROR COUNT ;READ SECTOR, SEND IT CALL READ$SECTOR LDA SECTNO ;INCR SECT NO. INR A STA SECTNO ;SEND OR REPEAT SECTOR REPTB LXI D,SECTMSG CALL PRINT$MESSAGE LDA SECTNO CALL HEXO CALL CRLF MVI A,SOH CALL SEND LDA SECTNO CALL SEND LDA SECTNO CMA CALL SEND MVI C,0 ;INIT CKSUM LXI H,80H SENDC MOV A,M CALL SEND INX H MOV A,H CPI 1 ;DONE WITH SECTOR? JNZ SENDC ;SECTOR SENT, SEND CKSUM MOV A,C ;GET CKSUM CALL SEND ;GET ACK ON SECTOR MVI B,4 ;WAIT 4 SECONDS MAX CALL RECV JNC SNTO ;NO TIMEOUT ;TIMED OUT WAITING FOR ACK CALL TOUT ;PRINT 'TIMEOUT', ERRCT DATERR LDA ERRCT INR A STA ERRCT CPI ERROR$LIMIT JC REPTB ;REPEAT SECTOR ;SECTOR SEND NO GOOD AFTER 10 TRIES CALL ERXIT DB 'CAN''T SEND SECTOR ' DB '- ABORTING',13,10,'$' SECTMSG DB 'SENDING SECTOR $' ;NO TIMEOUT SENDING SECTOR SNTO CPI ACK ;ACK RECIEVED? JZ SENDB ;..YES, SEND NEXT SECT ;ACK NOT RECIEVED CALL HEXO ;TYPE CHR IN HEX LXI D,ERR1 CALL PRINT$MESSAGE JMP DATERR ;GO TO DATA ERROR ERR1 DB 'H RECEIVED, NOT ACK',13,10,'$' OPENM DB 'FILE OPEN',13,10,'$' ; ;**************RECEIVE FILE**************** ; RECV$FILE: CALL ERASE$OLD$FILE CALL MAKE$NEW$FILE RECV$LOOP: XRA A ;GET 0 STA ERRCT ;INIT ERROR COUNT RECV$HDR: LXI D,RMSG CALL PRINT$MESSAGE LDA SECTNO INR A CALL HEXO CALL CRLF MVI B,5 ;5 SEC TIMEOUT CALL RECV JNC RHNTO ;NO TIMEOUT RECV$HDR$TIMEOUT: CALL TOUT ;PRINT TIMEOUT RECV$SECT$ERR: ;PURGE THE LINE OF INPUT CHARS MVI B,1 ;1 SEC W/NO CHARS CALL RECV JNC RECV$SECT$ERR ;LOOP UNTIL SENDER DONE MVI A,NAK CALL SEND ;SEND NAK LDA ERRCT INR A STA ERRCT CPI ERROR$LIMIT JC RECV$HDR CALL ERXIT DB '++UNABLE TO GET VALID HEADER',0DH,0AH,'$' RMSG DB 'WAITING FOR SECTOR #$' ;GOT CHAR - MUST BE SOH RHNTO CPI SOH JZ GOT$SOH ORA A ;00 FROM SPEED CHECK? JZ RECV$HDR CPI EOT JZ GOT$EOT ;DIDN'T GET SOH - CALL HEXO LXI D,ERRSOH CALL PRINT$MESSAGE JMP RECV$SECT$ERR ERRSOH DB 'H RECEIVED, NOT SOH',0DH,0AH,'$' GOT$SOH: MVI B,1 CALL RECV JC RECV$HDR$TIMEOUT MOV D,A ;D=BLK # MVI B,1 CALL RECV ;GET CMA'D SECT # JC RECV$HDR$TIMEOUT CMA CMP D ;GOOD SECTOR #? IF TEST JMP RECV$SECTOR ENDIF JZ RECV$SECTOR ;GOT BAD SECTOR # LXI D,ERR2 CALL PRINT$MESSAGE JMP RECV$SECT$ERR ERR2 DB '++BAD SECTOR # IN HDR',0DH,0AH,'$' ; RECV$SECTOR: MOV A,D ;GET SECTOR # STA RECVD$SECT$NO MVI C,0 ;INIT CKSUM LXI H,80H ;POINT TO BUFFER RECV$CHAR: MVI B,1 ;1 SEC TIMEOUT CALL RECV ;GET CHAR JC RECV$HDR$TIMEOUT MOV M,A ;STORE CHAR INR L ;DONE? JNZ RECV$CHAR ;VERIFY CHECKSUM MOV D,C ;SAVE CHECKSUM MVI B,1 ;TIMEOUT CALL RECV ;GET CHECKSUM JC RECV$HDR$TIMEOUT CMP D ;CHECK JNZ RECV$CKSUM$ERR ; ;GOT A SECTOR, WRITE IF = 1+PREV SECTOR ; LDA RECVD$SECT$NO MOV B,A ;SAVE IT LDA SECTNO ;GET PREV INR A ;CALC NEXT SECTOR # CMP B ;MATCH? JNZ DO$ACK ;GOT NEW SECTOR - WRITE IT LXI D,FCB MVI C,WRITE CALL BDOS ORA A JNZ WRITE$ERROR LDA RECVD$SECT$NO STA SECTNO ;UPDATE SECTOR # DO$ACK MVI A,ACK CALL SEND JMP RECV$LOOP ; WRITE$ERROR: CALL ERXIT DB '++ERROR WRITING FILE',0DH,0AH,'$' ; RECV$CKSUM$ERR: LXI D,ERR3 CALL PRINT$MESSAGE JMP RECV$SECT$ERR ERR3 DB '++BAD CKSUM ON SECTOR' DB 0DH,0AH,'$' ; GOT$EOT: MVI A,ACK ;ACK THE EOT CALL SEND LXI D,FCB MVI C,CLOSE CALL BDOS INR A JNZ XFER$CPLT CALL ERXIT DB '++ERROR CLOSING FILE$' ; ERASE$OLD$FILE: LXI D,FCB MVI C,SRCHF ;SEE IF IT EXISTS CALL BDOS INR A ;FOUND? RZ ;NO, RETURN LXI D,EXIST CALL PRINT$MESSAGE MVI C,RDCON CALL BDOS CPI 'Y' JNZ 0 ;REBOOT IF NOT ERASE CALL CRLF ;ERASE OLD FILE LXI D,FCB MVI C,ERASE CALL BDOS RET EXIST DB '++FILE EXISTS, TYPE Y TO ERASE:$' ; MAKE$NEW$FILE: LXI D,FCB MVI C,MAKE CALL BDOS INR A ;FF=BAD RNZ ;OPEN OK ;DIRECTORY FULL - CAN'T MAKE FILE CALL ERXIT DB '++ERROR - CAN''T MAKE FILE',0DH,0AH DB '++DIRECTORY MUST BE FULL',0DH,0AH,'$' ; ; S U B R O U T I N E S ; ;OPEN FILE OPEN$FILE LXI D,FCB MVI C,OPEN CALL BDOS INR A ;OPEN OK? RNZ ;GOOD OPEN CALL ERXIT DB 'CAN''T OPEN FILE$' ; - - - - - - - - - - - - - - - PRINT$MESSAGE: MVI C,PRINT JMP BDOS ;PRINT MESSAGE, RETURN ; - - - - - - - - - - - - - - - ;EXIT PRINTING MESSAGE FOLLOWING 'CALL ERXIT' ERXIT POP D ;GET MESSAGE CALL PRINT$MESSAGE ;PRINT IT EXIT LHLD STACK ;GET ORIGINAL STACK SPHL ;RESTORE IT RET ;--EXIT-- TO CP/M ; - - - - - - - - - - - - - - - ;MODEM RECV RECV PUSH D ;SAVE MSEC LXI D,0BBBBH ;1 SEC DCR COUNT IF NOT TEST MWTI IN MODEM$CTL$PORT ANI MODEM$RECV$MASK CPI RECV$READY JZ MCHAR ;GOT CHAR ENDIF IF TEST MWTI IN KEY$CTL$PORT ;READ KEYBOARD ANI KEY$READY$MASK CPI KEY$READY JZ MCHAR ENDIF DCR E ;COUNT DOWN JNZ MWTI ;FOR TIMEOUT DCR D JNZ MWTI DCR B ;DCR # OF SECONDS JNZ MSEC ;MODEM TIMED OUT RECEIVING POP D ;RESTORE D,E STC ;CARRY SHOWS TIMEOUT RET ;GOT MODEM CHAR IF NOT TEST MCHAR IN MODEM$DATA$PORT ENDIF IF TEST MCHAR IN KEY$DATA$PORT ANI 7FH ;DEL PARITY FROM KEYBOAREAD ENDIF POP D ;RESTORE DE ;CALC CHECKSUM PUSH PSW ADD C MOV C,A ;CHECK IF MONITORING INPUT IN 0FFH ANI 10H JZ NO$MON$INPUT POP PSW PUSH PSW CALL SHOW ;CHAR RECEIVED NO$MON$INPUT: POP PSW ;TURN OFF CARRY TO SHOW NO TIMEOUT ORA A RET ; - - - - - - - - - - - - - - - ;MODEM SEND CHAR ROUTINE SEND PUSH PSW ;CHECK IF MONITORING OUTPUT IN 0FFH ANI 20H JZ NO$MON$OUTPUT POP PSW PUSH PSW CALL SHOW NO$MON$OUTPUT: POP PSW PUSH PSW ADD C ;CALC CKSUM MOV C,A SENDW IN MODEM$CTL$PORT ANI MODEM$SEND$MASK CPI SEND$READY JNZ SENDW POP PSW ;GET CHAR OUT MODEM$DATA$PORT RET ; - - - - - - - - - - - - - - - ;SHOW CHAR RECEIVED OR SENT SHOW CPI 0AH ;LF? JZ TYPE CPI 0DH JZ TYPE CPI 09 ;TAB JZ TYPE CPI ' ' JC SHOWHEX CPI 7FH JC TYPE SHOWHEX PUSH PSW MVI A,'(' CALL TYPE POP PSW CALL HEXO MVI A,')' JMP TYPE ; - - - - - - - - - - - - - - - ;PRINT TIMEOUT MESSAGE TOUTM DB 'TIMEOUT $' TOUT LXI D,TOUTM CALL PRINT$MESSAGE PRINT$ERRCT: LDA ERRCT CALL HEXO ;FALL INTO CR/LF ; - - - - - - - - - - - - - - - CRLF MVI A,13 CALL TYPE MVI A,10 ; - - - - - - - - - - - - - - - TYPE PUSH PSW PUSH B PUSH D PUSH H MOV E,A MVI C,WRCON CALL BDOS POP H POP D POP B POP PSW RET ; - - - - - - - - - - - - - - - ;HEX OUTPUT 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 ; - - - - - - - - - - - - - - - ;FILE READ ROUTINE READ$SECTOR: LXI D,FCB MVI C,READ CALL BDOS ORA A RZ DCR A ;EOF? JNZ RDERR ;EOF XRA A STA ERRCT LXI D,FSENTM ;FILE SENT MESSAGE CALL PRINT$MESSAGE SEOT MVI A,EOT CALL SEND MVI B,5 ;WAIT 5 SEC FOR TIMEOUT CALL RECV JC EOTTOT ;EOT TIMEOUT CPI ACK JZ XFER$CPLT ;ACK NOT RECIEVED CALL HEXO LXI D,ERR1 CALL PRINT$MESSAGE EOTERR LDA ERRCT INR A STA ERRCT CPI ERROR$LIMIT JC SEOT CALL ERXIT DB 'NO ACK RECIEVED ON EOT$',10,13 FSENTM DB 13,10,'FILE SENT, SENDING EOT''S',10,13,'$' ;TIMEOUT ON EOT EOTTOT CALL TOUT JMP EOTERR ;READ ERROR RDERR CALL ERXIT DB '++FILE READ ERROR$' ; - - - - - - - - - - - - - - - ;DONE - CLOSE UP SHOP XFER$CPLT: CALL ERXIT DB 13,10,'TRANSFER COMPLETE$' DS 40 ;STACK AREA STACK DS 2 ;STACK POINTER RECVD$SECT$NO DB 0 SECTNO DB 0 ;CURRENT SECTOR NUMBER ERRCT DB 0 ;ERROR COUNT ; ; BDOS EQUATES (VERSION 2) ; RDCON EQU 1 WRCON EQU 2 PRINT EQU 9 CONST EQU 11 ;CONSOLE STAT OPEN EQU 15 ;0FFH=NOT FOUND CLOSE EQU 16 ; " " SRCHF EQU 17 ; " " SRCHN EQU 18 ; " " ERASE EQU 19 ;NO RET CODE READ EQU 20 ;0=OK, 1=EOF WRITE EQU 21 ;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC MAKE EQU 22 ;0FFH=BAD REN EQU 23 ;0FFH=BAD STDMA EQU 26 BDOS EQU 5 REIPL EQU 0 FCB EQU 5CH ;SYSTEM FCB