TITLE SEND -- PROGRAM TO UPLOAD CP/M FILES SUBTTL by Dr. Rebecca Thomas, updated 08 FEB 82 DTD ; ;************************************************************************ ;* * ;* CP/M FILE TRANSMIT PROGRAM * ;* * ;* Resident Computer: APPLE ][ * ;* * ;* assemble using Microsoft Macro-80 assembler (Ver. 3.36) * ;* and load with Link-80 (Ver. 3.37) * ;* * ;* by Dr. Rebecca Thomas, April 2, 1981 * ;* * ;************************************************************************ ; ; ; MODIFIED 2 OCT 81 BY D.T.D for wildcard files. ; 8 FEB 82 - DTD -- ADDED PBM CONDITIONAL .COMMENT* This file TRANSMIT utility is based on the communication protocol for intercomputer CP/M file transfer outlined in the Microsoft Z-80 Softcard documentation (Vol. II, pp. 5-28). Modification has been made to allow the transmitter to inform the receiver that the end-of-file has been encountered so it can then close up the file and return to the operating system. This is necessary in order to provide for submit files to transfer multiple named files. This software allows transfer of CP/M files between two computers over a RS-232 serial link. In particular one computer can be an Apple ][ employing the California Computer Systems asynchronous serial card (CCS 7710A) placed in expansion slot 2. In addition, there are provisions in this software for supporting the IMSAI MPUB and SIO45 serial ports as well as the Vector Graphics system. Other systems can be easily included in this program if the various port configurations for the transmitting computer, the port addresses, ready masks, and initialization strings (if any) are defined by conditional assembly constructs. This software will support handshaking up to 9600 baud (the fastest yet tested). Also submit files may be used on both computers for automatic transfer of multiple unambiguously named files (no wildcards). If the wildcard option is selected, the file or files will be transmitted with the 11 character filename as found in the FCB sent first followed by a checksum. The wildcard switch in the receive module will cause the file name to be used which was received from this program instead of any specified on the command line. * .Z80 ; Z80 MNEMONICS ; BUT 8080 COMPATIBLE CODE .RADIX 16 ; HEXADECIMAL BASE ASEG ; ABSOLUTE SEGMENT ; ; Boolean definitions: ; FALSE EQU 00 TRUE EQU NOT FALSE ; ; Set only one of the following TRUE: ; APPLE2 EQU FALSE MPUB EQU FALSE SIO45 EQU FALSE VECT67 EQU FALSE MOROHD EQU TRUE ALTOS EQU FALSE PBM EQU FALSE HEATH EQU FALSE ; ; Set to true to do wild cards ; WILCAR EQU TRUE ; DTD 10/2/81 ; ; Address constants: ; IF HEATH BOOT EQU 4200 ; ORG 4200 HEATHKIT ELSE BOOT EQU 0000 ; WARM BOOT JUMP POINT ENDIF BDOS EQU BOOT+5 ; BDOS ENTRY POINT FCB EQU BOOT+5C ; DEFAULT FILE CONTROL BLOCK CRREC EQU BOOT+7C ; FCB CURRENT RECORD NUMBER LOCATION DEFDMA EQU BOOT+80 ; DEFAULT DMA BUFFER TBASE EQU BOOT+100 ; BASE OF TRANSIENT PROGRAM AREA ; ; Other constants: ; CTRL_C EQU 'C'-40 ; CONTROL-C (USED TO EXIT PROGRAM) BELL EQU 'G'-40 ; CONTROL-G (BELL CODE) CR EQU 0D ; CARRIAGE RETURN LF EQU 0A ; LINE FEED EOFCHR EQU 'Z'-40 ; CONTROL-Z (END-OF-FILE CODE) DOT EQU '.' ; ASCII DOT CHARACTER MSCNT EQU 0F9 ; 1 MS. SOFTWARE DELAY LOOP CONSTANT ; ; BDOS Function numbers: ; RDRF EQU 03 ; READER FUNCTION -- FOR PBM PUNF EQU 04 ; PUNCH " " " CONIOF EQU 06 ; DIRECT CONSOLE I/O FUNCTION PRINTF EQU 09 ; DISPLAY ASCII STRING UNTIL $ OPENF EQU 0F ; FILE OPEN FUNCTION READF EQU 14 ; SEQUENTIAL READ FUNCTION SETDMA EQU 1A ; SET DMA ADDRESS FUNCTION ; ; Conditional assembly constructs: ; IF APPLE2 ; APPLE ][ WITH CCS CARD IN SLOT NO.2 ; RMASK EQU 01 ; RECEIVE READY MASK TMASK EQU 02 ; TRANSMIT READY MASK STR_LN EQU 02 ; INITIALIZATION STRING LENGTH DATA EQU 0E0A1 ; MEMORY MAPPED DATA PORT STAT EQU 0E0A0 ; MEMORY MAPPED STATUS PORT ENDIF ; ; IF VECT67 ; VECTOR GRAPHICS SERIAL CHANNEL C ; TMASK EQU 01 ; TRANSMIT READY MASK RMASK EQU 02 ; RECEIVE READY MASK STR_LN EQU 06 ; INITIALIZATION STRING LENGTH DATA EQU 06 ; SERIAL DATA PORT ADDRESS STAT EQU 07 ; SERIAL STATUS PORT ADDRESS ENDIF ; IF SIO45 OR MPUB ; IMSAI SIO PORTS 4,5 OR MPUB PORTS ; TMASK EQU 01 ; TRANSMIT READY MASK RMASK EQU 02 ; RECEIVE READY MASK STR_LN EQU 06 ; INITIALIZATION STRING LENGTH DATA EQU 04 ; SERIAL DATA PORT ADDRESS STAT EQU 05 ; SERIAL STATUS PORT ADDRESS ENDIF ; IF MPUB ; FOR IMSAI MPUB SOFTWARE BAUD-RATE SET ; MPUCTL EQU 0F3 ; MPUB CONTROL PORT MPUON EQU 40 ; TURN-ON CODE MPUOFF EQU 0C0 ; TURN-OFF CODE TIMMOD EQU 36 ; TIMER MODE TIMCTL EQU 0D103 ; TIMER CONTROL PORT TIMCNT EQU 0D100 ; TIMER COUNT PORT BAUDRT EQU 000D ; BAUD RATE COUNT FOR 9600 ENDIF ; IF MOROHD ; FOR MORROW HARD-DISK SYSTEMS ; TMASK EQU 20 ; TRANSMIT READY MASK RMASK EQU 01 ; RECEIVE READY MASK STR_LN EQU 00 ; INITIALIZATION STRING LENGTH DATA EQU 48 ; SERIAL DATA PORT ADDRESS STAT EQU 4D ; SERIAL STATUS PORT ADDRESS GRPCTL EQU 4F ; GROUP CONTROL PORT ADDRESS GRP3 EQU 03 ; SERIAL LINK IS GROUP 3 ENDIF ; IF ALTOS ; FOR ALTOS AUXILLIARY SERIAL PORT ; ASSUMING DTR WIRED HIGH TMASK EQU 04 ; TRANSMIT READY MASK RMASK EQU 01 ; RECEIVE READY MASK STR_LN EQU 00 ; INITIALIZATION STRING LENGTH DATA EQU 1E ; SERIAL DATA PORT ADDRESS STAT EQU 1F ; SERIAL STATUS PORT ADDRESS SETINP EQU 00 ; VALUE TO INITIALIZE INPUT SETOUT EQU 10 ; VALUE TO INITIALIZE OUTPUT ENDIF IF HEATH TMASK EQU 40 RMASK EQU 1 STR_LN EQU 0 DATA EQU 0E0 STAT EQU 0E5 ENDIF ; ;************************************************************************ ; ORG TBASE ; START AT BEGINNING OF TPA ; START: JP ENTRY ; BEGIN PROGRAM ; ;************************************************************************ ;* * ;* Program Subroutines * ;* * ;************************************************************************ ; ; ; This routine initializes the ACIA or USART for communication with ; another CP/M-based 8080 or Z80 computer. The command string should ; set the serial port for x16 clock, 8 data bits, 2 stop bits, ; no parity check, and enable both transmitter and receiver ; sections of the serial interface. ; INIT: IF MOROHD OR ALTOS OR PBM OR HEATH ; NO INITIALIZATION FOR MORROW HARD DISK RET ; OR ALTOS SYSTEMS ELSE ; LD HL,ISTRNG ; POINT TO INITIALIZATION STRING LD B,STR_LN ; GET COMMAND STRING LENGTH INIT10: LD A,(HL) ; GET COMMAND BYTE FROM STRING ; IF APPLE2 ; LD (STAT),A ; SEND TO ACIA STATUS PORT ELSE OUT (STAT),A ; SEND TO USART STATUS PORT ENDIF ; ; Delay 1 mS between sending command bytes to serial interface. ; LD DE,0001 ; COUNT = 1 MSEC DELAY CALL DELAY ; EXECUTE DELAY ; INC HL ; POINT TO NEXT ELEMENT OF STRING DEC B ; COUNT ONE BYTE SENT JP NZ,INIT10 ; SEND ENTIRE STRING RET ; FROM SERIAL INTERFACE INITIALIZATION ENDIF ; IF APPLE2 ; ISTRNG: DB 03, 011 ; RESET THEN PROGRAM (SEE ABOVE) ENDIF ; ; IF VECT67 ; ISTRNG: DB 0, 0, 0, 40, 0CE, 027 ENDIF ; IF SIO45 OR MPUB ; ISTRNG: DB 0, 0, 0, 40, 0CE, 037 ENDIF ; ;************************************************************************ ; ; This routine sets the baud rate of the MPUB. ; IF MPUB ; BAUD: ; LD A,MPUON ; GET CODE TO SET-UP MPUB OUT (MPUCTL),A ; SWITCH MPUB TO SET-UP MODE LD A,TIMMOD ; GET TIMER MODE CODE LD (TIMCTL),A ; SET TIMER MODE LD A,BAUDRT ; GET BAUD-RATE CODE LD (TIMCNT),A ; SET BAUD RATE XOR A ; CLEAR LD (TIMCNT),A ; LD A,MPUOFF ; GET CODE TO LEAVE SET-UP MODE OUT (MPUCTL),A ; FINISHED BAUD-RATE SETUP RET ; FROM BAUD ; ENDIF ; OF CONDITIONAL ASSEMBLY FOR MPUB ; ;************************************************************************ ; ; This is the character receive routine for the selected serial port. ; The received character is returned in "A" upon return from this routine. ; This code alse monitors the keyboard for the program abort command (^C). ; INPUT: ; PUSH BC ; SAVE ON STACK PUSH DE ; PUSH HL ; INLOOP: ; ; First monitor the keyboard for the abort command (^C) in case ; the program might be "hanging" in the input loop. ; CALL CONIN ; MONITOR KEYBOARD CP CTRL_C ; ABORT COMMAND? JP Z,ABORT ; YES, EXIT TO CP/M ; ; Then wait until the receiver section of the serial interface ; has a character ready before attempting clear latch. ; IF PBM LD C,RDRF CALL BDOS AND 7FH ELSE IF MOROHD LD A,GRP3 ; SET GROUP 3 OUT (GRPCTL),A ENDIF ; IF ALTOS LD A,SETINP ; WAKE-UP INPUT OUT (STAT),A ENDIF ; IF APPLE2 LD A,(STAT) ; GET STATUS BYTE FROM ACIA ELSE ; OR IN A,(STAT) ; GET STATUS BYTE FROM USART ENDIF ; AND RMASK ; RECEIVE BUFFER FULL? JP Z,INLOOP ; NO, GO MONITOR SERIAL PORT ; YES, FALL THROUGH ; ; Enter here when ready to obtain character. ; ; IF APPLE2 LD A,(DATA) ; GET DATA BYTE FROM ACIA ELSE ; OR IN A,(DATA) ; GET BYTE OF DATA FROM USART ENDIF ENDIF ; POP HL POP DE ; RESTORE POP BC ; RET ; FROM INPUT ; ;************************************************************************ ; ; This is the character transmit routine for the serial port. ; Enter with the character to send in "E". Both "AF" and "BC" ; registers are conserved. A call to CONIN as part of the loop ; monitors the keyboard for the abort (^C) command. ; OUTPUT: PUSH HL PUSH DE PUSH BC ; SAVE PUSH AF ; ; OUTLP: ; ; First monitor the keyboard for the abort command (^C) in case ; the program might hang in the output loop. ; CALL CONIN ; MONITOR KEYBOARD CP CTRL_C ; ABORT COMMAND? JP Z,ABORT ; YES, EXIT PROGRAM ; ; Next wait until the transmitter buffer is empty to send ; the next character. ; IF PBM PUSH DE LD C,PUNF CALL BDOS POP DE ELSE IF MOROHD LD A,GRP3 ; SET GROUP 3 OUT (GRPCTL),A ENDIF ; IF ALTOS LD A,SETOUT ; WAKE-UP OUTPUT OUT (STAT),A ENDIF ; IF APPLE2 LD A,(STAT) ; GET STATUS BYTE FROM ACIA ELSE ; OR IN A,(STAT) ; GET STATUS BYTE FROM USART ENDIF ; AND TMASK ; TRANSMITTER BUFFER EMPTY? JP Z,OUTLP ; NO, WAIT UNTIL IT IS LD A,E ; YES, GET CHARACTER TO SEND ; ; When ready, transmit the character. ; IF APPLE2 LD (DATA),A ; SEND CHARACTER TO ACIA ELSE ; OR OUT (DATA),A ; TRANSMIT DATA BYTE TO USART ENDIF ENDIF ; ; Now wait until the receiver sends the same character back ; as a verification that it was received correctly. This also ; provides for handshaking in software. Also the keyboard is ; monitored for the abort command (^C) so that the program ; won't hang here. ; WAITRT: ; CALL CONIN ; MONITOR KEYBOARD CP CTRL_C ; ABORT COMMAND? JP Z,ABORT ; YES, BACK TO CP/M ; CALL INPUT ; MONITOR SERIAL LINK ; YES, CONTINUE ; POP AF ; RESTORE REGISTERS POP BC ; BEFORE LEAVING POP DE POP HL RET ; FROM OUTPUT ; ;************************************************************************ ; ; This is the console input routine. It employs a call to the BDOS ; direct console I/O function. If a character is ready it will be ; returned in the accumulator, otherwise a null character (00H) is ; returned in "A". The contents of the "DE" and "HL" registers ; is conserved. ; CONIN: ; PUSH HL ; SAVE FROM CHANGE PUSH DE ; SAVE LD C,CONIOF ; GET DIRECT CONSOLE I/O FUNCTION LD E,0FF ; SET UP FOR CHARACTER INPUT CALL BDOS ; GET CHARACTER IN "A" POP DE ; RESTORE VALUE OF "DE" POP HL ; RESTORE VALUE OF "HL" RET ; FROM CONIN ROUTINE ; ; This is the console output routine. It employs a call to the BDOS ; direct console I/O function. Note that the character to be displayed ; must be in the "E" register before calling this routine. ; CONOUT: ; PUSH AF ; SAVE FROM CHANGE LD C,CONIOF ; GET DIRECT CONSOLE I/O FUNCTION CALL BDOS ; SEND TO CONSOLE POP AF ; RESTORE RET ; FROM CONOUT ROUTINE ; ; This is the display message routine which calls the BDOS ; string display function. ; PRMSG: LD C,PRINTF ; CONSOLE DISPLAY FUNCTION CALL BDOS ; DISPLAY STRING UNTIL "$" RET ; FROM PRMSG ; ;************************************************************************ ; ; This routine provides a delay of 1 msec times the value in the ; "DE" register when this subroutine is entered. ; DELAY: ; PUSH HL ; SAVE PUSH BC ; PUSH AF ; LD B,MSCNT ; GET 1 MSEC DELAY LOOP CONSTANT DLY10: LD A,B ; INITIALIZE DLY20: SUB 01 ; COUNT 1 OR A ; HAS ONE MSEC EXPIRED? JP NZ,DLY20 ; NO, CONTINUE UNTIL IT HAS DEC DE ; YES, COUNT ONE MSEC LD A,E ; GET REMAINING DELAY OR D ; TIMED OUT? JP NZ,DLY10 ; NO, CONTINUE MSEC COUNT POP AF ; RESTORE POP BC ; POP HL ; RET ; FROM DELAY ; ; ;************************************************************************ ;* * ;* Main Program Area * ;* * ;************************************************************************ ; ; The main program begins here: ; ENTRY: ; ; First set up a local program stack. ; LD HL,0000 ; CLEAR FOR ADDITION ADD HL,SP ; GET CURRENT STACK POINTER VALUE LD (SYSSTK),HL ; SAVE UNTIL EXITING PROGRAM LD HL,STACK ; POINT TO LOCAL STACK LD SP,HL ; AND RESET STACK TO LOCAL AREA ; ; This code checks to see if a filename was specified when this ; program was invoked. If so, the CCP will place a copy of the ; filename in the default DMA buffer (at 0080H). ; LD A,(DEFDMA) ; SOURCE FILE NAME SPECIFIED? OR A ; ANY CHARACTERS IN COMMAND LINE? LD DE,CMDMSG ; GET ERROR MESSAGE IN CASE JP Z,EXIT ; NO, DISPLAY MESSAGE AND QUIT ; ; Sign on with the installation we're assembled for ; LD DE,SIGNON CALL PRMSG ; ; Initialize the ACIA or USART. ; CALL INIT ; INITIALIZE SERIAL INTERFACE ; ; Set baud rate in software if MPUB. ; IF MPUB CALL BAUD ; SET BAUD RATE TO 9600 ENDIF ; ; If wildcards are working, we must copy the default DMA area ; which contains the command line into the command buffer to ; keep it from being destroyed by directory searches, etc. ; This is being done because we added the feature where if ; we don't find a file, we print the command line on the console. ; IF WILCAR ; DTD 10/05/81 LD HL,DEFDMA LD DE,COMBUF LD BC,80 CALL COPY ; ; The CCP has left us a model FCB in 5CH which has wildcards expanded. ; This model must be saved for later directory searches. ; LD HL,FCB LD DE,MODEL LD BC,24 ; 36 decimal=24 hex CALL COPY ENDIF ; ; Then establish the communication link with the receiver. ; Send a stream of ASCII 'R's (for "Ready?") until the RECEIVE ; program responds. ; RDYLP: ; LD E,'R' ; GET ASCII 'R' CALL OUTPUT ; SEND OVER SERIAL LINK ; ; Monitor the serial link for the response "S" (for "Set"). ; CALL INPUT ; GET REPLY (WAIT UNTIL THERE IS ONE) CP 'S' ; RECEIVE COMPUTER READY? JP NZ,RDYLP ; NO, TRY AGAIN ; YES, FALL THROUGH ; ; Set DMA to default buffer area: ; LD C,SETDMA ; GET BDOS SET DMA FUNCTION LD DE,DEFDMA ; GET DEFAULT BUFFER ADDRESS CALL BDOS ; INSURE DMA IS THE DEFAULT BUFFER ; ; Search for the first (or only) occurrence of the model FCB ; IF WILCAR LD C,11 ; COMMAND TO SEARCH FIRST LD DE,MODEL ; FOR THE FILE (WITH WILDCARDS) IN MODEL CALL BDOS ; ; If there aren't any files, ACCU wil be 255d. Return to CP/M. ; CP 0FF ; ANY FILES BY THAT NAME? JP Z,EXIT1 ; NO - RETURN TO CP/M AFTER ERROR MESSAGE ; ; If there are files, ACCU contains 0, 1, 2, or 3 indicating which 32 byte ; FCB from the DMA buffer is the right one. Therefor, ACCU*32+DMAADDR ; points to an OPEN FCB for this file. Note that it is open because we ; just read the actual FCB from the disk. The disk map is inherently ; already filled in. ; NEXTFL: ADD A,A ; *2 ADD A,A ; *4 ADD A,A ; *8 ADD A,A ; *16 ADD A,A ; *32 ; ; Cheat a little -- as long as we're using the default DMA buffer at 80H ; there is an easy way to ADD this to HL to prepare for copying the real ; FCB into the default FCB area. ; ADD A,80 LD L,A ; + DMAADDR LD H,0 LD DE,FCB ; THIS OPENS THE FILE LD BC,20 ; COPY REAL FCB TO 5CH CALL COPY ; ; Print "XMITTING FILE: FILENAME.EXT" to the console ; ; First build the file name in message buffer. ; LD B,0 LD C,8 LD HL,MESBUF LD DE,FCB ; ; Here DE are pointer to the real FCB, HL will point to MESBUF ; and (C) will count number of bytes checked in FCB. ; ; Check for default disk - if default, don't need to print disk name LD A,(DE) OR A JP Z,NODRIV ; ; Not the default - so print the drive name followed by a colon ; ADD A,40H ; CONVERT TO ASCII LD (HL),A ; SAVE IT IN THE MESBUF INC HL ; NEXT BUFFER LOCATION LD (HL),':' ; PUT A COLON THERE INC HL ; ; Now move the file name. Extract spaces between name and ext, and ; insert a period there. ; NODRIV: INC DE ; POINT PAST AUTO-DRIVE AND TO FRIST BYTE OF NAME FILOP: LD A,(DE) ; GET A BYTE OF THE FILENAME CP 20 ; SPACE? JP Z,SPACE ; SPACE OR C=0 TERMINATES THIS PART OF SCAN ; LD (HL),A ; FILENAME CHAR - MOVE IT TO OUTPUT BUFFER INC HL ; NEXT OUTPUT LOCATION SPACE: INC DE ; NEXT FCB LOCATION (SKIPS SPACES) DEC C ; NAME IS 8 CHARS -- COUNTED BY (C) JP NZ,FILOP ; ; ; Filename is in buffer with optional drive. Now check for an extension. ; DE point to first extension byte, HL to next output location. ; C is set to extension length. LD C,3 ; Is there an extension? ; LD A,(DE) ; GET THE FIRST EXTENSION BYTE CP 20H ; IF SPACE, THEN NO EXTENSION JP Z,NOEXT ; ; We have an extension. First put a period into the output buffer ; LD (HL),'.' ; EXTENSION SEPARATOR INC HL ; NEXT BUFFER LOCATION ; ; Now transfer the extension to the output buffer ; EXTLOP: LD (HL),A ; ON ENTRY A HAD FIRST BYTE - LOADED AGAIN AT END OF LOOP INC HL ; NEXT BUFFER LOC INC DE ; POINT TO NEXT LD A,(DE) ; GET NEXT BYTE DEC C ; MORE TO MOVE - NOTE THAT WERE MOVING SPACES JP NZ,EXTLOP ; MOVE ALL THREE BYTES ; ; Mark the end of the print buffer for PRMG ; NOEXT: LD (HL),'$' ; $ TERMINATES PRINT LD DE,SENMES ; PRINT THE "XMITTING" PROTION CALL PRMSG ; LD DE,MESBUF ; NOW THE FILE NAME CALL PRMSG ; ; Transmit the filename to the receiver and look for an OK. ; RETRYF: LD HL,FCB+1 ; SKIP THE AUTO DRIVE # LD C,0 ; THIS WILL BE THE CHECKSUM START LD B,0B ; AND THIS THE COUNT CALL XMTLP ; TRANSMIT IT CALL INPUT ; GET THE RESPONSE CP 'G' ; GOOD? JP Z,OPENIT ; YES -- GO ON AND SEND FILE LD E,BELL ; NO -- GIVE WARNING AND RETRY CALL CONOUT JP RETRYF ; SHOULD PROBABLY BE A QUERY HERE ; Now open the file ; OPENIT: LD HL,0 LD (FCB+0C),HL ; CLEAR EX AND S1 LD (FCB+0E),HL ; CLEAR S2 AND RC LD DE,FCB LD C,OPENF CALL BDOS ELSE ; Not a wildcard assembly. ; ; Then open the file specified. ; LD DE,FCB ; POINT TO FCB LD C,OPENF ; GET BDOS OPEN FILE FUNCTION CALL BDOS ; OPEN THE FILE ; ; Open successful? If not, the filename specified was not in the directory. ; INC A ; SUCCESSFUL FILE OPEN? LD DE,FNFMSG ; POINT TO ERROR MESSAGE IN CASE JP Z,EXIT ; NO, DISPLAY "FILE NOT FOUND" AND EXIT ; ; Display the console message indicating that transmission is in progress. ; LD DE,WRKMSG ; POINT TO MESSAGE "Uploading" CALL PRMSG ; DISPLAY ON TRANSMITTING CONSOLE ENDIF ; ; Before we read the first record, make sure that the current record ; number in the file control block is zero, else the sequential ; read will not start at the beginning of the file. ; SEND: XOR A ; GET A ZERO LD (CRREC),A ; CURRENT RECORD = 00 TO START ; ; Then read the next record from the disk into the default DMA ; buffer. ; READ: ; LD C,READF ; GET BDOS SEQUENTIAL READ FUNCTION LD DE,FCB ; POINT TO FILE CONTROL BLOCK CALL BDOS ; READ NEXT RECORD INTO DMA AREA ; ; Check the error code returned by BDOS. If not zero, then the end-of-file ; was encountered. ; OR A ; END-OF-FILE? JP NZ,EOF ; YES, GO PREPARE TO EXIT PROGRAM ; NO, FALL THROUGH TO TRANSMIT ROUTINE ; ; First display a dot on the transmitting console to indicate ; that a CP/M record has been read into the buffer. ; ; LD E,DOT ; GET A DOT TO DISPLAY CALL CONOUT ; ON THE TRANSMITTER CONSOLE ; ; Now send a "G" (for "Go") to the receiving computer to inform ; it that a data record will follow immediately. ; XMTBUF: ; LD E,'G' ; GET AN ASCII 'G' (FOR GO) CALL OUTPUT ; SEND OVER SERIAL LINK ; ; Now we transmit the contents of the DMA buffer over the serial ; link. The buffer pointer is reset to the beginning of the default ; DMA buffer, the counter set for a 128-byte record and the checksum ; storage reset to zero. ; LD HL,DEFDMA ; POINT TO THE RECORD TO SEND LD C,00 ; INITIALIZE CHECKSUM TO ZERO LD B,80 ; COUNT = 128 BYTES IN BUFFER IF WILCAR ; MAKE XMTLP A SUBR CALL XMTLP JP VRFYLP ENDIF ; ; This is the data transmit loop. The 128-byte CP/M data record is ; sent first. ; XMTLP: ; LD A,(HL) ; GET CHARACTER FROM BUFFER PUSH AF ; CONVERT TO HEX RRA ; HIGH NIBBLE FIRST RRA RRA RRA AND 0FH ; MAKE IT ASCII OR 30H ; BUT DONT CORRECT FOR A-F LD E,A ; MOVE TO SEND TO RECEIVER CALL OUTPUT ; SEND OVER SERIAL LINK POP AF ; NOW THE LOW NIBBLE PUSH AF XOR C ; COMPUTE CHECKSUM LD C,A ; AND UPDATE STORAGE POP AF AND 0FH ; NOW CONVERT TO ASCII OR 30H LD E,A CALL OUTPUT ; AND TRANSMIT INC HL ; POINT TO NEXT BYTE IN RECORD DEC B ; COUNT ONE BYTE SENT JP NZ,XMTLP ; SEND ALL 128-BYTES ; ; Then the checksum just computed for the transmitted data record ; is sent to the receiving computer. ; CHKSUM: LD A,C ; CONVERT CHECKSUM TO HEX RRA RRA RRA RRA AND 0FH OR 30H LD E,A ; HIGH NIBBLE FIRST CALL OUTPUT ; AND SEND IT LAST LD A,C AND 0FH OR 30H ; ASCII -- SORT OF LD E,A CALL OUTPUT IF WILCAR RET ENDIF ; ; Wait for verification from the receiver. If the checksum ; computed for the record just received matches the checksum ; sent at the end of the record, the transmitter will send back ; a "G" for good read. ; VRFYLP: ; CALL INPUT ; MONITOR SERIAL LINK CP 'G' ; A GOOD TRANSMISSION? JP Z,READ ; YES, SEND NEXT RECORD ; ; Since there was a transmission error the operator's attention ; is obtained by ringing the terminal bell (if any) and the ; opportunity to abort (and see what's wrong) is given. ; LD E,BELL ; NO, GET ASCII BELL CODE CALL CONOUT ; GET OPERATORS ATTENTION LD DE,XMERMSG ; POINT TO ERROR MESSAGE CALL PRMSG ; DISPLAY "TRANSMISSION ERROR, ; TRY TO SEND LAST RECORD AGAIN (Y/N)? ANSLP: CALL CONIN ; MONITOR KEYBOARD OR A ; ANY RESPONSE? JP Z,ANSLP ; NO, WAIT UNTIL THERE IS ; LD E,A ; MOVE TO ECHO CALL CONOUT ; ON CONSOLE ; CP 'Y' ; YES, ATTEMPT TO SEND AGAIN? JP Z,XMTBUF ; YES, TRY AGAIN ; NO, FALL THROUGH TO ABORT ROUTINE ; ; This is the program abort routine, entered either if a ^C is ; entered from the keyboard during the INPUT or OUTPUT loop or ; if a non-negative response is given to the ABORT? question. ; ABORT: ; LD DE,ABRTMSG ; POINT TO ABORT MESSAGE CALL PRMSG ; DISPLAY "PROGRAM ABORTED!" JP EXIT20 ; BACK TO CP/M ; ; This is the error free exit routine from the program. This code ; is entered when an end-of-file is detected during the disk read. ; EOF: ; IF WILCAR LD E,EOFCHR ; CLOSE FILE ON RECEIVE SIDE CALL OUTPUT ; ; Search for the next (if any) file. We must do that by first ; looking for the last file xmitted using the SEARCH FIRST ; system call, and then using SEARCH NEXT using the model FCB ; ; ; First position pointer to current file in directory ; and set extent byte to zero so we start where we left off. XOR A LD (FCB+0C),A ; Extent byte LD C,11 LD DE,FCB CALL BDOS ; ; Now put the model FCB into the area used by the search next function ; LD HL,MODEL LD DE,FCB LD BC,20 CALL COPY LD C,12 ; SEARCH NEXT CALL BDOS CP 0FF ; NOT FOUND? LD DE,ENDMES JP Z,EXIT JP NEXTFL ELSE LD DE,EOFMSG ; POINT TO END-OF-FILE MESSAGE ENDIF ; ; Enter here to exit program if not necessarily end-of-file. ; In all cases an end-of-file character is sent to the receiver ; so that it will also abort and exit to CP/M. ; EXIT: ; CALL PRMSG ; DISPLAY MESSAGE POINTED TO BY "DE" ; EXIT1: LD E,EOFCHR ; GET CONTROL-Z (END-OF-FILE CODE) CALL OUTPUT ; TELL RECEIVER TO CLOSE UP FILE ; AND EXIT TO CP/M ; LD DE,DONMSG ; POINT TO SIGN-OFF MESSAGE CALL PRMSG ; DISPLAY "EXITING Program" ; ; Insert a delay here before reboot so that this transmitter doesn't ; overrun the receiver when transmitting multiple submit files. ; A four second delay is provided by this routine. ; LD B,10 ; COUNT IS SIXTEEN LOOPS EXIT10: LD DE,00FF ; DELAY = 250 MS. CALL DELAY ; DELAY 1/4 SECOND DEC B ; COUNT ONE LOOP JP NZ,EXIT10 ; CONTINUE FOR ALL LOOPS ; EXIT20: LD HL,(SYSSTK) ; GET SYSTEM STACK POINTER LD SP,HL ; AND RESTORE BEFORE LEAVING JP BOOT ; BACK TO CP/M ; ; COPY -- COPY FROM (HL) TO (DE) FOR (BC) BYTES. NO REGS SAVED ; COPY: LD A,(HL) LD (DE),A INC HL INC DE DEC BC LD A,B OR C RET Z JP COPY ; ; ;************************************************************************ ;* * ;* Console Message Area * ;* * ;************************************************************************ ; ; SIGNON: IF ALTOS DB CR,LF,'ALTOS' ENDIF IF APPLE2 DB CR,LF,'APPLE' ENDIF IF HEATH DB CR,LF,'HEATH' ENDIF IF MPUB OR SIO45 DB CR,LF,'IMSAI' ENDIF IF MOROHD DB CR,LF,'MORROW' ENDIF IF PBM DB CR,LF,'IOBYTE' ENDIF IF VECT67 DB CR,LF,'VECTOR GRAPHICS' ENDIF DB ' TRANSMIT UTILITY$' CMDMSG: DB CR,LF DB 'NO FILE NAME SPECIFIED!$' ; ENDMES: DB CR,LF DB 'END OF TRANSMISSION$' ; FNFMSG: DB CR,LF DB 'FILE NOT FOUND!$' ; WRKMSG: DB CR,LF DB ' Uploading$' ; SENMES: DB CR,LF DB 'XMITING FILE: $' ; XMERMSG:DB CR,LF DB 'TRANSMISSION ERROR, Try to send last record again (Y/N)?$' ; ABRTMSG:DB CR,LF DB 'PROGRAM ABORTED!$' ; EOFMSG: DB CR,LF DB 'End-of-File Encountered$' ; DONMSG: DB CR,LF DB 'EXITING Program$' ; ;************************************************************************ ;* * ;* Variable Storage and Local Stack * ;* * ;************************************************************************ ; DS 30 ; 32-BYTE LOCAL PROGRAM STACK STACK: DS 01 ; STACK GROWS DOWNWARD FROM HERE SYSSTK: DS 02 ; STORAGE FOR SYSTEM STACK POINTER COMBUF: DS 80 ; COMMAND LINE STORAGE MODEL: DS 24 HEXFLG: DB 1 MESBUF: DS 80 ; END ; OF PROGRAM