home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols000
/
vol007
/
xmodem32.asm
< prev
next >
Wrap
Assembly Source File
|
1984-04-29
|
20KB
|
865 lines
;
;XMODEM.ASM V3.2, BY KEITH PETERSEN, W8SDZ
; (revised 9/13/80)
;
;CP/M - CP/M FILE TRANSFER PROGRAM
;BASED ON MODEM.ASM V2.0, BY WARD CHRISTENSEN
;THIS PROGRAM IS INTENDED FOR USE ON REMOTE CP/M
;SYSTEMS WHERE IT IS IMPORTANT THAT THE INITIALIZATION
;OF THE MODEM NOT BE CHANGED, SUCH AS WHEN USING
;THE PMMIBYE PROGRAM. THE BAUD RATE AND NUMBER OF BITS
;REMAINS THE SAME AS WHATEVER WAS SET PREVIOUSLY.
;THERE IS NO DISCONNECT, TERMINAL OR ECHO OPTION.
;
;NOTE: THIS FILE WILL ASSEMBLE, WITHOUT NEED FOR
;EDITING, TO WORK WITH A PMMI MM-103 MODEM. SEE
;EQUATES FOR OTHER OPTIONS INCLUDING SYSTEM CLOCK
;FREQUENCY AND OTHER TYPES OF MODEMS.
;
; Added conditional assembly to prevent filetypes
;'.COM' or '.??#' from being sent to distant end
;and added conditional assembly of test for '.COM'
;filetype on receive as well. See 'NOCOM' below.
; Any filetype ending in '#' will not be sent by
;this program if 'NOCOM' is set to TRUE.
; 9-SEP-80 J.SEYMOUR
;
FALSE EQU 0
TRUE EQU NOT FALSE
;
STDCPM EQU TRUE ;TRUE, IS STANDARD CP/M
ALTCPM EQU FALSE ;TRUE, IS H8 OR TRS-80 CP/M
;
IF STDCPM
BASE EQU 0 ;CP/M BASE ADDRESS
ENDIF
;
IF ALTCPM
BASE EQU 4200H ;ALTERNATE CP/M BASE ADDRESS
ENDIF
;
PMMI EQU TRUE ;TRUE, IS PMMI
;
DCH EQU FALSE ;TRUE, IS D.C. HAYES
;
NOCOM EQU TRUE ;TRUE, NO .COM OR .??# FILES
;SENT OR '.COM' RECEIVED
;
IF PMMI
MODCTLP EQU 0C0H ;PMMI VALUES
MODSNDB EQU 1 ;BIT TO TEST FOR SEND
MODSNDR EQU 1 ;VALUE WHEN READY
MODRCVB EQU 2 ;BIT TO TEST FOR RECEIVE
MODRCVR EQU 2 ;VALUE WHEN READY
MODDATP EQU 0C1H ;DATA PORT
BAUDRP EQU 0C2H ;BAUD RATE OUTPUT
MODCTL2 EQU 0C3H ;SECOND CTL PORT
ENDIF
;
IF DCH
MODCTLP EQU 82H ;D. C. HAYES VALUES
MODSNDB EQU 2 ;BIT TO TEST FOR SEND
MODSNDR EQU 2 ;VALUE WHEN READY
MODRCVB EQU 1 ;BIT TO TEST FOR RECEIVE
MODRCVR EQU 1 ;VALUE WHEN READY
MODDATP EQU 80H ;DATA PORT
MODCTL2 EQU 81H ;SECOND CTL PORT
ENDIF
;
;IF YOU ARE USING AN EXTERNAL MODEM (NOT S-100 PLUG-IN)
;CHANGE THESE EQUATES FOR YOUR MODEM PORT REQUIREMENTS
;
IF NOT PMMI AND NOT DCH
MODCTLP EQU 02H ;PUT YOUR MODEM CONTROL PORT HERE
MODSNDB EQU 80H ;YOUR BIT TO TEST FOR SEND
MODSNDR EQU 80H ;YOUR VALUE WHEN READY
MODRCVB EQU 40H ;YOUR BIT TO TEST FOR RECEIVE
MODRCVR EQU 40H ;YOUR VALUE WHEN READY
MODDATP EQU 03H ;YOUR MODEM DATA PORT
ENDIF ;END OF EXTERNAL MODEM EQUATES
;
ERRLIM EQU 10 ;MAX ALLOWABLE ERRORS
;
FASTCLK EQU FALSE ;PUT TRUE HERE FOR 4 MHZ CLOCK
;
;DEFINE ASCII CHARACTERS USED
;
SOH EQU 1 ;START OF HEADER
EOT EQU 4 ;END OF TRANSMISSION
ACK EQU 6 ;ACKNOWLEDGE
NAK EQU 15H ;NEG ACKNOWLEDGE
CAN EQU 18H ;CONTROL-X FOR CANCEL
LF EQU 10 ;LINEFEED
CR EQU 13 ;CARRIAGE RETURN
;
ORG BASE+100H
;
;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 ILPRT ;PRINT:
DB CR,LF
DB 'XMODEM ver 3.2',CR,LF,0
;
;GET OPTION
;
LDA FCB+1 ;GET OPTION (S or R)
PUSH PSW ;SAVE OPTION
;
;MOVE THE FILENAME FROM FCB 2 TO FCB 1
;
CALL MOVEFCB
;
;GOBBLE UP GARBAGE CHARS FROM THE LINE
;PRIOR TO RECEIVE OR SEND
;
IN MODDATP
IN MODDATP
;
;JMP TO APPROPRIATE FUNCTION
;
POP PSW ;GET OPTION
;
CPI 'S' ;SEND..
JZ SENDFIL ;..A FILE?
;
CPI 'R' ;RECEIVE..
JZ RCVFIL ;..A FILE?
;
;INVALID OPTION
CALL ERXIT ;EXIT W/ERROR
DB '++INVALID OPTION ON XMODEM '
DB 'COMMAND++',CR,LF
DB 'Must be S for SEND or R for '
DB 'RECEIVE',CR,LF,'$'
;
* * * * * * * * * * * * * * * * * * * * *
* *
* SENDFIL: SENDS A CP/M FILE *
* *
* * * * * * * * * * * * * * * * * * * * *
;
;THE CP/M FILE SPECIFIED IN THE MODEM COMMAND
;IS TRANSFERRED OVER THE PHONE TO ANOTHER
;COMPUTER RUNNING MODEM WITH THE "R" (RECEIVE)
;OPTION. THE DATA IS SENT ONE SECTOR AT A
;TIME WITH HEADERS AND CHECKSUMS, AND RE-
;TRANSMISSION ON ERRORS.
;
;
SENDFIL CALL OPENFIL ;OPEN THE FILE
MVI E,80 ;WAIT 80 SEC..
CALL WAITNAK ;..FOR INITIAL NAK
SENDLP CALL RDSECT ;READ A SECTOR
JC SENDEOF ;SEND EOF IF DONE
CALL INCRSNO ;BUMP SECTOR #
XRA A ;ZERO ERROR..
STA ERRCT ;..COUNT
SENDRPT CALL SENDHDR ;SEND A HEADER
CALL SENDSEC ;SEND DATA SECTOR
CALL SENDCKS ;SEND CKSUM
CALL GETACK ;GET THE ACK
JC SENDRPT ;REPEAT IF NO ACK
JMP SENDLP ;LOOP UNTIL EOF
;
;FILE SENT, SEND EOT'S
;
SENDEOF MVI A,EOT ;SEND..
CALL SEND ;..AN EOT
CALL GETACK ;GET THE ACK
JC SENDEOF ;LOOP IF NO ACK
JMP EXIT ;ALL DONE
;
* * * * * * * * * * * * * * * * * * * * *
* *
* RCVFIL: RECEIVE A FILE *
* *
* * * * * * * * * * * * * * * * * * * * *
;
;RECEIVES A FILE IN BLOCK FORMAT AS SENT
;BY ANOTHER PERSON DOING "MODEM S FN.FT".
;
RCVFIL EQU $
;
IF NOCOM
LXI H,FCB+9 ;POINT TO FILETYPE
MVI A,'C' ;1ST LETTER
CMP M ;IS IT C ?
JNZ CONTINU ;IF NOT, CONTINUE NORMALLY
INX H ;GET 2ND LETTER
MVI A,'O' ;2ND LETTER
CMP M ;IS IT O ?
JNZ CONTINU ;IF NOT, CONTINUE NORMALLY
INX H ;GET 3RD LETTER
MVI A,'M' ;3RD LETTER
CMP M ;IS IT M ?
JNZ CONTINU ;IF NOT, CONTINUE NORMALLY
CALL ERXIT ;EXIT, PRINT ERROR MESSAGE
DB '++CAN''T RECEIVE A .COM FILE++'
DB CR,LF,CR,LF
DB 'Rename filetype ".OBJ" and try again'
DB CR,LF,'$'
ENDIF
;
CONTINU CALL CHEKFIL ;SEE IF FILE EXISTS
CALL MAKEFIL ;..THEN MAKE NEW
CALL ILPRT ;PRINT:
DB 'FILE OPEN, READY TO RECEIVE',CR,LF,0
;
RCVLP CALL RCVSECT ;GET A SECTOR
JC RCVEOT ;GOT EOT
CALL WRSECT ;WRITE THE SECTOR
CALL INCRSNO ;BUMP SECTOR #
CALL SENDACK ;ACK THE SECTOR
JMP RCVLP ;LOOP UNTIL EOF
;
;GOT EOT ON SECTOR - FLUSH BUFFERS, END
;
RCVEOT CALL WRBLOCK ;WRITE THE LAST BLOCK
CALL SENDACK ;ACK THE SECTOR
CALL CLOSFIL ;CLOSE THE FILE
JMP EXIT ;ALL DONE
;
* * * * * * * * * * * * * * * * * * * * *
* *
* SUBROUTINES *
* *
* * * * * * * * * * * * * * * * * * * * *
;
;
;----> RCVSECT: RECEIVE A SECTOR
;
;RETURNS WITH CARRY SET IF EOT RECEIVED.
;
RCVSECT XRA A ;GET 0
STA ERRCT ;INIT ERROR COUNT
;
RCVRPT MVI B,10 ;10 SEC TIMEOUT
CALL RECV ;GET SOH/EOT
JC RCVSTOT ;TIMEOUT
CPI SOH ;GET SOH?
JZ RCVSOH ;..YES
;
;EARLIER VERS. OF MODEM PROG SENT SOME NULLS -
;IGNORE THEM
;
ORA A ;00 FROM SPEED CHECK?
JZ RCVRPT ;YES, IGNORE IT
CPI EOT ;END OF TRANSFER?
STC ;RETURN WITH CARRY..
RZ ;..SET IF EOT
;
;DIDN'T GET SOH OR EOT -
;
;DIDN'T GET VALID HEADER - PURGE THE LINE,
;THEN SEND NAK.
;
RCVSERR MVI B,1 ;WAIT FOR 1 SEC..
CALL RECV ;..WITH NO CHARS
JNC RCVSERR ;LOOP UNTIL SENDER DONE
MVI A,NAK ;SEND..
CALL SEND ;..THE NAK
LDA ERRCT ;ABORT IF..
INR A ;..WE HAVE REACHED..
STA ERRCT ;..THE ERROR..
CPI ERRLIM ;..LIMIT?
JC RCVRPT ;..NO, TRY AGAIN
;
;10 ERRORS IN A ROW -
;
RCVSABT CALL CLOSFIL ;KEEP WHATEVER WE GOT
CALL ERXIT
DB '++UNABLE TO RECEIVE BLOCK '
DB '- ABORTING++',CR,LF,'$'
;
;TIMEDOUT ON RECEIVE
;
RCVSTOT JMP RCVSERR ;BUMP ERR CT, ETC.
;
;GOT SOH - GET BLOCK #, BLOCK # COMPLEMENTED
;
RCVSOH MVI B,1 ;TIMEOUT = 1 SEC
CALL RECV ;GET SECTOR
JC RCVSTOT ;GOT TIMEOUT
MOV D,A ;D=BLK #
MVI B,1 ;TIMEOUT = 1 SEC
CALL RECV ;GET CMA'D SECT #
JC RCVSTOT ;TIMEOUT
CMA ;CALC COMPLEMENT
CMP D ;GOOD SECTOR #?
JZ RCVDATA ;YES, GET DATA
;
;GOT BAD SECTOR #
;
JMP RCVSERR ;BUMP ERROR CT.
;
RCVDATA MOV A,D ;GET SECTOR #
STA RCVSNO ;SAVE IT
MVI C,0 ;INIT CKSUM
LXI H,BASE+80H ;POINT TO BUFFER
;
RCVCHR MVI B,1 ;1 SEC TIMEOUT
CALL RECV ;GET CHAR
JC RCVSTOT ;TIMEOUT
MOV M,A ;STORE CHAR
INR L ;DONE?
JNZ RCVCHR ;NO, LOOP
;
;VERIFY CHECKSUM
;
MOV D,C ;SAVE CHECKSUM
MVI B,1 ;TIMEOUT LEN.
CALL RECV ;GET CHECKSUM
JC RCVSTOT ;TIMEOUT
CMP D ;CHECKSUM OK?
JNZ RCVSERR ;NO, ERROR
;
;GOT A SECTOR, IT'S A DUP IF = PREV,
; OR OK IF = 1 + PREV SECTOR
;
LDA RCVSNO ;GET RECEIVED
MOV B,A ;SAVE IT
LDA SECTNO ;GET PREV
CMP B ;PREV REPEATED?
JZ RECVACK ;ACK TO CATCH UP
INR A ;CALC NEXT SECTOR #
CMP B ;MATCH?
JNZ ABORT ;NO MATCH - STOP SENDER, EXIT
RET ;CARRY OFF - NO ERRORS
;
;PREV SECT REPEATED, DUE TO THE LAST ACK
;BEING GARBAGED. ACK IT SO SENDER WILL CATCH UP
;
RECVACK CALL SENDACK ;SEND THE ACK,
JMP RCVSECT ;GET NEXT BLOCK
;
;SEND AN ACK FOR THE SECTOR
;
SENDACK MVI A,ACK ;GET ACK
CALL SEND ;..AND SEND IT
RET
;
;----> SENDHDR: SEND THE SECTOR HEADER
;
;SEND: (SOH) (BLOCK #) (COMPLEMENTED BLOCK #)
;
SENDHDR MVI A,SOH ;SEND..
CALL SEND ;..SOH,
LDA SECTNO ;THEN SEND..
CALL SEND ;..SECTOR #
LDA SECTNO ;THEN SECTOR #
CMA ;..COMPLEMENTED..
CALL SEND ;..SECTOR #
RET ;FROM SENDHDR
;
;----> SENDSEC: SEND THE DATA SECTOR
;
SENDSEC MVI C,0 ;INIT CKSUM
LXI H,BASE+80H ;POINT TO BUFFER
SENDC MOV A,M ;GET A CHAR
CALL SEND ;SEND IT
INR L ;POINT TO NEXT CHAR
JNZ SENDC ;LOOP IF <100H
RET ;FROM SENDSEC
;
;----> SENDCKS: SEND THE CHECKSUM
;
SENDCKS MOV A,C ;SEND THE..
CALL SEND ;..CHECKSUM
RET ;FROM SENDCKS
;
;----> GETACK: GET THE ACK ON THE SECTOR
;
;RETURNS WITH CARRY CLEAR IF ACK RECEIVED.
;IF AN ACK IS NOT RECEIVED, THE ERROR COUNT
;IS INCREMENTED, AND IF LESS THAN "ERRLIM",
;CARRY IS SET AND CONTROL RETURNS. IF THE
;ERROR COUNT IS AT "ERRLIM", THE PROGRAM
;ABORTS.
;
GETACK MVI B,10 ;WAIT 10 SECONDS MAX
CALL RECVDG ;RECV W/GARBAGE COLLECT
JC GETATOT ;TIMED OUT
CPI ACK ;OK? (CARRY OFF IF =)
RZ ;YES, RET FROM GETACK
;
;TIMEOUT OR ERROR ON ACK - BUMP ERROR COUNT
;
ACKERR LDA ERRCT ;GET COUNT
INR A ;BUMP IT
STA ERRCT ;SAVE BACK
CPI ERRLIM ;AT LIMIT?
RC ;NOT AT LIMIT
;
;REACHED ERROR LIMIT
;
CSABORT CALL ERXIT
DB '++CAN''T SEND SECTOR '
DB '- ABORTING++',CR,LF,'$'
;
;TIMEOUT GETTING ACK
;
GETATOT JMP ACKERR ;NO MSG
;
ABORT LXI SP,STACK
;
ABORTL MVI B,1 ;1 SEC. W/O CHARS.
CALL RECV
JNC ABORTL ;LOOP UNTIL SENDER DONE
MVI A,CAN ;CONTROL X
CALL SEND ;STOP SENDING END
;
ABORTW MVI B,1 ;1 SEC W/O CHARS.
CALL RECV
JNC ABORTW ;LOOP UNTIL SENDER DONE
MVI A,' ' ;GET A SPACE...
CALL SEND ;TO CLEAR OUT CONTROL X
CALL ERXIT ;EXIT WITH ABORT MSG
DB 'XMODEM PROGRAM CANCELLED',CR,LF,'$'
;
;----> INCRSNO: INCREMENT SECTOR #
;
INCRSNO LDA SECTNO ;INCR..
INR A ;..SECT..
STA SECTNO ;..NUMBER
RET
;
;----> CHEKFIL: SEE IF FILE EXISTS
;
;IF IT EXISTS, SAY IT MUST BE ERASED.
;
CHEKFIL LXI D,FCB ;POINT TO CTL BLOCK
MVI C,SRCHF ;SEE IF IT..
CALL BDOS ;..EXISTS
INR A ;FOUND?
RZ ;..NO, RETURN
CALL ERXIT ;EXIT, PRINT ERROR MESSAGE
DB '++FILE EXISTS, USE A DIFFERENT NAME++'
DB CR,LF,'$'
;
;----> MAKEFIL: MAKES THE FILE TO BE RECEIVED
;
MAKEFIL LXI D,FCB ;POINT TO FCB
MVI C,MAKE ;GET BDOS FNC
CALL BDOS ;TO THE MAKE
INR A ;FF=BAD?
RNZ ;OPEN OK
;DIRECTORY FULL - CAN'T MAKE FILE
CALL ERXIT
DB '++ERROR - CAN''T MAKE FILE++',CR,LF
DB 'Directory must be full',CR,LF,'$'
;
;----> OPENFIL: OPENS THE FILE TO BE SENT
;
OPENFIL LXI D,FCB ;POINT TO FILE
MVI C,OPEN ;GET FUNCTION
CALL BDOS ;OPEN IT
INR A ;OPEN OK?
JNZ OPENOK ;..YES
CALL ERXIT ;..NO, ABORT
DB '++FILE NOT FOUND++',CR,LF,'$'
;
;CHECK FOR DISTRIBUTION-PROTECTED FILE
;
OPENOK LDA FCB+1 ;FIRST CHAR OF FILE NAME
ANI 80H ;CHECK BIT 7
JZ OPENOK2 ;IT WAS OFF, FILE CAN BE SENT
;
OPENOT CALL ERXIT ;EXIT W/MESSAGE
DB '++THIS FILE IS NOT FOR DISTRIBUTION, SORRY++'
DB CR,LF,'$'
;
OPENOK2 EQU $
;
IF NOCOM
LXI H,FCB+11
MOV A,M ;CHECK FOR PROTECT ATTR
ANI 7FH ;REMOVE CP/M 2.x ATTRS
CPI '#' ;CHK FOR '#' AS LAST FIRST
JZ OPENOT ;IF '#', CAN'T SEND, SHOW WHY
CPI 'M' ;IF NOT, CHK FOR '.COM'
JNZ OPENOK3 ;IF NOT, OK TO SEND
DCX H
MOV A,M ;CHK NEXT CHAR
ANI 7FH ;STRIP ATTRIBUTES
CPI 'O' ; 'O'?
JNZ OPENOK3 ;IF NOT, OK TO SEND
DCX H
MOV A,M ;NOW CHK FIRST CHAR
ANI 7FH ;STRIP ATTRIBUTES
CPI 'C' ; 'C' AS IN '.COM'?
JNZ OPENOK3 ;IF NOT, CONTINUE
CALL ERXIT ;EXIT W/MESSAGE
DB '++CAN''T SEND A .COM FILE++'
DB CR,LF,'$'
ENDIF
;
OPENOK3 CALL ILPRT ;PRINT:
DB 'FILE OPEN, EXTENT LENGTH: ',0
LDA FCB+15 ;GET # SECTORS
CALL HEXO ;PRINT IN HEX
CALL ILPRT ;PRINT H AFTER NUMBER, THEN CR,LF
DB 'H',CR,LF,0
RET
;
;----> CLOSFIL: CLOSES THE RECEIVED FILE
;
CLOSFIL LXI D,FCB ;POINT TO FILE
MVI C,CLOSE ;GET FUNCTION
CALL BDOS ;CLOSE IT
INR A ;CLOSE OK?
RNZ ;..YES, RETURN
CALL ERXIT ;..NO, ABORT
DB '++CAN''T CLOSE FILE++',CR,LF,'$'
;
;----> RDSECT: READS A SECTOR
;
;FOR SPEED, THIS ROUTINE BUFFERS UP 16
;SECTORS AT A TIME.
;
RDSECT LDA SECINBF ;GET # SECT IN BUFF.
DCR A ;DECREMENT..
STA SECINBF ;..IT
JM RDBLOCK ;EXHAUSTED? NEED MORE.
LHLD SECPTR ;GET POINTER
LXI D,BASE+80H ;TO DATA
CALL MOVE128 ;MOVE TO BUFFER
SHLD SECPTR ;SAVE BUFFER POINTER
RET ;FROM "READSEC"
;
;BUFFER IS EMPTY - READ IN ANOTHER BLOCK OF 16
;
RDBLOCK LDA EOFLG ;GED EOF FLAG
CPI 1 ;IS IT SET/
STC ;TO SHOW EOF
RZ ;GOT EOF
MVI C,0 ;SECTORS IN BLOCK
LXI D,DBUF ;TO DISK BUFFER
RDSECLP PUSH B
PUSH D
MVI C,STDMA ;SET DMA..
CALL BDOS ;..ADDR
LXI D,FCB
MVI C,READ
CALL BDOS
POP D
POP B
ORA A ;READ OK?
JZ RDSECOK ;YES
DCR A ;EOF?
JZ REOF ;GOT EOF
;
;READ ERROR
;
CALL ERXIT
DB '++FILE READ ERROR',CR,LF,'$'
;
RDSECOK LXI H,80H ;ADD LENGTH OF ONE SECTOR...
DAD D ;...TO NEXT BUFF
XCHG ;BUFF TO DE
INR C ;MORE SECTORS?
MOV A,C ;GET COUNT
CPI 16 ;DONE?
JZ RDBFULL ;..YES, BUFF IS FULL
JMP RDSECLP ;READ MORE
;
REOF MVI A,1
STA EOFLG ;SET EOF FLAG
MOV A,C
;
;BUFFER IS FULL, OR GOT EOF
;
RDBFULL STA SECINBF ;STORE SECTOR COUNT
LXI H,DBUF ;INIT BUFFER..
SHLD SECPTR ;..POINTER
LXI D,BASE+80H ;RESET..
MVI C,STDMA ;..DMA..
CALL BDOS ;..ADDR
JMP RDSECT ;PASS SECT TO CALLER
;
;----> WRSECT: WRITE A SECTOR
;
;WRITES THE SECTOR INTO A BUFFER. WHEN 16
;HAVE BEEN WRITTEN, WRITES THE BLOCK TO DISK.
;
;ENTRY POINT "WRBLOCK" FLUSHES THE BUFFER AT EOF.
;
WRSECT LHLD SECPTR ;GET BUFF ADDR
XCHG ;TO DE FOR MOVE
LXI H,BASE+80H ;FROM HERE
CALL MOVE128 ;MOVE TO BUFFER
XCHG ;SAVE NEXT..
SHLD SECPTR ;..BLOCK POINTER
LDA SECINBF ;BUMP THE..
INR A ;..SECTOR #..
STA SECINBF ;..IN DHE BUFF
CPI 16 ;HAVE WE 16?
RNZ ;NO, RETURN
;
;----> WRBLOCK: WRITES A BLOCK TO DISK
;
WRBLOCK LDA SECINBF ;# SECT IN BUFFER
ORA A ;0 MEANS END OF FILE
RZ ;NONE TO WRITE
MOV C,A ;SAVE COUNT
LXI D,DBUF ;POINT TO DISK BUFF
;
DKWRLP PUSH H
PUSH D
PUSH B
MVI C,STDMA ;SET DMA
CALL BDOS ;TO BUFFER
LXI D,FCB ;THEN WRITE
MVI C,WRITE ;..THE..
CALL BDOS ;..BLOCK
POP B
POP D
POP H
ORA A
JNZ WRERR ;OOPS, ERROR
LXI H,80H ;LENGTH OF 1 SECT
DAD D ;HL= NEXT BUFF
XCHG ;TO DE FOR SETDMA
DCR C ;MORE SECTORS?
JNZ DKWRLP ;..YES, LOOP
XRA A ;GET A ZERO
STA SECINBF ;RESET # OF SECTORS
LXI H,DBUF ;RESET BUFFER..
SHLD SECPTR ;..POINTER
;
RSDMA LXI D,BASE+80H ;RESET..
MVI C,STDMA ;..DMA..
CALL BDOS ;..ADDR
RET
;
WRERR CALL RSDMA ;RESET DMA TO NORM.
MVI C,CAN ;CANCEL..
CALL SEND ;..SENDER
CALL ERXIT ;EXIT W/MSG:
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 $ ;RECEIVE W/GARBAGE DELETE
IN MODDATP ;GET A CHAR
IN MODDATP ;..TOTALLY PURGE UART
;
RECV PUSH D ;SAVE
;
IF FASTCLK ;4MHZ?
MOV A,B ;GET TIME REQUEST
ADD A ;DOUBLE IT
MOV B,A ;NEW TIME IN B
ENDIF
;
MSEC LXI D,50000 ;1 SEC DCR COUNT
;
IF NOT DCH
MWTI IN MODCTLP ;CHECK STATUS
ENDIF
;
IF DCH
MWTI IN MODCTL2 ;CHECK STATUS
ENDIF
;
ANI MODRCVB ;ISOLATE BIT
CPI MODRCVR ;READY?
JZ MCHAR ;GOT CHAR
DCR E ;COUNT..
JNZ MWTI ;..DOWN..
DCR D ;..FOR..
JNZ MWTI ;..TIMEOUT
DCR B ;MORE SECONDS?
JNZ MSEC ;YES, WAIT
;
;MODEM TIMED OUT RECEIVING
;
POP D ;RESTORE D,E
STC ;CARRY SHOWS TIMEOUT
RET
;
;GOT CHAR FROM MODEM
;
MCHAR IN MODDATP ;READ THE CHAR
POP D ;RESTORE DE
;
;CALC CHECKSUM
;
PUSH PSW ;SAVE THE CHAR
ADD C ;ADD TO CHECKSUM
MOV C,A ;SAVE CHECKSUM
POP PSW ;RESTORE CHAR
ORA A ;CARRY OFF: NO ERROR
RET ;FROM "RECV"
;
;----> SEND: SEND A CHARACTER TO THE MODEM
;
SEND PUSH PSW ;SAVE THE CHAR
ADD C ;CALC CKSUM
MOV C,A ;SAVE CKSUM
;
IF NOT DCH
SENDW IN MODCTLP ;GET STATUS
ENDIF
;
IF DCH
SENDW IN MODCTL2 ;GET STATUS
ENDIF
;
ANI MODSNDB ;ISOLATE READY BIT
CPI MODSNDR ;READY?
JNZ SENDW ;..NO, WAIT
POP PSW ;GET CHAR
OUT MODDATP ;OUTPUT IT
RET ;FROM "SEND"
;
;----> WAITNAK: WAITS FOR INITIAL NAK
;
;TO ENSURE NO DATA IS SENT UNTIL THE RECEIVING
;PROGRAM IS READY, THIS ROUTINE WAITS FOR THE
;THE FIRST TIMEOUT-NAK FROM THE RECEIVER.
;(E) CONTAINS THE # OF SECONDS TO WAIT.
;
WAITNAK MVI B,1 ;TIMEOUT DELAY
CALL RECV ;DID WE GET..
CPI NAK ;..A NAK?
RZ ;YES, SEND BLOCK
DCR E ;80 TRIES?
JZ ABORT ;YES, ABORT
JMP WAITNAK ;NO, LOOP
;
;----> MOVEFCB: MOVES FCB(2) TO FCB
;
;I ATTEMPTED TO MAKE THE MODEM COMMAND 'NATURAL',
;I.E. MODEM SEND FILENAME (MODEM S FN.FT) RATHER
;THAN MODEM FILENAME SEND (MODEM FN.FT S) SO THIS
;ROUTINE MOVES THE FILENAME FROM THE SECOND FCB
;TO THE FIRST
;
MOVEFCB LXI H,FCB+16 ;FROM
LXI D,FCB ;TO
MVI B,16 ;LEN
CALL MOVE ;DO THE MOVE
XRA A ;GET 0
STA FCBSNO ;ZERO SECTOR #
STA FCBEXT ;..AND EXTENT
RET
;
CTYPE PUSH B ;SAVE..
PUSH D ;..ALL..
PUSH H ;..REGS
MOV E,A ;CHAR TO E
MVI C,WRCON ;GET BDOS FNC
CALL BDOS ;PRIN THE CHR
POP H ;RESTORE..
POP D ;..ALL..
POP B ;..REGS
RET ;FROM "CTYPE"
;
HEXO PUSH PSW ;SAVE FOR RIGHT DIGIT
RAR ;RIGHT..
RAR ;..JUSTIFY..
RAR ;..LEFT..
RAR ;..DIGIT..
CALL NIBBL ;PRINT LEFT DIGIT
POP PSW ;RESTORE RIGHT
;
NIBBL ANI 0FH ;ISOLATE DIGIT
CPI 10 ;IS IS <10?
JC ISNUM ;YES, NOT ALPHA
ADI 7 ;ADD ALPHA BIAS
;
ISNUM ADI '0' ;MAKE PRINTABLE
JMP CTYPE ;..THEN TYPE IT
;
;----> ILPRT: INLINE PRINT OF MSG
;
;THE CALL TO ILPRT IS FOLLOWED BY A MESSAGE,
;BINARY 0 AS THE END.
;
ILPRT XTHL ;SAVE HL, GET HL=MSG
;
ILPLP MOV A,M ;GET CHAR
ORA A ;END OF MSG?
JZ ILPRET ;..YES, RETURN
CALL CTYPE ;TYPE THE MSG
INX H ;TO NEXT CHAR
JMP ILPLP ;LOOP
;
ILPRET XTHL ;RESTORE HL
RET ;PAST MSG
;
;----> ERXIT: EXIT PRINTING MSG FOLLOWING CALL
;
ERXIT POP D ;GET MESSAGE
MVI C,PRINT ;GET BDOS FNC
CALL BDOS ;PRINT MESSAGE
;
EXIT LHLD STACK ;GET ORIGINAL STACK
SPHL ;RESTORE IT
RET ;--EXIT-- TO CP/M
;
;MOVE 128 CHARACTERS
;
MOVE128 MVI B,128 ;SET MOVE COUNT
;
;MOVE FROM (HL) TO (DE) LENGTH IN (B)
;
MOVE MOV A,M ;GET A CHAR
STAX D ;STORE IT
INX H ;TO NEXT "FROM"
INX D ;TO NEXT "TO"
DCR B ;MORE?
JNZ MOVE ;..YES, LOOP
RET ;..NO, RETURN
;
;TEMPORARY STORAGE AREA
;
RCVSNO DB 0 ;SECT # RECEIVED
SECTNO DB 0 ;CURRENT SECTOR NUMBER
ERRCT DB 0 ;ERROR COUNT
;FOLLOWING 3 USED BY DISK BUFFERING ROUTINES
EOFLG DB 0 ;EOF FLAG (1=TRUE)
SECPTR DW DBUF
SECINBF DB 0 ;# OF SECTORS IN BUFFER
DS 60 ;STACK AREA
STACK DS 2 ;STACK POINTER
;
;16 SECTOR DISK BUFFER
;
DBUF EQU $ ;16 SECTOR DISK BUFFER
;
; 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 ;SET DMA
BDOS EQU BASE+5
FCB EQU BASE+5CH ;SYSTEM FCB
FCBEXT EQU FCB+12 ;FILE EXTENT
FCBSNO EQU FCB+32 ;SECTOR #
FCB2 EQU BASE+6CH ;SECOND FCB
;
END