TITLE RECEIVE -- PROGRAM TO DOWNLOAD CP/M FILES SUBTTL by Dr. Rebecca Thomas, updated 08 FEB 82 DTD ; ;************************************************************************ ;* * ;* CP/M FILE RECEIVE 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 * ;* * ;************************************************************************ ; .COMMENT* This file RECEIVE 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 receiver to be informed by the transmitter that the end-of-file has been received so it can then close the file and return to the operating system. This is necessary in order to provide for submit files to transfer multiple unambiguously 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). * .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 TRUE MPUB EQU FALSE SIO45 EQU FALSE VECT67 EQU FALSE MOROHD EQU FALSE ALTOS EQU FALSE PBM EQU FALSE HEATH EQU FALSE ; ; Set to true for 'wildcard' assemblies ; WILCAR EQU TRUE ; ; ; Address constants: ; IF HEATH BOOT EQU 4200 ELSE BOOT EQU 0000 ; WARM BOOT JUMP POINT ENDIF BDOS EQU BOOT+05 ; BDOS ENTRY POINT FCB EQU BOOT+5C ; DEFAULT FILE CONTROL BLOCK 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 MSEC SOFTWARE DELAY LOOP CONSTANT ; ; BDOS Function numbers: ; RDRF EQU 03 ; READER INPUT FUNCTION -- FOR PBM PUNF EQU 04 ; PUNCH OUTPUT FUNCTION -- FOR PBM CONIOF EQU 06 ; DIRECT CONSOLE I/O FUNCTION PRINTF EQU 09 ; DISPLAY ASCII STRING UNTIL $ CLOSEF EQU 10 ; CLOSE FILE FUNCTION DELETF EQU 13 ; DELETE FILE FUNCTION WRITEF EQU 15 ; SEQUENTIAL WRITE FILE FUNCTION MAKEF EQU 16 ; MAKE OR CREATE FILE FUNCTION SETDMA EQU 1A ; SET DMA FUNCTION ; ; Conditional assembly constructs: ; IF APPLE2 ; APPLE ][ MEMORY MAPPED SLOT NO.2 ; RMASK EQU 01 ; RECEIVE READY MASK TMASK EQU 02 ; TRANSMIT READY MASK STR_LN EQU 02 ; INITIALIZATION STRING LENGTH STAT EQU 0E0A0 ; STATUS PORT ADDRESS DATA EQU 0E0A1 ; DATA PORT ADDRESS ; 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 STAT EQU 05 ; SERIAL STATUS PORT 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 ; NO INITIALIZATION STRING 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 ; 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 ; over the serial RS-232 data link. 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 COMMAND BYTE TO ACIA ELSE ; OR OUT (STAT),A ; SEND TO USART 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 USART INITIALIZATION ENDIF ; IF APPLE2 ; ISTRNG: DB 03, 011 ; RESET AND 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 character received is returned in the "A" register. This code ; alse monitors the keyboard for the program abort command (^C). ; INPUT: ; PUSH BC ; SAVE FROM CHANGE PUSH DE PUSH HL ; INLOOP: ; ; First monitor keyboard for the abort (^C) command. This is ; so the program doesn't "hangup" in the input loop. ; CALL CONIN ; MONITOR KEYBOARD AND 7FH CP CTRL_C ; ABORT COMMAND? JP Z,ABORT ; YES, RETURN TO CP/M ; ; Then wait until the receive buffer has a character. ; 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, WAIT UNTIL IT IS ; YES, FALL THROUGH ENDIF ; OF IF PBM ; ; Enter here when the receive buffer is full. ; ; IF NOT PBM IF APPLE2 LD A,(DATA) ; GET DATA BYTE FROM ACIA ELSE ; OR IN A,(DATA) ; GET BYTE OF DATA FROM USART ENDIF ENDIF ; ; Now return the character just received to the transmitter ; for verification and to provide for handshaking with software. ; LD E,A ; MOVE FOR OUTPUT ROUTINE CALL OUTPUT ; SEND BACK TO TRANSMITTER ; POP HL POP DE POP BC ; RESTORE VALUE RET ; FROM INPUT ; ;************************************************************************ ; ; This is the character transmit routine for the serial port. ; Enter this routine with the character to send in "E". Both the ; "AF" and "BC" registers are conserved. This code also monitors ; the keyboard for the program abort command (^C). ; OUTPUT: PUSH HL PUSH DE PUSH BC ; SAVE FROM CHANGE PUSH AF ; OUTLP: ; ; First monitor the keyboard for the abort (^C) command. ; CALL CONIN ; MONITOR KEYBOARD CP CTRL_C ; ABORT COMMAND? JP Z,ABORT ; YES, EXIT TO CP/M ; ; Then wait until the transmitter buffer is empty. ; IF PBM LD C,PUNF ; E ALREADY HAS CHAR CALL BDOS 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 ; ; Enter here when the transmitter can send the character. ; IF APPLE2 LD (DATA),A ; SEND DATA BYTE TO ACIA ELSE ; OR OUT (DATA),A ; TRANSMIT DATA BYTE TO USART ENDIF ENDIF ; OF IFBM POP AF ; RESTORE POP BC ; 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". Both the "DE" and "HL" registers are conserved. ; CONIN: ; PUSH HL ; SAVE FROM CHANGE ON STACK PUSH DE ; SAVE ALSO 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 VALUES FROM STACK POP HL ; RESTORE ALSO RET ; FROM CONIN ; ; 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 LD C,CONIOF ; GET DIRECT CONSOLE I/O FUNCTION CALL BDOS ; SEND TO CONSOLE POP AF ; RESTORE RET ; FROM CALL ; ; 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 have placed a copy of the ; filename in the default DMA buffer (at 0080H). ; IF NOT WILCAR 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 ; DISPLAY MESSAGE AND QUIT ENDIF ; ; Initialize the ACIA or USART. ; CALL INIT ; INITIALIZE SERIAL INTERFACE LD DE,SIGNON CALL PRMSG ; ; Set baud rate in software if MPUB. ; IF MPUB CALL BAUD ; SET BAUD RATE TO 9600 ENDIF ; ; Then establish the communication link with the transmitter. ; Wait until the UPLOAD program transmits an 'R' (for "Ready?"). ; RDYLP: CALL INPUT ; MONITOR SERIAL PORT CP 'R' ; READY TO SEND DATA? JP NZ,RDYLP ; NO, WAIT UNTIL WE ARE ; ; Now send a "S" (for "Set") to the transmitting computer to inform it that ; we are ready to receive the data record. ; LD E,'S' ; GET AN ASCII 'S' (FOR SET) CALL OUTPUT ; SEND OVER SERIAL LINK ; ; First we make sure that the DMA is set to the default buffer. ; LD C,SETDMA ; GET SET DMA FUNCTION LD DE,DEFDMA ; GET DEFAULT BUFFER ADDRESS CALL BDOS ; SET DAM TO DEFAULT BUFFER ; ; If wilcar is on, get the filename from the interface. Otherwise it ; is already in the FCB. ; IF WILCAR GETFIL: LD HL,FCB ; POINT TO FCB TO RECEIVE FILENAME LD (HL),0 ; FORCE DEFAULT DISK SELECTION INC HL ; SET UP TO RECEIVE NAME LD C,0 ; INIT THE CHECKSUM LD B,0B ; LENGTH OF THE FILENAME GETF1: CALL INPUT ; PUTS FILE NAME IN BUFFER AND COMPUTES CHECKSUM IN C CP EOFCHR ; RCVLP RETURNS EOF IF RECEIVED BEFORE COUNT=0 JP Z,EXIT10 ; END PROGRAM IF NO FILES ADD A,A ADD A,A ADD A,A ADD A,A ; CONVERT FROM HEX AND 0F0H LD D,A CALL INPUT CP EOFCHR JP Z,EXIT10 AND 0FH OR D LD (HL),A XOR C ; COMPUTE CHECKSUM LD C,A INC HL DEC B JP NZ,GETF1 CALL INPUT ; GET THE CHECKSUM IN A -- INPUT SAVES BC ADD A,A ADD A,A ADD A,A ADD A,A AND 0F0H LD D,A CALL INPUT AND 0FH OR D CP C ; CHECKSUM OK? JP Z,GOODFL ; YES -- DO RECEIVE FILE LD E,'B' ; SEND NOGO CHAR TO TRANSMITTER CALL OUTPUT JP GETFIL ; AND RETRY AUTO UNTIL EOF GOODFL: LD E,'G' ; SEND GO CHAR CALL OUTPUT LD HL,FCB+0C ; CLEAR EXTENT AND DISKMAP AREAS LD B,17 GOODLP: LD (HL),0 INC HL DEC B JP NZ,GOODLP ENDIF ; ; Next delete the specified file if it already exists in the directory. ; LD C,DELETF ; DELETE FILE FUNCTION LD DE,FCB ; POINT TO FCB PUSH DE ; SAVE ON STACK CALL BDOS ; DELETE ANY FILE BY THIS NAME POP DE ; RESTORE ; ; Then create (and open the file). ; LD C,MAKEF ; GET MAKE FILE FUNCTION CALL BDOS ; CREATE NEW FILE BY THIS NAME ; ; Examine error code returned by BDOS. If "-1" is returned then ; the directory was full already. ; INC A ; SUCCESSFUL? LD DE,NDSMSG ; POINT TO ERROR MESSAGE IN CASE JP Z,EXIT ; NO, DISPLAY "INSUFFICIENT DIRECTORY SPACE" ; AND ABORT PROGRAM ; ; Display the message indicating that reception is in progress. ; LD DE,WRKMSG ; POINT TO MESSAGE "Receiving file:" CALL PRMSG ; DISPLAY ON RECEIVING CONSOLE LD HL,FCB+1 ; FORMAT THE FILENAME FOR DISPLAY LD DE,MESBUF ; LD B,8 MOVNAM: LD A,(HL) CP 20 JP Z,SPACE LD (DE),A INC DE SPACE: INC HL DEC B JP NZ,MOVNAM LD A,(HL) CP 20 JP Z,SHOWNM LD A,'.' LD (DE),A INC DE LD B,3 MOVEXT: LD A,(HL) LD (DE),A INC HL INC DE DEC B JP NZ,MOVEXT SHOWNM: LD A,'$' LD (DE),A LD DE,MESBUF CALL PRMSG ; Now wait until the transmitter sends a "G" (for "Go"). ; If the transmitter has detected an end-of-file condition ; then it will send the end-of-file character (^Z) instead. ; GETGEE: CALL INPUT ; MONITOR SERIAL LINK CP EOFCHR ; END-OF-FILE? JP Z,EOF ; YES, GO CLOSE FILE AND EXIT CP 'G' ; NO, DATA RECORD COMING? JP NZ,GETGEE ; NO, WAIT UNTIL IT IS ; YES, FALL THROUGH RECEIVE RECORD ; ; First reset the DMA buffer pointer to the start of the default ; buffer. Set the count for a 128-byte record and initialize the ; checksum storage to zero. ; RCVREC: ; LD HL,DEFDMA ; POINT TO START OF RECEIVE BUFFER LD C,00 ; INITIALIZE CHECKSUM TO ZERO LD B,80 ; COUNT = 128 BYTES IN BUFFER ; ; This is the receive data loop which controls storing of the ; 128-byte CP/M data received over the serial link into the DMA buffer. ; RCVLP: ; CALL INPUT ; GET DATA FROM SERIAL PORT ADD A,A ; FILE IS IN HEX ADD A,A ADD A,A ADD A,A ; MOVE TO HIGH POSITION AND 0F0H ; STRIP ASCII LD D,A ; SAVE HIGH NIBBLE CALL INPUT ; GET LOW NIBBLE AND 0FH ; CONVERT TO BINARY OR D ; FORM BYTE LD (HL),A ; STORE IN BUFFER XOR C ; COMPUTE CHECKSUM LD C,A ; AND UPDATE INC HL ; ADVANCE BUFFER POINTER DEC B ; COUNT ONE BYTE RECEIVED JP NZ,RCVLP ; FILL ENTIRE BUFFER ; ; Now get the checksum sent from the transmitter and compare it ; to the one just computed on the received data. If they match ; go get the next record. If not, display an error message and ; give the operator the chance to abort the program. ; CALL INPUT ; GET THE CHECKSUM ADD A,A ; BUILD BYTE ADD A,A ADD A,A ADD A,A AND 0F0H LD D,A CALL INPUT AND 0FH OR D CP C ; MATCH? JP Z,GOODRD ; YES, GOOD READ ; ; There was a checksum compare error. Get the operators attemtion ; and give the operator the chance to abort the program or attempt ; to send the same data record again. ; LD E,BELL ; NO, GET THE BELL CHARACTER CALL CONOUT ; AND GET THE OPERATORS ATTENTION ; ; Inform the transmitter that there was a checksum compare error. ; LD E,'B' ; GET THE BAD READ CODE CALL OUTPUT ; AND SEND TO UPLOAD DEVICE ; ; Display prompt message. ; LD DE,RVERMSG ; POINT TO THE ERROR MESSAGE CALL PRMSG ; DISPLAY "RECEPTION ERROR, ; Try to receive last record again (Y/N)? ; ; Get and analyze the response. ; ANSLP: CALL CONIN ; MONITOR KEYBOARD OR A ; KEYPRESS? JP Z,ANSLP ; NO, WAIT UNTIL THERE IS ; LD E,A ; MOVE TO DISPLAY CALL CONOUT ; ON CONSOLE ; CP 'Y' ; YES, TRY AGAIN? JP Z,GETGEE ; YES, TRY RECEIVING AGAIN ; NO, FALL THROUGH TO ABORT ROUTINE ; ; This is the program abort routine, entered if an affirmative ; response is given to the ABORT PROGRAM? question. ; ABORT: ; LD DE,ABRTMSG ; POINT TO ABORT MESSAGE CALL PRMSG ; DISPLAY "PROGRAM ABORTED!" JP EXIT20 ; AND GO BACK TO CP/M ; ; Enter here if the computed checksum agrees with that send by UPLOAD. ; GOODRD: ; ; First display a dot on the receiver console for a good record read. ; LD E,DOT ; GET ASCII DOT CHARACTER CALL CONOUT ; DISPLAY ON CONSOLE ; ; Now write the buffer to disk. ; LD DE,FCB ; POINT TO FILE CONTROL BLOCK LD C,WRITEF ; GET BDOS SEQUENTIAL WRITE FUNCTION CALL BDOS ; WRITE RECORD TO DISK ; ; Check the error code returned by the BDOS. If non-zero then ; the disk was full so display an error message and exit program. ; OR A ; WRITE OPERATION SUCCESSFUL? LD DE,FULMSG ; POINT TO ERROR MESSAGE IN CASE NOT JP NZ,EXIT ; NO, DISPLAY "WRITE ERROR, DISK FULL!" ; AND EXIT TO CP/M ; ; Write operation successful so send a "G" back to transmitter ; to indicate that the checksums matched. ; LD E,'G' ; GET "G" FOR GO CALL OUTPUT ; TELL TRANSMITTER-RECEPTION WAS O.K. JP GETGEE ; MONITOR SERIAL LINK ; ; 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: ; LD DE,EOFMSG ; POINT TO END-OF-FILE MESSAGE CALL PRMSG ; DISPLAY ; ; Last thing is to close the file. ; LD C,CLOSEF ; GET CLOSE FILE FUNCTION LD DE,FCB ; POINT TO FILE CONTROL BLOCK CALL BDOS ; CLOSE THE FILE ; ; Check to see that closure was successful. If not then the filename ; was not found in the directory. Switched disks? ; INC A ; ERROR "-1" BECOMES "0" LD DE,CLERMSG ; POINT TO ERROR MESSAGE IN CASE IF WILCAR JP Z,EXIT JP GETFIL ELSE JP NZ,EXIT10 ; ERROR FREE EXIT ENDIF ; FALL THROUGH TO DISPLAY CLOSE ERROR ; MESSAGE AND EXIT TO CP/M ; ; Enter here to exit program if not necessarily end-of-file. ; EXIT: ; CALL PRMSG ; DISPLAY MESSAGE POINTED TO BY "DE" ; EXIT10: LD DE,DONMSG ; POINT TO SIGN-OFF MESSAGE CALL PRMSG ; DISPLAY "EXITING Program" ; EXIT20: LD HL,(SYSSTK) ; GET SYSTEM STACK POINTER LD SP,HL ; AND RESTORE BEFORE LEAVING JP BOOT ; BACK TO CP/M ; ; ;************************************************************************ ;* * ;* Console Message Area * ;* * ;************************************************************************ ; ; CMDMSG: DB CR,LF DB 'NO FILE NAME SPECIFIED!$' ; NDSMSG: DB CR,LF DB 'INSUFFICIENT DIRECTORY SPACE!$' ; FULMSG: DB CR,LF DB 'WRITE ERROR, DISK FULL!$' ; WRKMSG: DB CR,LF DB 'Receiving File: $' ; RVERMSG:DB CR,LF DB 'RECEPTION ERROR, Try to receive last record again (Y/N)?$' ; CLERMSG:DB CR,LF DB 'FILENAME NOT IN DIRECTORY!$' ; ABRTMSG:DB CR,LF DB 'PROGRAM ABORTED!$' ; EOFMSG: DB CR,LF DB 'DOWNLOAD Complete$' ; DONMSG: DB CR,LF DB 'EXITING Program$' SIGNON: DB CR,LF IF ALTOS DB 'ALTOS' ENDIF IF APPLE2 DB 'APPLE' ENDIF IF HEATH DB 'HEATHKIT' ENDIF IF MPUB OR SIO45 DB 'IMSAI' ENDIF IF MOROHD DB 'MORROW' ENDIF IF PBM DB 'IOBYTE' ENDIF IF VECT67 DB 'VECTOR GRAPHICS' ENDIF DB ' RECEIVE UTILITY$' ; ;************************************************************************ ;* * ;* Variable Storage and Local Stack * ;* * ;************************************************************************ ; DS 20 ; 32-BYTE LOCAL PROGRAM STACK STACK: DS 01 ; STACK GROWS DOWNWARD FROM HERE SYSSTK: DS 02 ; STORAGE FOR SYSTEM STACK POINTER MESBUF: DS 80 ; END ; OF PROGRAM