; KMD23.ASM - KMD CP/M FILE TRANSFER PROGRAM - 01/11/88 ; ; by ; Irvin M. Hoff ; copyrighted (c) 1985, 1986, 1987, 1988 ; ; ; This program assembles with ASM, LASM, M80, MAC, RMAC or SLRMAC. ; If using M80, remove the ";;" at the beginning of the ASEG line. ; ;; ASEG ; Needed for M80, ignore for other assemblers ; ;======================================================================= ; ; This is an universal CP/M-80 file transfer program that gets ; its I/O (and time clock, if used) information from the BYE5 ; program itself - thus no hardware specific or time clock over- ; lays are needed, and no overlay libraries need to be retained. ; It uses the XMODEM protocol with either CRC or checksum. It ; supports automatic (or manual) 1k protocol. It has KMD batch ; mode that is compatible with YMODEM batch other than does not ; use date in Julian or time in octal. It is based on a 1979 ; program called XMODEM by Keith Petersen, which was adapted from ; Ward Christensen's 1978 MODEM2 program. Many of the routines ; in the version used had been added by the the authors of this ; program. ; - Irv Hoff, W6FFC ; (415) 948-2166 (voice) ; (415) 948-2513 PRACSA RCPM ; ;======================================================================= ; ; Read the KMD23.UPD file to see what this and past versions have added. ; It provides you with valuable data on understanding and using the many ; support files that are available, such as FOR, NEW, KFOR, KNEW, KMDEL, ; etc. ; ;======================================================================= ; ; TO USE: ; ------ ; 1) Edit the KMD23OVL.ASM overlay for desired options. (The ones most- ; changed are marked with an asterisk '*' in the comment field). ; 2) Assemble to a .HEX file. ; 3) Use MLOAD.COM to merge KMD23.COM with your new KMD23OVL.HEX file: ; B>MLOAD KMD.COM=KMD23.COM,KMD23OVL.HEX. ; 4) Finished, ready to use, requires BYE5 (uses its I/O and time clock) ; or a slightly modified XMODEM overlay if not using BYE5. ; ; ; Selecting options: ; ----------------- ; Options that are most often changed are marked with ';*' at the start ; of the comment line for that option. Simple systems not using time ; clocks, user logs, etc. will keep most of those 'NO'. RCPM systems ; running the usual bulletin board systems, etc., will change most of ; those to 'YES'. ; ; When finished changing whatever options are desired, use your normal ; assembler to get a .HEX file of this overlay. Then use MLOAD.COM to ; merge into the KMD.COM file with your new changes: ; ; A>MLOAD KMD.COM=KMD23.COM,KMD23OVL.HEX ; ; MLOAD.COM is included with this overlay file, but SID.COM or DDT.COM ; may also be used to merge the KMD23OVL.HEX file with KMD23.COM if you ; then save the correct number of memory pages as the new KMD.COM file. ; ; - Notes by Irv Hoff W6FFC ; ;====================================================================== ; YES EQU 0FFH NO EQU 0 ; CR EQU 0DH LF EQU 0AH ; ; ORG 0100H ; JMP 0400H ; ; ;----------------------------------------------------------------------- ; ; Options - change to suit your system, then assemble to get a .HEX file ; ;----------------------------------------------------------------------- ; MHZ: DB 4 ;*Clock speed, use integer (2,4,5,8, etc.) MSPEED: DW 003CH ; Location of BYE5's modem speed indicator ; ;----------------------------------------------------------------------- ; ; Normal disk systems can transfer 16k from computer to disk in 2-3-4 ; seconds and less. Some very slow 5-1/4" floppy systems (such as North ; Star) may take up to 20-30 seconds to transfer 16k. This would cause ; several timeouts at 10 seconds each. If you experience any timeouts, ; change the BUFSIZ to somethng smaller, perhaps 8k or even 4k. ; BUFSIZ: DB 16 ; File transfer buffer size in Kbytes ; ;----------------------------------------------------------------------- ; DESCRIB:DB yes ;*Yes, asks for a description of uploaded file DRIVE: DB 'A' ; Drive area for description of upload USER: DB 14 ; User area for description of upload ; ;----------------------------------------------------------------------- ; DRIVMAX:DW 003DH ; Location of MAXDRIV byte USRMAX: DW 003FH ; Location of MAXUSER byte WHEEL: DW 003EH ;*Location of ZCMD or ZCPR2 wheel byte. If ; using if ZCPR3, run SHOW.COM to find correct ; address for the WHEEL byte. ;----------------------------------------------------------------------- ; ; The following will all be available for the SYSOP's personal use when ; the wheel byte is set in local non-zero (0FFH) mode. If not using a ; wheel byte, select manually with a YES. ; NOCOMR: DB NO ; NO = change .COM to .OBJ and .PRL to .OBP NOCOMS: DB no ;*Permit sending .COM files? NOLBS: DB NO ; Permit sending .??# files? NOSYS: DB NO ; Permit sending .SYS files? ; ;----------------------------------------------------------------------- ; ZCPR: DB yes ;*Yes, if using ZCMDR or ZCPR with WHEEL byte ; implemented. If Yes, .NDR, .RCP and .SYS ; files not received. ;----------------------------------------------------------------------- ; ; Allows drive/user area to be specified for downloading. If using ZCMD ; or ZCPR, set USEMAX 'YES'. Then the answers to MAXDRV and MAXUSR are ; ignored. ; USEMAX: DB yes ;*Yes ZCMD or ZCPR for DRIVMAX & USRMAX values ; No to use MAXDRV and MAXUSR specified next ; ; If USEMAX above is set YES for automatic operation the following two ; are ignored. If set NO, the following will be used. ; MAXDRV: DB 4 ; Number of disk drives used MAXUSR: DB 12 ; Maximum user area allowed ; ;----------------------------------------------------------------------- ; ; Selects the drive/user area for uploading private files for the SYSOP. ; This permits experimental files, replacement files and proprietary ; programs to be sent to the SYSOP. ; PRDRV: DB 'B' ; Private drive for SYSOP to receive file PRUSR: DB 15 ; Private user area for SYSOP to receive file ; ;----------------------------------------------------------------------- ; ; Selects the drive/user area for downloading private files from the ; SYSOP. This permits him to put a special file in this area, then leave ; a private note to that person mentioning the name of the file and to ; use "SP". Although anybody could download that program, they don't ; know what (if any) files are there. A high degree of security exists, ; while the SYSOP still has the ability to make special files available. ; Thus any person can be a temporary "privileged user". (Wildcards and ; batch are not allowed, for what should be obvious reasons.) ; SPLDRV: DB 'B' ; Special drive area for downloading SYSOP files SPLUSR: DB 15 ; Special user area for downloading SYSOP files ; ;----------------------------------------------------------------------- ; ; Allows uploading to be done on a specified driver and user area so all ; viewers (including the SYSOP) can readily find the latest entries. ; SETAREA:DB YES ; Yes, if using designated area to receive files DRV: DB 'B' ;*Drive to receive file on USR: DB 0 ; User area to receive file in ; ;----------------------------------------------------------------------- ; MSDOS: DB yes ;*Yes if using separate area for MS-DOS files IBMDRV: DB 'D' ; Drive to upload MS-DOS, NEW and FOR files IBMUSR: DB 0 ; User area for IBM files NEWLST: DB YES ; Yes to put MSDOS "NEW" and "FOR" lists on new ; listing, NO to keep them on CP/M lists NIBMDR: DB 'A' ; Drive to store the IBM 'FOR' and KMD.LOG on NIBMUS: DB 13 ; User area to store the IBM 'FOR' and KMD.LOG ; ;----------------------------------------------------------------------- ; MSGFIL: DB NO ; Yes if supporting message files, No if not ; (Not supported by most BBS systems) ;----------------------------------------------------------------------- ; ; Set the following equate YES, if your BBS software sets BYE5's LCPTR ; bit-mapped flag register to restrict user's ability to download files. ; This is used on systems that require at least an occassional upload to ; allow the user to continue numerous downloads. See BYE5.DOC for a de- ; scription of how to implement the LCPTR byte. ; ; Bit-5 KMD downloads allowed? (0 = NO, 1 = YES) ; RESUSR: DB yes ;*YES, if restricting downloads ; ;----------------------------------------------------------------------- ; XCANCL: DB YES ; Cancel file transfer with 3 or more CTL-X's ; ;----------------------------------------------------------------------- ; ; A few spares for possible future additions so this overlay will not be ; obsolete at that time. ; SPARES1:DB 0,0 ; ;----------------------------------------------------------------------- ; logging options ; ; File transfer logging options ; LOGCAL: DB yes ;*Yes, logs KMD transfers LOGDRV: DB 'A' ; Drive to place 'KMD.LOG' file LOGUSR: DB 14 ; User area to put 'KMD.LOG' file LASTDRV:DB 'A' ; Drive 'LASTCALR???'file is on LASTUSR:DB 14 ; User area of 'LASTCALR???' file LCNAME: DB 1 ; Column # where the caller's name starts in ; LASTCALR, normally column one. Some BBS ; systems start in col. 12 if CLOCK is YES. NAMELEN:DB 2 ;*Number of names in LASTCALR for a user, some ; BBS systems allow 3 names. (John H. Smith) ; Select 2 or 3, accordingly - you can use a ; larger number (like 50) if you want to use ; up to the first CR or LF in LASTCALR. CPM3: DB NO ; Yes if using CP/M 3.0 and LOGCAL is Yes ; ; end of logging options ;----------------------------------------------------------------------- ; start of TIMEON area ; CLOCK: DB yes ;*If YES, you must have clock code installed in ; BYE5 that sets RTCBUF with time/date and ; time-on-system. Status and MXTIME are also ; picked up from BYE5. DTOS: DB NO ;*Yes to display "time on system" messages EDATE: DB NO ; Yes to show dd/mm/yy vice mm/dd/yy in KMD.LOG TIMEON: DB yes ;*Yes to police time-on-system with BYE5 ; ; NOTE: If ZCPR = YES and WHEEL byte is set, send time is unlimited. If ; TIMEON is YES, unlimited time is allowed if MAXMIN in BYE5 is 0. ; Set CLOCK and TIMEON the same way in BYE5 and KMD. Select your ; preference and set MAXMIN in BYE5. Suggest using 60 if CLOCK ; and TIMEON are YES, and 45 if CLOCK is NO. See examples below. ; ; TIME 300 BPS 1200 BPS 1k 2400 BPS 1k ; ------ ------- ------------ ------------ ; 30 min 48.7k 180k 200k 320k 380k ; 45 min 73.1k 270k 300k 480k 570k ; 60 min 97.5k 360k 400k 640k 760k ; CREDIT: DB no ;*Yes to credit upload time to BYE5's MXTIME ; LOGLDS: DB no ;*Count number of up/down loads since login. ; Your BBS program can check UPLDS and DNLDS ; when user logs out and update either the ; user's file or a file for this purpose. ; You can either modify your BBS entry program ; to check the LASTCALR file before updating ; it and then update (risky), or make a sepa- ; rate program that BYE calls when logging ; off a user (preferred). ; UPLDS: DW 0054H ; Clear these values to Zero from your BBS pro- DNLDS: DW 0055H ; gram when somebody logs in. NOTE: Clear ; ONLY when a user logs in. Not when he re- ; enters the BBS program from CP/M. ; ; end of TIMEON area ;----------------------------------------------------------------------- ; ; File descriptors, change as desired if this list is not suitable. ; Move the line with the terminating '$' up, if fewer descriptors are ; desired. There are 129 extra bytes available. Be sure to terminate ; before address 0250h. (Can check with a .PRN file if getting close.) ; FILDES: DB CR,LF,' 0) - ''C'' ' ; (Extra space needed for M80) DB CR,LF,' 1) - CP/M' DB CR,LF,' 2) - dBASE' DB CR,LF,' 3) - MS-DOS' DB CR,LF,' 4) - PASCAL' DB CR,LF,' 5) - RCPM/BBS' DB CR,LF,' 6) - ZCPR3' DB CR,LF,' 7) - Other' DB CR,LF,' 8) - (spare)' DB CR,LF,' 9) - (spare)' DB CR,LF,'$' ; GUIDE: DS 0 ; MUST QUIT PRIOR TO 0250H ;..... ; ORG 0250H ; ; This section selects the text used when sending files to different ; portions of the disk. Not used unless the MSDOS option is set YES. ; There are 175 byes available (including those shown.) Must terminate ; before address 0300h. (Can check with a .PRN file if getting close.) ; SELECT: DB CR,LF,' 1 for 8-bit (CP/M, Apple, general)' DB CR,LF,' 2 for 16-bit (IBM, Macintosh, Atari, ' DB 'Amiga)' DB CR,LF,'$' ; GUIDE1: DS 0 ; ;----------------------------------------------------------------------- ; ; PROGRAM STARTS HERE ; ;----------------------------------------------------------------------- ; VERSION EQU 2 MODLEV EQU 3 ; ; ORG 0300H ; JMP BEGIN ; ; ; This is the I/O patch area. Assemble the appropriate I/O patch file ; for your modem (insure it starts at 0200h, not 0100h), then integrate ; into this program via DDT.COM, SID.COM or MLOAD.COM. The jumps are ; shown as zero, which aborts to a warm boot if the overlay has not been ; added to the program yet. End all I/O routines with a RET instruction. ; Room is allowed for an overlay up to 256 bytes. ; CONOUT: JMP 0000H ; See 'CONOUT' discussion above MINIT: JMP 0000H ; Initialization routine (if needed) UNINIT: JMP 0000H ; Undo whatever MINIT did (or return) MDOUTP: JMP 0000H ; Send character (via POP PSW) MDCARCK:JMP 0000H ; Test for carrier MDINP: JMP 0000H ; Receive data byte GETCHR: JMP 0000H ; Get character from modem MDINST: JMP 0000H ; Check receive ready (A - ERRCDE) MDOUTST:JMP 0000H ; Check send ready SPEED: JMP 0000H ; Get speed value for transfer time EXTRA1: JMP 0000H ; Extra for custom routine EXTRA2: JMP 0000H ; Extra for custom routine EXTRA3: JMP 0000H ; Extra for custom routine ;..... ; ; ORG 0400H ; JMP BEGIN ; ; ; The following does not show when the program is operated, being added ; for reference purposes. ; NOTICE: DB 0,0,'Copyrighted (c) 1985, 1986, 1987, 1988 ' DB 'by Irvin M. Hoff - v23 - 01/11/88',0 ; ; ; Save CP/M stack, initialize new one for KMD and check to see if ; BYE is available before continuing. ; BEGIN: LXI H,0 DAD SP SHLD STACK ; Save current return to CCP address LXI SP,STACK ; Reset the stack ; ; ; Save the current D/U area ; MVI C,32 MVI E,255 CALL 5 STA OLDUSR ; Save for now MVI C,25 CALL 5 STA OLDDRV LDA DRV STA CPMDRV ; ; ; Check for BYE5, wrecks current user area if not present ; MVI C,32 MVI E,241 CALL 5 ; See if BYE is running CPI 77 STA BYE5 JZ BEGIN2 ; BYE5 is present, exit ; ; ; No BYE5, restore the original user area ; LDA OLDUSR MVI C,32 MOV E,A CALL 5 ; ; ; No BYE5, see if an I/O overlay has been added at 0200h ; LDA MDINP+2 ; Check for MDINP address ORA A JNZ BEGIN1 ; If not zero, overlay was added MVI C,9 LXI D,NOBYE CALL 5 LHLD STACK SPHL RET ;... ; ; NOBYE: DB 13,10,'BYE5 unavailable and no I/O overlay ' DB 'has been added - aborting',13,10,7,'$' ;... ; ; ; If using an overlay in place of BYE5, then cannot limit downloads, ; use the clock or find total time, all of which depend on addresses ; established by BYE5 at signon time. ; BEGIN1: XRA A STA BYE5 STA CLOCK STA RESUSR STA TIMEON ; ; ; Check to see if CONOUT address was included in overlay, if not, put ; one in based on normal BIOS addresses ; LDA CONOUT+2 ORA A JNZ BEGIN2 ; Not zero, already has an address, exit LHLD 0000H+1 LXI D,9 ; Index into CRT output jump address DAD D SHLD CONOUT+1 ; BEGIN2: MVI C,75 MVI E,1 CALL CKBDOS ; Set the WRTLOC flag if using BYE5 LDA RESUSR ORA A JZ BEGIN3 MVI C,85 ; Access flags byte MVI E,255 CALL CKBDOS ; Byte returned in 'A' STA AFBYTE ; Store it ; ; ; See if DISKLOG is activated and store answer if yes ; BEGIN3: LDA BYE5 ORA A JZ BEGIN4 MVI C,86 ; Bye DISKLOG status request MVI E,0FFH ; ask for status CALL 5 CPI 77 ; 77 says 'no is set in BYE5' JZ BEGIN4 ; Exit with no change to DISKFLG ORA A ; says DISKLOG is there but turned off JZ BEGIN4 ; Exit wit no change to DISKFLG STA DSKFLG ; DISKLOG is in use, so set flag first MVI C,86 ; BYE5 DISKLOG status request MVI E,0 ; Now turn off the DISKLOG in BYE5 CALL 5 ; BEGIN4: LDA TIMEON ORA A JNZ BEGIN5 LDA CLOCK ORA A JZ BEGIN6 ; BEGIN5: CALL KTIME ; Get user's time and status from BYE ; BEGIN6: CALL ILPRT DB 13,10,'KMD v',VERSION+'0',MODLEV+'0',' (c)',0 ;..... ; ; ; Gobble up garbage characters from the line prior to receive or send ; CALL CATCH ; ; ; Check option for send or receive, first see if special Sysop character ; (which turns off the DESCRIB, LOGON, REUSR, CLOCK, TIMEON, CREDIT and ; LOGLDS options if used when the WHEEL byte is set) was typed. ; LXI H,93 MOV A,M ; Get the main option CPI 'I' ; Special Sysop character? STA KIND JNZ BEGIN7 PUSH H LHLD WHEEL ; See if the WHEEL byte is set MOV A,M POP H ORA A JZ OPTERR ; If not, show help guide XRA A STA DESCRIB STA LOGCAL STA RESUSR STA CLOCK STA TIMEON STA CREDIT STA LOGLDS INX H MOV A,M ; BEGIN7: CPI 76 JNZ BEGIN8 MVI M,65 MOV A,M ; BEGIN8: STA OPTSAV ; Save it for later use STA CRCFLG ; Insure in CRC mode now LDA LOGCAL ORA A MOV A,M JZ BEGIN9 STA LOGOPT ; Save for the station's log file ; BEGIN9: CPI 'A' ; For .ARC, .ARK or .LBR file JZ CHKSND CPI 'S' ; To send a normal file? JZ CHKSND CPI 'R' ; Going to receive a file? JNZ OPTERR ; None of these, show help guide ; CALL GTSPD1 JC CKROPT ; If less than 1200, skip 1k blocks STA KFLG ; Set the 1k flag for now ; ; ; Check for additional receive options ; CKROPT: INX H MOV A,M ; Get the receive option, if any CPI ' ' ; Next column a space character? JZ RCKBCH ; If yes, see if requesting batch CPI 'B' ; For batch mode JZ RCKBCH CPI 'C' ; Want checksum? JZ RCKSM CPI 'X' ; Want 128-character blocks? JZ R128 CPI 'P' ; Want a private upload? JZ SETPRV CPI 'M' ; Want a sequential message upload? JZ SETMSG JNZ OPTERR ; None of these, it's an error ; SETMSG: LDA MSGFIL ORA A JZ CKROPT STA MSGFLG ; Set the message flag ; SETPRV: STA PRVTFL ; Set the private flag ; ; ; Have asked for a private upload so check for "B", "C" or "X" request ; JMP CKROPT ; R128: XRA A ; Reset the 1k block flag STA KFLG JMP RCRC ; CHKSND: INX H ; Next space on command line MOV A,M ; Get the character CPI 'B' ; Requesting batch mode? JZ SBCH CPI 'X' ; Was it an 'X' for XMODEM protocol? JZ SNDFL ; If yes go send the file CPI 'P' JNZ CHKSND1 STA SPLFL ; Set the "send private" flag JMP CHKSND ; Check for any other requests ; CHKSND1:CPI 'K' ; A 'K' to force 1k transmissions? JNZ SNDFL ; If not, continue normally STA KFLG ; Else set the 1k flag CALL ILPRT DB ' 1k protocol enabled',0 JMP SNDFL ;..... ; ; ; Allows batch mode to private area if R, RB or RPB is typed ; RCKBCH: LDA 109 ; Was a file requested CPI ' ' JNZ RCRC ; Can't use batch if file requested CALL BCHMSG ; Show batch enabled message JMP RCVFL ;..... ; ; RCRC: MVI A,1 STA CRCFLG ; Show in CRC mode CALL ILPRT DB ' (CRC is enabled)',13,10,0 JMP RCVFL ; All set to receive a file, now ;..... ; ; RCKSM: XRA A STA CRCFLG STA KFLG ; Can't use 1k blocks with checksum CALL ILPRT DB ' Checksum enabled',13,10,0 JMP RCVFL ;..... ; ; ; Displays the Batch enabled message for send ; SBCH: LDA SPLFL ; Sending from the private area? ORA A JNZ SNDFL ; If yes, skip batch message INR A ; To set the batch flag to non-zero CALL BCHMSG ; Display the batch message JMP SNDFL ;..... ; ; BCHMSG: STA BCHFLG ; Set the batch flag LDA MSPEED CPI 5 JNC BCHMSG1 CALL ERXIT DB '++ Batch mode not available at 300 baud ++','$' ; BCHMSG1:CALL ILPRT DB ' Batch is enabled',13,10,0 RET ;..... ; ; ;----------------------------------------------------------------------- ; help guide ; Invalid option ; OPTERR: CALL ILPRT DB 13,10,0 ; OPTERR1:LHLD WHEEL MOV A,M ORA A JZ OPTERR2 CALL ILPRT DB 13,10,'++ (NOTE: WHEEL is on, Sysop can ' DB 'use an ''I'' for private KMD use) ++',0 ; OPTERR2:LDA SETAREA ORA A JNZ OPTERR3 CALL ILPRT DB 13,10 DB ' Uploads to current disk/user',0 JMP OPTERR4 ; OPTERR3:CALL ILPRT DB 13,10,' Normal uploads to ',0 LDA DRV CALL CTYPE LDA USR MVI H,0 MOV L,A CALL DECOUT MVI A,':' CALL CTYPE CALL ILPRT DB ' (',0 LDA DRV STA KDRV CALL KSHOW MVI A,')' CALL CTYPE ; OPTERR4:CALL ILPRT DB 13,10,' Private uploads to ',0 LDA PRDRV CALL CTYPE LDA PRUSR MVI H,0 MOV L,A CALL DECOUT MVI A,':' STA HLPFLG ; Set flag to finish the help guide CALL CTYPE LDA PRDRV ; Get private drive MOV B,A ; Save for now LDA DRV CMP B JZ OPTERR5 CALL ILPRT DB ' (',0 MOV A,B STA KDRV CALL KSHOW MVI A,')' CALL CTYPE ; OPTERR5:CALL ILPRT DB 13,10,0 LXI SP,STACK ; Reset the stack, just in case ; ; ; Menu of command examples shown if KMD is entered by itself or caused ; by a command line error. ; CALL ERXIT ; Exit with display DB '++ Examples: CTL-S to pause, anything else ' DB 'to abort ++',13,10,10 DB ' KMD A CAT.ARK CAT.COM to send a member ' DB 'file from an .ARK file',13,10 DB ' KMD A CAT.LBR CAT.COM to send a member ' DB 'file from a .LBR file',13,10 DB ' KMD A CAT CAT.COM .ARK or .ARC or ' DB '.LBR extent may be omitted',13,10 DB ' KMD R (or RB) to receive a ' DB 'batch file from remote user',13,10 DB ' KMD R HELLO.DOC ==>normal file ' DB 'upload from user to host',13,10 DB ' KMD RC HELLO.DOC tells host to ' DB 'receive file in checksum mode',13,10 DB ' KMD RP HELLO.DOC tells host to ' DB 'receive file in private area',13,10 DB ' KMD RPC HELLO.DOC to receive ' DB 'private file via checksum',13,10 DB ' KMD RX HELLO.DOC suspends KMD''s ' DB 'automatic 1k protocol request',13,10 DB ' KMD S HELLO.DOC ==>normal file ' DB 'download from host to user',13,10 DB ' KMD SB HELLO.* KMD-type batch ' DB 'send with 1k protocol',13,10 DB ' KMD SB HELLO.ARK CAT.LBR KMD-type batch ' DB 'send several files',13,10 DB ' KMD SK HELLO.DOC forces 1k ' DB 'protocol for MEX114 users',13,10 DB '$' ;..... ; ; ;====================================================================== ; ; ---> SNDFL DOWNLOAD (from USER to RCP/M System) ; ;====================================================================== ; ; The file specified in the KMD command line is transferred over the ; phone from the RCP/M system to another computer via modem using ; the "S" (send) option. The data is sent one record at a time, with ; headers and checksums and retransmissions on errors. ; SNDFL: LDA RESUSR ORA A JZ SNDOK LDA AFBYTE ANI 20H ; Test for download access (bit 5) JNZ SNDOK ; Yes CALL ERXIT DB 'You are not allowed to download files...','$' ; SNDOK: XRA A STA SNDFLG ; Show in send mode ; LDA MSDOS ; Separate area for MS-DOS files? ORA A JZ SNDOK1 ; Exit, if not ; LDA NEWLST ; Running more than one 'NEW' list? ORA A JZ SNDOK1 ; If not, ok now, so exit ; LDA IBMDRV ; Get the drive to store MS-DOS stuff MOV B,A ; Store in 'B' for now LDA OLDDRV ; Get the current drive ADI 65 ; Convert binary drive to ASCII CMP B ; Current drive less than that? JC SNDOK1 ; If yes, exit ; ; Using a separate drive/user area for IBM description and KMD.LOG files ; LDA NIBMDR ; Find drive for IBM use STA DRIVE ; Store for 'FOR' description file STA LOGDRV ; Store for KMD.LOG file LDA NIBMUS ; Find user area for IBM use STA USER ; Store for 'FOR' description file STA LOGUSR ; Store for KMD.LOG file ; SNDOK1: LDA BCHFLG ; Batch mode requested? ORA A JNZ SBTCH ; If yes, go handle batch mode CALL LOGDU ; SNDFL1: LDA OPTSAV CPI 'A' ; If .ARC, .ARK or .LBR mode skip 'CNREC' CNZ CNREC ; Get record count SNDFL2: CALL OPNFIL ; Open the file CALL RDBLK1 ; Put up to 16k from file into buffer CALL CATCH ; Clear the decks LDA BYE5 ORA A JNZ SNDFL3 MVI E,75 ; No BYE5, looking for 1 minute wait ; SNDFL3: MVI E,73 ; Wait up to 1 minute for initial 'NAK' ; (Correction factor of .84) SNDFL4: CALL WAITNAK CALL SETFLG ; Can't use 1k if not 8 records in file ; ; ; Loops back to this point after a successful transmission for next one ; SNDLP: CALL GTRATIO ; Check the ACK ratio if using 1k blocks CALL RDRECD ; Read a record JC SNDEOF ; Send 'EOF' if done CALL INCRNO ; Bump record number if sent ok CALL SNDABT ; Check for local abort XRA A ; Initialize error count to zero STA ERRCT ; ; ; Comes back here to repeat previous transmission if no ACK was received ; SNDRPT: CALL CKABORT ; Want to stop sending for some reason? CALL FUNCHK ; Check the function keys CALL SNDABT ; Check for local abort CALL SNDHDR ; Send a header CALL SNDREC ; Send data record CALL SNDCHK ; Send CRC or checksum value CALL GTACK ; Get the 'ACK' CPI 6 ; ACK? JNZ SNDRPT ; If not, repeat transmission CALL SETPTR ; Successful record so increase pointers LDA OPTSAV ; Get the command option again CPI 'A' ; In .ARC, .ARK or .LBR mode? JNZ SNDLP ; If not one of these, exit ; SNDRPT1:CALL SETLBR ; Set library pointers and size left LHLD RCNT ; See if anything was actually sent MOV A,H ORA L ; See if L and H both zero now JZ SNDEOF ; If finished, exit JMP SNDLP ;..... ; ; ; File sent, send EOT but do local log-keeping first ; SNDEOF: LDA LOGLDS ORA A JZ EOF1 PUSH H LHLD DNLDS ; Address of downloads counter INR M ; One more download since log in POP H ; Restore original address ; EOF1: LDA LOGCAL ORA A CNZ LOGCALL ; Write log entries first ; EOF2: CALL EOFSND LDA TIMEON ORA A JZ EOF3 LDA CLOCK ORA A JNZ EOF3 CALL ADDTON ; Update BYE5's time-on-system byte ; EOF3: CALL ALLDON JMP DONE ;..... ; ; ; Sends batch mode ; SBTCH: LDA FSTFLG ; If first time through ORA A JNZ SBTCH1 ; If not first time, exit CALL ILPRT DB 'calculating.....',0 CALL LOGDU ; Check disk, user CALL TNMBUF ; Put all requested files into NAMBUF ; ; ; Total number of files, total records and total length is shown, user ; then gets up to 5 seconds to abort. ; CALL ILPRTB ; Show to remote 1st time through DB 13,'Total files : ',0 LDA FILCNT ; Get total files STA SHOCNT PUSH PSW MOV L,A MVI H,0 CALL DECOUT ; Show remote # of files POP PSW ORA A ; Abort if no files to send JZ NOFILE CALL ILPRT DB 13,10,'Total records : ',0 LHLD TOTREC ; Get total records - all files PUSH H CALL DECOUT ; Show remote CALL ILPRT DB ' (',0 POP H LXI D,8 CALL DVHLDE ; To get # of 1024 byte blocks MOV A,H ORA L ; Check if remainder MOV H,B ; Get quotient MOV L,C JZ $+4 ; If 0 remainder, exact k INX H ; Else bump up 1 k CALL DECOUT ; Show # of k CALL ILPRT DB 'k)' DB 13,10,'Space needed : ',0 LHLD BLOKK ; Get k required on remote disk for 2k XCHG ; Block size LHLD BLOKK DAD D ; Double the size for 2k blocks CALL DECOUT ; Print it CALL ILPRT DB 'k with 2k blocks',0 ; SBTCH1: LDA FILCNT ORA A JZ SBTCH3 ; LDA FSTFLG STA CONONL CALL ILPRT DB 13,10,'Time for files: ',0 LXI H,KTABLE CALL GTSPD MVI D,0 MOV E,A ; Set up for table access DAD D ; Index to proper factor DAD D MOV E,M INX H MOV D,M LHLD TOTREC ; Get number of records CALL FILTIM1 CALL OPNOK8 CALL ILPRT DB 13,10,0 LDA FSTFLG ORA A JNZ SBTCH3 INR A ; Now show we have been this way STA FSTFLG CALL ILPRT DB 13,10,'Ready to send in batch mode' DB 13,10,0 ; SBTCH2: LDA XCANCL ; 3 or more CTL-X's to cancel? ORA A JZ SBTCH3 ; If not, exit CALL ILPRT DB 'Type several CTL-X to abort' DB 13,10,0 ; SBTCH3: CALL CKABORT CALL SNDFN ; Sends file name to receive JC SBTCH4 ; No more files, exit CALL SHOWFIL ; Show the batch filename JMP SNDFL1 ; Send the file ;... ; ; SBTCH4: LDA GOTONE ; Did we actually send at least one? ORA A JZ ABORT ; If not, don't act like we did CALL EOFSND ; No more files so send EOT to finish CALL XFRDON CALL WAIT1 JMP EXIT ;..... ; ; NOFILE: CALL ERXIT DB 13,10,'++ Ask again, no files found ++','$' ;..... ; ; EOFSND: MVI A,4 ; Send an 'EOT' CALL SEND LDA CHKEOT ; Did not get an ACK, try again INR A STA CHKEOT ; Limit number of retries to 4 CPI 4 ; (to prevent possible 'lock-up') RNC ; Quit if already sent 4 or more CALL GTACK ; Get the ACK CPI 6 ; ACK? JNZ EOFSND ; Resend if not RET ;..... ; ; ALLDON: LDA BCHFLG ; In batch mode? ORA A RNZ ; If yes, ignore message CALL ILPRT ; (Want to keep this a separate message) DB 13,10,0 ;... ; ; XFRDON: CALL ILPRT DB 13,10,'[Transfer completed]',13,10,0 RET ;..... ; ; SNDABT: LDA SYSABT ORA A ; Local abort requested? (^X) JNZ ABORT ; Yes, else return RET ;..... ; ; ;======================================================================= ; ; ---> RCVFL UPLOAD (from USER to RCP/M System) ; ;======================================================================= ; ; The file specified in the KMD command line is transferred over the ; phone from the user's computer to the RCP/M system via modem using ; the "R" (receive) option. The data is sent one record at a time, ; with headers and checksums and retransmissions on errors. ; RCVFL: LDA MSGFLG ; Message file flag set? ORA A JZ RCVF2 ; If not, exit ; LDA AFBYTE ; See what bits are set ANI 8 ; Allowing message file uploads? (Bit 3) JNZ RCVF1 ; If yes, exit CALL ERXIT DB '++ You are not currently allowed to upload messages ++' DB '$' ; Terminates the statement ; RCVF1: LHLD WHEEL ; Wheel byte in use? MOV A,M ORA A JZ RCVF4 ; If not, skip next section ; CALL ERXIT DB '++ Turn the WHEEL off to upload a message file ++' DB '$' ; Terminates the statement ; RCVF2: LDA RESUSR ; Checking upload/download ratio? ORA A JZ RCVF3 ; LDA AFBYTE ANI 64 ; Check upload access (bit 6) JNZ RCVF3 CALL ERXIT DB '++ You are not currently allowed to upload files ++' DB '$' ; Terminates the statement ; RCVF3: LDA BCHFLG ; Requesting batch mode? ORA A JNZ RCVBCH ; If yes, exit ; RCVF4: CALL RCVFL1 ; Find drive/user/filetype permitted CALL RCVFL7 ; Display drive/user area CALL CONTIN ; Display drive/user area CALL MAKEFIL ; Open the file, ready to receive ; RCVLP: CALL RCVRECD ; Get a record JC RCVEOT ; Exit if 'EOT' for end of current file CALL INCRNO ; Bump record number, if received ok CALL WRRECD ; Write the record CALL SNDACK ; Ack the record JMP RCVLP ; Loop until 'EOF' ;..... ; ; ;----------------------------------------------------------------------- ; ; Using batch so reset flags ; RCVBCH: XRA A STA FRSTIM ; Needs to be reset for each new file MVI A,1 STA SNDFLG ; Shows we are in receive batch mode LDA FSTFLG ; First batch file? ORA A JNZ RCVBC1 ; If not, exit CALL RCVFL1 ; Find drive/user/filetype permitted CALL CONTIN ; Display drive/user area LXI H,NAMBUF SHLD NBSAVE MVI A,1 STA FSTFLG ; No need to run those routines again ; RCVBC1: CALL RCVFN ; Get the batch file name and display JC RCVBC2 ; If all done, exit CALL RCVFL7 ; Change file extent if needed CALL CHEKFIL ; Already have a file with that name? CALL MAKEFIL CALL BCHINR CALL ILPRTL DB 'Waiting.....',0 MVI A,67 CALL SEND MVI A,75 ; Request 1k blocks CALL SEND JMP RCVLP ; Start receiving the file ; RCVBC2: XRA A ; Zero the batch mode flag STA BCHFLG LDA GOTONE ; Were there any files received? ORA A JZ ABORT CALL XFRDON ; Show transmission is finished CALL WAIT1 ; Delay to let remote get into ter. mode JMP RCVEOT1 ; Ask for descriptions ;..... ; ; ;----------------------------------------------------------------------- ; ; Check on what drive/user area the file(s) will go into ; RCVFL1: CALL LOGDU ; Select drive/user for upload CALL GTWHL ; Let SYSOP put file wherever he wants JZ RCVFL5 ; If WHEEL byte not set, stay normal LDA ZCPR ; See if using ZCMD, ZCPR, etc. ORA A JZ RCVFL5 ; If not, exit LDA RCVDRV ; Any drive specified? ORA A JZ RCVFL2 ; If not, exit SUI 65 ; Convert ASCII drive to binary JMP RCVFL3 ; RCVFL2: LDA OLDDRV ; RCVFL3: INR A STA 92 ADI 64 ; Convert binary to ASCII STA DRV ; Drive LDA RCVDRV ; See if a drive was requested ORA A LDA OLDUSR ; Current user JZ RCVFL4 ; If not, use current user LDA RCVUSR ; Else get requested user ; RCVFL4: STA USR ; User INR A ; Make sure it is a positive number STA RWHEEL RET ;... ; ; RCVFL5: LDA SETAREA ORA A JZ RCVFL6 LDA DRV SUI 64 STA 92 ; RCVFL6: LDA PRVTFL ; Receiving to a private area? ORA A RZ ; If not, exit LDA PRDRV ; Private area takes precedence SUI 64 ; Convert to binary STA 92 ; Store drive to be used RET ;..... ; ; ; Changes the name of certain type of files such a .COM to .OBJ, ect. ; RCVFL7: LDA RWHEEL ; Wheel byte set for SYSOP? ORA A RNZ ; Yes, don't change any file extents LDA NOCOMR ORA A JNZ RCVFL9 LXI H,101 ; Point to filetype MVI A,'C' ; 1st letter CMP M ; Is it 'C' ? JNZ RCVFL8 ; If not, continue normally INX H ; Get 2nd letter MVI A,'O' ; 2nd letter CMP M ; Is it 'O' ? JNZ RCVFL8 ; If not, continue normally INX H ; Get 3rd letter MVI A,'M' ; 3rd letter CMP M ; Is it 'M' ? JNZ RCVFL8 ; If not, continue normally CALL ILPRT ; Print renaming message DB 'Auto-renaming file to ".OBJ"',13,10,0 LXI H,101 ; Point to filetype MVI M,'O' INX H MVI M,'B' INX H MVI M,'J' RET ;... ; ; RCVFL8: LXI H,101 ; Point to filetype MVI A,'P' ; 1st letter CMP M ; Is it 'P' ? JNZ RCVFL9 ; If not, continue normally INX H ; Get 2nd letter MVI A,'R' ; 2nd letter CMP M ; Is it 'R' ? JNZ RCVFL9 INX H ; Get 3rd letter MVI A,'L' ; 3rd letter CMP M ; Is it 'L' ? JNZ RCVFL9 CALL ILPRT ; Print renaming message DB 'Auto-renaming file to ".OBP"',13,10,0 LXI H,101 ; Point to filetype MVI M,'O' INX H MVI M,'B' INX H MVI M,'P' RET ;... ; ; RCVFL9: LDA ZCPR ORA A JZ RCVFL13 LXI H,101 ; Point to filetype MVI A,'N' ; 1st letter CMP M ; Is it 'N' ? JNZ RCVFL10 ; If not, continue normally INX H ; Get 2nd letter MVI A,'D' ; 2nd letter CMP M ; Is it 'D' ? JNZ RCVFL10 ; If not, continue normally INX H ; Get 3rd letter MVI A,'R' ; 3rd letter CMP M ; Is it 'R' ? JZ RCVFL12 ; If yes, print error message and abort ; RCVFL10:LXI H,101 ; Point to filetype MVI A,'R' ; 1st letter CMP M ; Is it R ? JNZ RCVFL11 ; If not, continue normally INX H ; Get 2nd letter MVI A,'C' ; 2nd letter CMP M ; Is it C ? JNZ RCVFL11 ; If not, continue normally INX H ; Get 3rd letter MVI A,'P' ; 3rd letter CMP M ; Is it P ? JZ RCVFL12 ; Else play error message ; RCVFL11:LXI H,101 ; Point to filetype MVI A,'S' ; 1st letter CMP M ; Is it S ? RNZ ; If not, continue normally INX H ; Get 2nd letter MVI A,'Y' ; 2nd letter CMP M ; Is it Y ? RNZ ; If not, continue normally INX H ; Get 3rd letter MVI A,'S' ; 3rd letter CMP M ; Is it S ? JZ RCVFL12 ; Else play error message RET ; If not, continue normally ;... ; ; RCVFL12:CALL ERXIT ; Print renaming message DB 13,10,'++ Select a different file extent ++','$' ; RCVFL13:RET ; Just in case ZCPR not used, etc. ;..... ; ; RCVMSG: CALL ILPRT ; Print the message DB 'File will be received on ',0 RET ;..... ; ; ; Displays where the file(s) will go, opens the file and shows the name ; CONTIN: LDA PRVTFL ; Going to store in the private area? ORA A JZ CONT1 ; If not, exit ; ; ; It's a private file ; CALL RCVMSG LDA PRDRV JMP CONT9 ; If yes, it takes priority ; CONT1: LDA KIND CPI 73 JZ CONT7 LDA SETAREA ORA A JZ CONT8 ; Exit if not using a specified area ; ; ; Using separate drives for 8-bit and 16-bit uploads? ; LDA MSDOS ; Separate area for MS-DOS files? ORA A JZ CONT7 ; Exit if not ; ; ; Using separate areas, see if same drive for both ; CALL GTWHL ; See if wheel byte is set JNZ CONT2 ; If yes, ask if CP/M or MS-DOS LDA DRV ; Get the drive to store CP/M stuff MOV B,A LDA IBMDRV CMP B ; IBM drive less than CP/M drive? JC CONT7 ; If yes, exit without change ; ; ; 16-bit drive is same as or larger than CP/M drive, so ask which to use ; CONT2: MVI C,9 LXI D,SELECT CALL 5 CALL ILPRT DB 13,10,'select: ',0 MVI A,1 STA KIND ; Allows use of INPUT routine with timer ; CONT3: CALL INPUT CPI 50+1 JNC CONT3 ; If more than '2', ask again CPI 49 JC CONT3 ; If less than '1', ask again CALL TYPE JZ CONT6 ; If '1', no change LDA CPMDRV ; Get CP/M drive again MOV B,A ; Store for now LDA IBMDRV ; Get IBM drive again CMP B ; See if same as for CP/M JZ CONT5 ; If yes, use same KMD and FOR files MOV B,A ; Store IBM drive value for now LDA NEWLST ; See if the same lists used for both ORA A JZ CONT4 LDA NIBMDR ; Get the IBM drive to store stuff on STA DRIVE ; Use MS-DOS drive for file descriptions STA LOGDRV ; Use MS-DOS drive for new KMD.LOG file LDA NIBMUS ; Get the IBM user area to store stuff on STA USER ; Use MS-DOS user area for 'FOR' file STA LOGUSR ; Use MS-DOS user area for KMD.LOG file ; CONT4: CALL GTWHL ; See if wheel byte is set JNZ CONT6 ; If yes, exit, leave drive alone MOV A,B ; Get the IBM value back STA DRV ; Use IBM drive for storing file ; CONT5: CALL GTWHL ; See if wheel byte is set JNZ CONT6 ; If yes, exit, leave user alone LDA IBMUSR ; User area for IBM files to go to STA USR ; Store in area for uploads ; CONT6: CALL ILPRT DB 13,10,10,0 ; CONT7: CALL RCVMSG LDA DRV JMP CONT9 ; CONT8: CALL RCVMSG LDA OLDDRV ; Otherwise get current drive ADI 65 ; Convert to ASCII ; NOTDRV: DB 0,0 ; Filled in by 'GETDU' if requested ; CONT9: STA KDRV ; Save drive for KSHOW PUSH PSW ; Save the drive CALL CTYPE ; Print the drive to store on POP PSW ; Get the drive back SUI 64 ; Convert to binary STA 92 LDA PRVTFL ; Going to store in the private area? ORA A JZ NOPRVL ; If nope, skip ahead LDA LOGCAL ORA A JZ CONT10 MVI A,80 ; If private upload STA LOGOPT ; CONT10: LDA PRUSR ; Get private user area JMP CONT11 ; It takes priority ; NOPRVL: LDA SETAREA ORA A LDA USR ; SETAREA takes next precedence JNZ CONT11 LDA OLDUSR ; Get current drive for default ; NOTUSR: DB 0,0 ; Filled in by 'GETDU' if requested ; CONT11: MVI H,0 MOV L,A CALL DECOUT ; Print the user area CALL ILPRT DB 58,13,10,0 CALL KSHOW ; Show available space remaining CALL ILPRT DB 13,10,0 CALL CHEKFIL ; See if file exists LDA BCHFLG ORA A JZ CONT12 CALL ILPRT DB 13,10,'Batch mode, ready to receive',13,10,0 JMP CONT13 ; CONT12: CALL FILNAM ; Send the filename requested CALL ILPRT DB 13,10,'File open - ready to receive',13,10,0 ; CONT13: LDA XCANCL ; 3 or more CTL-X's to cancel? ORA A JZ CONT14 ; If not, exit CALL ILPRT DB 'Abort with several control-X',13,10,0 ; CONT14: LDA BCHFLG ; In batch mode now? ORA A JNZ CONT15 ; Exit if yes CALL ILPRTL ; Show locally only DB 'Waiting.....',0 RET ;... ; ; CONT15: LDA PRVTFL ; Batch to the private area? ORA A RNZ ; If yes, don't mention descriptions ; LDA DESCRIB ORA A JZ CONT16 CALL ILPRT DB 13,10,'Description needed when done',13,10,0 ; CONT16: RET ;..... ; ; ; Got EOT on record so flush buffers then done ; RCVEOT: LHLD RECDNO ; Check for zero length file MOV A,H ; If no records, no file ORA L JZ ABORT ; Abort and erase the zero length file LDA EOTFLG ; This the first EOT character? ORA A JNZ RCVEND ; If not, exit MVI A,21 STA EOTFLG ; Set the flag CALL SEND ; Send the NAK JMP RCVLP ; Go wait for another EOT ; RCVEND: CALL SNDACK ; ACK the record ; RCVQT: CALL WRBLOCK ; Write the last block CALL CLOSFIL ; Close the file ; ; ; Write record to log file if LOGCAL is YES ; LDA LOGCAL ORA A JZ RCVEN1 LHLD RECDNO ; If yes, get # of records SHLD RCNT ; And stuff in RCNT CALL XTIM ; Calculate appoximate transfer time CALL STORTIM ; Store the time CALL LOGCALL ; RCVEN1: LDA LOGLDS ORA A JZ RCVEN2 PUSH H LHLD UPLDS ; Get current uploads INR M ; One more upload since log in POP H ; Restore the original address ; RCVEN2: CALL ALLDON ; If not batch, print Xfer complete ; ; ;----------------------------------------------------------------------- ; credit routine ; LDA CREDIT ; Crediting time for uploads? ORA A JZ CRED6 ; If not, exit LDA MSGFLG ; Was this a message file? ORA A JNZ RCVEOT2 ; If yes, no credit given LDA BCHFLG ; In batch mode now? ORA A JNZ CRED4 ; If yes, skip following messages LDA ZCPR ORA A JZ CRED1 CALL GTWHL ; WHEEL byte set for SYSOP? JNZ CRED2 ; Yes, skip the thanks ; CRED1: CALL ILPRTB ; Show to remote also DB 13,10,'Thank you for the upload',13,10,0 LDA TLIMIT ORA A ; Special user? JNZ CRED3 ; No ; CRED2: CALL ILPRT ; Yes, let him know DB 13,10,'You have unlimited time on the system',13,10,0 JMP CRED5 ; CRED3: CALL ILPRT DB 13,10,'Your upload time has been credited and',13,10 DB 'extends your allowed time-on-system',13,10,0 ; CRED4: LDA TLIMIT ; Get user status/MXTIME ORA A JZ CRED5 PUSH PSW ; Save value LHLD RECDNO SHLD RCNT CALL XTIM POP PSW ; Get original MXTIME INR A ; Round up 1 minute ADD C ; Plus upload time STA TLIMIT ; CRED5: INR A ; Set to local display only STA CONONL ; ; ; If not still in batch mode, ask for file description ; CRED6: LDA BCHFLG ; In batch receive? ORA A JNZ RCVEOT2 ; If yes, skip asking for a description ; ; ; end of credit routine ;----------------------------------------------------------------------- ; RCVEOT1:LDA DESCRIB ORA A JZ RCVEOT2 LHLD 0001H DCX H MOV D,M DCX H MOV E,M LXI H,12 DAD D XRA A MOV M,A CALL ASK ; If yes, ask for description of file ; RCVEOT2:LDA TIMEON ORA A JZ RCVEOT3 LDA CLOCK ORA A JNZ RCVEOT3 CALL ADDTON ; Update BYE5's time-on-system byte ; RCVEOT3:JMP DONE ;..... ; ; ;======================================================================= ; ; BATCH MODE ROUTINES ; ;======================================================================= ; ; ; If in batch receive, gets a file name from the buffer then asks for a ; description. ; BCHDCR: LDA FILCNT DCR A STA FILCNT ; BCHD1: LHLD NBSAVE ; Get address of next batch filename LXI D,92 ; Where to put it MVI B,12 CALL MOVE SHLD NBSAVE ; Store address for next filename RET ;..... ; ; ; If receiving batch, increment the file count, store the filename so we ; can later ask for a description. ; BCHINR: LHLD NBSAVE ; Where to put the name LXI D,92 ; Where to get the name XCHG MVI B,12 ; Move the current file name into buffer CALL MOVE XCHG SHLD NBSAVE ; Store address for next filename LDA FILCNT ; Increment the file count INR A STA FILCNT RET ;... ; ; BCHINR1:LDA FILCNT CPI 50 ; 50 files yet? RC ; CALL ILPRT DB 13,10,'++ 50 batch uploads is the limit ++',13,10,0 XRA A STA BCHFLG ; Reset the batch mode flag to zero POP H ; Reset stack from "CALL BCHINR" JMP RCVEOT1 ; Request file descriptions ;..... ; ; ; Loads a command line addressed by 'DE' registers (max # characters in ; line in 'DE', number of characters in line in DE+1, line starts in ; DE+2) into FCB addressed by 'HL' registers. The FCB should be at ; least 33 bytes in length. The command line buffer must have a maxi- ; mum length at least one more than the greatest number of characters ; that will be needed. ; CMDLINE:PUSH PSW PUSH B PUSH D PUSH H CALL INITIAL ; Fills FCBs with blanks and nulls XCHG ; Get start of command line in HL INX H ; Address # bytes in command line MOV E,M ; Load DE pair with # bytes MVI D,0 INX H DAD D ; Point to byte after last character MVI M,13 ; In command line and store delimiter POP H ; Restore HL and DE POP D PUSH D PUSH H INX D ; Address start of command INX D CALL DRIVEX MVI C,8 ; Transfer first filename to FCB CALL TRANS CPI 13 JZ DONEL CPI 32 ; If space, then start of 2nd filename JZ NAME1 POP H ; Filetype starts after 8th byte PUSH H LXI B,9 DAD B MVI C,3 ; Transfer type of first file CALL TRANS CPI 13 JZ DONEL ; NAME1: LDAX D ; Eat multiple spaces between names CPI 32 JNZ NAME2 INX D JMP NAME1 ; NAME2: POP H ; Second name starts in 16th byte PUSH H ; Point HL to this byte LXI B,16 DAD B CALL DRIVEX MVI C,8 CALL TRANS CPI 13 JZ DONEL POP H ; Second file type starts in 25th byte PUSH H LXI B,25 DAD B MVI C,3 CALL TRANS ; DONEL: POP H PUSH H INX H ; Point to 1st char of 1st name in FCB CALL SCANL ; Check for * (ambiguous names) POP H PUSH H LXI B,17 ; To 1st character of second name in FCB DAD B CALL SCANL POP H POP D POP B POP PSW RET ;..... ; ; ; Subroutines for CMDLINE section ; INITIAL:PUSH H ; Initializes FCB with 1 null for first PUSH B ; Drive with 11 blanks, 4 nulls, 1 MVI M,0 ; Null for second drive with 11 blanks INX H ; And 4 nulls MVI B,11 MVI A,32 CALL INITFILL MVI B,5 XRA A CALL INITFILL MVI B,11 MVI A,32 CALL INITFILL MVI B,4 XRA A CALL INITFILL POP B POP H RET ;..... ; ; INITFILL: MOV M,A INX H DCR B JNZ INITFILL RET ;..... ; ; ; Show batch files remaining after this one is sent ; CUMSTS: CALL ILPRTL DB 'When done: ',0 LDA SHOCNT ; Get cumulative files DCR A STA SHOCNT ; Less one MOV L,A MVI H,0 CALL DECOUT CALL ILPRTL DB ' left with ',0 LHLD RCNT ; Get this file's record count again XCHG ; Put in DE LHLD TOTREC ; Total records remaining MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A JNC $+6 LXI H,0 ; In case of a slightly negative number SHLD TOTREC PUSH H CALL DECOUT ; Show remote remaining records CALL ILPRTL DB ' records (',0 POP H LXI D,8 CALL DVHLDE MOV A,H ORA L MOV H,B MOV L,C JZ $+4 INX H CALL DECOUT CALL ILPRTL DB 'k)',13,10,0 RET ;..... ; ; ;----------------------------------------------------------------------- ; DRIVEX: INX D ; Check 2nd byte of filename. if it.. LDAX D ; Is a ":", then drive was specified.. DCX D CPI ':' JNZ DEFDR ; Else zero for default drive LDAX D ; ('INIT' put zero) ANI 95 SUI 64 ; Calculate drive (A=1, B=2,...) MOV M,A ; Place it in FCB INX D ; Address first byte in command line INX D ; DEFDR: INX H ; And name field in FCB RET ;..... ; ; ;----------------------------------------------------------------------- ; ; Clears the FCB area ; INITFCB:MVI M,0 ; Clears the drive ; INITFCB1: INX H MVI B,11 ; Clears the filename and extent area ; LOOP11: MVI M,32 INX H DCR B JNZ LOOP11 MVI B,21 ; Clears the rest with zeros ; LOOP21: MVI M,0 INX H DCR B JNZ LOOP21 RET ;..... ; ; ; Finished with the file transfer ; DONE: LDA BCHFLG ; In batch mode now? ORA A JZ EXIT ; If not, all done so go finish up ; LDA OLDDRV ; Restore the original drive CALL RECDRX LDA OLDUSR ; Restore the original number CALL RECARE CALL RSDMA ; Reset to default DMA address MVI B,12 ; Zero out DONE6 LXI H,DONE6 ; ; ; Null the batch file name buffer ; DONE1: MVI M,0 ; Zero the memory location INX H DCR B JNZ DONE1 ; Zero all 12 locations ; ; ; Now fill in the batch file name ; MVI B,12 ; Put file name in DONE6 LXI H,93 LXI D,DONE6 ; DONE2: MVI A,4 ; Start of file type? CMP B JZ DONE4 ; Put in period if so MOV A,M CPI 32 ; Don't put in space JZ DONE3 STAX D ; Store in DONE6 INX D ; DONE3: INX H DCR B MOV A,B ORA A ; End of file name? JZ DONE5 ; Display file name JMP DONE2 ; Loop for another character ;..... ; ; DONE4: MOV A,M CPI 32 ; Is file type empty? JZ DONE5 ; Go if so MVI A,46 ; Else put period in message STAX D INX D DCR B JMP DONE2 ;..... ; ; DONE5: MVI A,1 ; Display filename locally only STA GOTONE ; Indicates there was a file handled CALL ILPRTL ; Display the file name locally only DB 13,10 ; DONE6: DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0 CALL ILPRTL DB ' Transferred',13,10,0 ; ; ; Now reset some flags for another possible batch file ; XRA A STA EOFLG ; Clear end of file flag STA EOTFLG ; Clear end of text flag STA CHKEOT ; Clear the "resend EOT" flag LXI H,0 SHLD ACCERR ; Reset the accumulate error count SHLD RECNBF ; Zero number of records in the buffer SHLD RECDNO ; Zero the current record number SHLD RCDCNT ; Zero the transmit record counter LXI H,DBUF ; Reset buffer pointers SHLD RECPTR LDA SNDFLG ; Goes to either send or ORA A ; Receive file, depending JZ SNDFL ; Upon which routine set CALL BCHINR1 ; Store filename, increment the count JMP RCVFL ; The flag in multi-file mode ;..... ; ; ;----------------------------------------------------------------------- ; multi-file access routine ; ; Multi-file access subroutine. Allows processing of multiple files ; (i.e., *.ASM) from disk. Builds the correct name in the FCB each time ; it is called. The command is used in programs to process single or ; multiple files. The FCB is set up with the next name, ready to do ; normal processing (open, read, etc.) when routine is called. Carry is ; set if no more names are found. ; MFNAM: PUSH B PUSH D PUSH H CALL RSDMA POP H POP D POP B XRA A STA 104 LDA MFFLG1 ORA A JNZ MFNAM1 MVI A,1 STA MFFLG1 LXI H,92 LXI D,MFNAM5 LXI B,12 CALL MOVER LDA 92 STA MFNAM6 ; Save disk in current FCB LXI H,MFNAM5 LXI D,92 LXI B,12 CALL MOVER PUSH B PUSH D PUSH H MVI C,17 LXI D,92 CALL 5 POP H POP D POP B JMP MFNAM2 ;... ; ; MFNAM1: LXI H,MFNAM6 LXI B,12 LXI D,92 CALL MOVER PUSH B PUSH D PUSH H MVI C,17 LXI D,92 CALL 5 POP H POP D POP B LXI H,MFNAM5 LXI B,12 LXI D,92 CALL MOVER PUSH B PUSH D PUSH H MVI C,18 LXI D,92 CALL 5 POP H POP D POP B ; MFNAM2: INR A STC JNZ MFNAM3 STA MFFLG1 RET ;..... ; ; MFNAM3: DCR A ANI 3 ADD A ADD A ADD A ADD A ADD A ADI 129 MOV L,A MVI H,0 PUSH H ; Save name pointer LXI D,MFNAM6+1 LXI B,11 CALL MOVER POP H LXI D,93 LXI B,11 CALL MOVER XRA A STA 104 STA 124 RET ;..... ; ; MOVER: MFNAM4: MOV A,M ; Used if an 8080 CPU is active STAX D INX H INX D DCX B MOV A,B ORA C JNZ MFNAM4 RET ;..... ; ; end of multi-file access routine ;----------------------------------------------------------------------- ; KMD receive batch mode ; RCVFN: LXI H,92 CALL INITFCB1 ; Does not initialize drive XRA A STA RCVTRY INR A ; Set to local display only STA CONONL ; RKMD1: CALL CKABORT ; Check for user abort MVI B,5 ; Wait up to 5 sec. for SOH from remote CALL RECV JC RKMD2 ; No character, decrement counter CPI 24 ; Was it a CTL-X for cancel? CZ CKCAN ; Check for abort CPI 1 JZ RKMD4 ; Got SOH JMP RKMD1 ; None of these, wait some more ; RKMD2: MVI A,67 ; Send a 'C' CALL SEND ; RKMD3: LDA RCVTRY INR A STA RCVTRY CPI 20 ; Up to 100 seconds for batch to start JC RKMD1 JMP ABORT ; Quit and try to force him to quit also ;... ; ; RKMD4: MVI B,5 ; 5 seconds to get sector number CALL RECV JC KMDTOT MOV D,A ; Save sector number in D ORA A ; Must be a 0 if sending batch JNZ KMDHDR MVI B,5 ; 5 seconds to get reciprocal CALL RECV JC KMDTOT CMA ; Invert it and compare to sector # CMP D JNZ KMDCRC ; Bad match LXI H,0 SHLD CRCVAL ; Clear CRC counter MVI E,128 ; Expecting a 128 character block LHLD RECPTR ; Point to the buffer address ; RKMD5: MVI B,5 ; 5 seconds to get 128 byte header block CALL RECV ; Get the character JC KMDTOT ; Exit if no character MOV M,A ; Store the character INX H ; Point to next buffer location DCR E ; One less to go JNZ RKMD5 MVI E,2 ; Number of CRC bytes to get ; RKMD6: MVI B,5 CALL RECV ; Get CRC bytes JC KMDTOT DCR E ; Done? JNZ RKMD6 ; No CALL CRCCHK ; Compare CRC received against ours ORA A ; Ok? JNZ KMDCRC ; No CALL SNDACK ; Yes, acknowledge to remote ; ; ; Decode pathname into CPM format ; LXI D,93 ; Where to put it LHLD RECPTR ; Where to get it MVI B,8 ; Filename length ; RKMD7: MOV A,M ; Get the character from the buffer ORA A ; Was it a zero? JZ RKMD12 ; If yes, all done CPI 46 ; Was it a delimiter? JZ RKMD9 ; RKMD8: CALL UCASE ; Insure name is in upper case CALL KMDUNDR ; Convert any underline to a hyphen STAX D ; Store filename character in FCB INX D ; Increment pointers INX H DCR B ; One less to go JNZ RKMD7 ; If not 8, keep going MOV A,M ; Get the character back ORA A ; We had 8, was there an extent? JZ RKMD11 ; If zero, was all done JMP RKMD10 ; Else must be a period ; RKMD9: MVI A,32 ; Spaces to make up 8 spaces for name STAX D ; Store space character in FCB INX D ; Increment pointers DCR B ; One less to go JNZ RKMD9 ; Keep going until in extent area ; RKMD10: INX H ; Skip the '.' position MVI B,3 ; Extent length RKMD11: MOV A,M ; Get the character from the buffer ORA A ; Was it a zero? JZ RKMD12 ; If yes, all done CALL UCASE ; Insure extent is in upper case CALL KMDUNDR ; Convert any underlines to a hyphen STAX D ; Store extent character INX D ; Increment pointers INX H DCR B ; One less to go JNZ RKMD11 ; Keep going until finished ; RKMD12: LDA 93 ; See if there was any filename at all CPI 32 STC ; If not set the carry flag RZ ; No, all done, no more files ; CALL ILPRTL DB 13,10,'File name: ',0 LHLD RECPTR ; Print filename ; RKMD13: MOV A,M ORA A JZ RKMD14 CALL UCASE CALL CTYPE INX H JMP RKMD13 ; RKMD14: LHLD BUFSTR ; Get the file length, if provided MOV A,H ORA L JZ RKMD15 ; If both zero, length not provided ; SHLD RCNT ; Store the file length CALL OPNOK7 CALL ILPRTL DB 'k)',13,10,'Recv time: ',0 CALL KTIM CALL OPNOK8 ; RKMD15: CALL ILPRTL DB 13,10,0 ; Finish the filename line XRA A ; Reset the carry flag STA RCVTRY ; Reset the error counter RET ;..... ; ; KMDCRC: CALL ILPRTL DB '++ CRC error ++',13,10,0 JMP KMDXFR ;..... ; ; KMDHDR: CALL ILPRTL DB '++ Wrong header type ++',13,10,0 JMP KMDXFR ;... ; ; KMDTOT: CALL ILPRTL DB ' ++ Time out receiving filename ++',13,10,0 ;... ; ; KMDXFR: CALL WAIT1 ; Make sure sender has stopped MVI A,21 ; Tell sender it was not successful CALL SEND LDA RCVTRY ; Increment the error counter INR A STA RCVTRY CPI 33 JC RKMD3 ; Send a NAK and tell him to try again JMP ABORT ; Else abort ;..... ; ; KMDUNDR:CPI '_' ; CP/M doesn't like underlines RNZ ; If not, exit MVI A,'-' ; Change any to a hyphen RET ;..... ; ; end of KMD get batch file name ;----------------------------------------------------------------------- ; KMD send batch mode ; SNDFN: LXI H,92 CALL INITFCB1 ; Does not initialize drive XRA A STA ERRCT ; Reset the error count INR A STA CONONL ; Set to local display only LDA FILCNT ; Get the file count ORA A JZ CCHECK ; No more files, exit ; MVI A,1 STA CRCFLG ; Make sure in CRC mode CALL BCHD1 ; Get the name into FCB LHLD RECPTR ; Where to load the 0 block XCHG ; Put into DE LXI H,93 ; Get the start of the filname in HL MVI B,8 ; SKMD0: MOV A,M ANI 7FH ; Strip any high bit set ORA A JZ SKMD5 ; Null pathname CPI 32 JZ SKMD2 ; SKMD1: CALL LCASE ; Put file name in lower case for UNIX STAX D INX H INX D DCR B JNZ SKMD0 JMP SKMD3 ; SKMD2: INX H ; Skip over spaces if short name DCR B JNZ SKMD2 ; SKMD3: MOV A,M CPI 32 JZ SKMD5 ; Missing file type field MVI A,46 ; Send name-type seperator STAX D INX D MVI B,3 ; SKMD4: MOV A,M ANI 127 ; Strip any high bit set CPI 32 JZ SKMD5 CALL LCASE ; Put in lower case for UNIX STAX D INX H INX D DCR B JNZ SKMD4 ; SKMD5: XCHG ; Get the address back to HL MVI M,0 ; Shows FILENAME.EXT is terminated INX H SHLD HDRADR CALL CNREC ; Get number of records in this file CALL CHARLN ; Include the ASCII character length ; SKMD6: MVI M,0 ; Fill rest of block with zeroes INR L JNZ SKMD6 ; Keep going until block is filled ; LHLD RCNT ; Get the record count again and store SHLD BUFSTR ; Store the file length at end of block XRA A ; Make sure the header starts with Zero STA RCDCNT ; ; ; Wait for a `C' from the remote system, indicating he is ready ; CCHECK: CALL CATCH ; Clear the decks for action MVI E,73 ; Wait up to 60 seconds to abort (x.84) ; CCHECK0:CALL CKABORT ; Manually requesting an abort? MVI B,5 ; Wait up to 5 seconds for a character CALL RECV JC CCHECK1 ; No character, decrement counter CPI 24 ; If they sent a CTL-X, abort now CZ CKCAN ; Check for cancel CPI 67 ; If they sent a 'C', go to work JZ SKMD7 JMP CCHECK0 ; None of these, wait some more ; CCHECK1:DCR E ; One less to go JNZ CCHECK JMP ACKMSG ; Abort if timed out and no character ; ; ; Got a 'C' so either send the file or show all finished with batch ; SKMD7: LDA FILCNT ; Remote is ready, anything to send? ORA A JZ KMDEND ; If no more files, terminate batch mode DCR A ; Decrement it for this one STA FILCNT ; ; ; Now send the 128-byte file name record ; SKMD8: XRA A STA KFLG MVI A,1 ; Send SOH STA ACKCHK ; Temporary flag CALL SEND ; Send the SOH character to the modem CALL SNDHNM ; Send header (record number, inverse) CALL SNDREC ; Send a 128 byte record CALL SNDCRC ; Send a two byte CRC CALL GTACK CPI 6 JNZ SKMD8 ; If not 'ACK' send again ; XRA A STA ACKCHK ; Reset the flag for normal GTACK use CALL GTSPD1 JC SKMD9 ; Don't allow 1k blocks if 300 bps MVI A,1 STA KFLG ; Now change to 1k for normal file xfer ; SKMD9: XRA A ; Clear the carry flag STA ERRCT ; Start fresh for the main file RET ;..... ; ; KMDEND: XRA A ; Reset the pointers STA ACKCHK ; Reset the flag for normal GTACK use LHLD RECPTR ; Reset the pointers MOV M,A STA RCDCNT ; Reset the record counter STA KFLG ; Show in 128 size now MVI A,1 ; Send a start of header CALL SEND CALL SNDHNM ; This header is a zero count CALL SNDREC ; Send an empty record CALL SNDCRC ; Send the CRC for the empty record STC ; Set the carry flag to show all done RET ;..... ; ; end of send KMD batch name ;----------------------------------------------------------------------- ; ; ; Scans CMDBUF counting names and putting delimiter (space) after last ; name ; SCAN: LXI D,CMDBUF ; Save original TBUF contents in CMDBUF LXI H,0080H MVI B,128 CALL MOVE LXI H,CMDBUF MOV C,M MVI B,0 INX H DAD B ; Now pointing at space after last char MVI M,32 ; Put in the space LXI H,CMDBUF ; Get the count again MOV B,M INX H ; Skip the first space INR B ; SCAN1: INX H ; On first entry HL points to 1st char DCR B ; 1st go-thru B is count to last space JZ SCAN5 MOV A,M ; Look for the first space CPI 32 JNZ SCAN1 ; SCAN2: INX H ; Eat extra spaces DCR B JZ SCAN5 MOV A,M CPI 32 JZ SCAN2 SHLD BGNMS ; Save start of names in TBUFF INR B DCX H ; SCAN3: INX H DCR B JZ SCAN5 MOV A,M CPI 32 JNZ SCAN3 LDA NAMECT INR A STA NAMECT CPI 255 ; Limit is 255 files RZ ; SCAN4: INX H ; Eat spaces DCR B JZ SCAN5 MOV A,M CPI 32 JZ SCAN4 JMP SCAN3 ;... ; ; SCAN5: LDA NAMECT ; Were there any names? ORA A RNZ ; Yes POP H ; Remove calls from stack POP H JMP OPTERR ; Bail out to avoid BDOS error ;..... ; ; SCANL: MVI B,8 ; Scan file name addressed by HL ; TSTNAM: MOV A,M CPI '*' ; If '*' found, fill in rest of field JZ FILL1 ; With '?' for ambiguous name. INX H DCR B JNZ TSTNAM JMP FILL2 ; FILL1: CALL FILL4 ; FILL2: MVI B,3 ; Scan and fill name 'type' field ; FILL3: MOV A,M ; Specified above CPI '*' JZ FILL4 INX H DCR B JNZ FILL3 RET ;... ; ; FILL4: MVI M,'?' ; Routine transfers '?' INX H DCR B JNZ FILL4 RET ;..... ; ; ;----------------------------------------------------------------------- ; ; Show the file name as stored in the FCB but in CP/M format ; SHOWFIL:CALL ILPRTL ; Show locally only DB 'File name: ',0 LXI H,93 XRA A STA FTYCNT MVI C,11 ; PRNAM: CALL FTYTST INX H DCR C JNZ PRNAM RET ;..... ; ; FTYTST: LDA FTYCNT INR A STA FTYCNT CPI 9 ; Are we at the file type? JZ SPCTST ; Go if so ; ENDSPT: MOV A,M CPI 32 ; Test for space CNZ CTYPE ; Type if not RET ;..... ; ; SPCTST: MOV A,M CPI 32 ; Test for space in 1st file type byte RZ ; Do not output period if space MVI A,46 CALL CTYPE JMP ENDSPT ; Output 1st file type byte ;..... ; ; ;----------------------------------------------------------------------- ; ; Loads the batch file names into the storage buffer ; TNMBUF: XRA A STA FILCNT ; Reset the file count CALL SCAN LXI H,NAMBUF ; Start of buffer into NBSAVE SHLD NBSAVE ; Save address of 1st name ; TNLP1: CALL TRTOBUF ; Move a filename into FCBBUF LXI H,92 LXI D,FCBBUF CALL CMDLINE ; Parse name to CP/M format ; TNLP2: CALL MFNAM ; Search for names (wildcard format) JC NEXTNM MVI C,35 LXI D,92 CALL 5 LHLD 125 ; Get number of records in this file MOV A,H ORA L JZ TNLP2 ; If no records, don't copy this file SHLD DIRSIZ ; Save temporarily LDA ZCPR ORA A JZ TNLP3 CALL GTWHL ; WHEEL byte set for SYSOP use? JNZ TNLP5 ; If yes, let him transfer any file ; TNLP3: LDA 93 ; Tagged library file to not send? ANI 128 JNZ TNLP2 ; If set, do not send LDA 94 ; Special tag? ANI 128 JNZ TNLP2 ; If set, do not send LDA 102 ; It is a .SYS file? ANI 128 JNZ TNLP2 ; If set, do not send LDA NOLBS ORA A JNZ TNLP4 LXI H,103 ; Last character in the file extent MOV A,M ANI 127 ; Strip off the high bit CPI '#' JZ TNLP2 ; If set, do not send ; TNLP4: LDA NOCOMS ORA A JNZ TNLP5 LXI H,103 ; Last character in the file extent MOV A,M ANI 127 ; Strip off the high bit CPI 'M' ; Do not allow '.COM' files to be sent JNZ TNLP5 ; If not, file is ok to send DCX H MOV A,M ANI 7FH ; strip any high bit CPI 'O' JNZ TNLP5 ; If not, file is ok to send DCX H MOV A,M ANI 7FH ; Stripf off any high bit set CPI 'C' JZ TNLP2 ; If yes, ignore file ; TNLP5: LHLD NBSAVE ; Get the filename LXI D,92 ; Move it to FCB XCHG MVI B,12 CALL MOVE XCHG SHLD NBSAVE ; Address of next name LDA FILCNT ; Count files found INR A STA FILCNT CPI 255 ; Ignore more than 255 files JZ NEXTNM ; ; ; Add up the total records for all files to be sent ; LHLD DIRSIZ ; Get number of records in this file PUSH H ; Save for lat;r XCHG ; Put record count into 'DE' LHLD TOTREC ; Get record count up to this file DAD D ; Add this file to previous total SHLD TOTREC ; New total record count POP H ; Get the length of this file LXI D,15 ; Bring up to closest 2k size DAD D INX D ; Divide result by 16 CALL DVHLDE ; Divide HL by DE MOV H,B MOV L,C ; NOREM: XCHG LHLD BLOKK ; Current number of 2k blocks needed DAD D SHLD BLOKK JMP TNLP2 ;... ; ; NEXTNM: LXI H,NAMECT ; Count names found DCR M JNZ TNLP1 LXI H,NAMBUF ; Save start of buffer SHLD NBSAVE RET ;..... ; ; ;----------------------------------------------------------------------- ; ; TRANS: LDAX D ; Transfer from command line to FCB INX D ; Up to number of chars specified CPI 13 ; By 'C' reg. Keep scanning field RZ ; Without transfer until a delimiting CPI 46 ; Field char such as '.', blank, or RZ ; CR (for end of commmand line). CPI 32 RZ DCR C JM TRANS ; Once C-reg is less than zero, keep MOV M,A ; Reading command line but do not INX H ; Transfer to FCB. JMP TRANS ;..... ; ; ;----------------------------------------------------------------------- ; ; Places next name in buffer so 'CMDLINE' may parse it ; TRTOBUF:LHLD BGNMS MVI B,0 LXI D,FCBBUF+2 ; TBLP: MOV A,M CPI 32 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 32 JZ TRBFEND SHLD BGNMS LXI H,FCBBUF+1 ; Put # chars before name MOV M,B RET ;..... ; ; ;----------------------------------------------------------------------- ; ; LCASE: CPI 65 ; If less than capital 'A' ignore RC CPI 91 ; If more than capital Z' ignore RNC ORI 32 ; Change to lower case RET ;..... ; ; UCASE: CPI 97 ; Changes lower case character in 'A' RC ; register to upper case. CPI 123 ; See if more than small 'Z' RNC ANI 95 RET ;..... ; ; ;======================================================================= ; ; SUBROUTINES ; ;======================================================================= ; ; ; Add the time of the last up/download to BYE5's time-on-system byte ; ADDTON: LDA BYE5 ORA A RZ MVI C,79 CALL CKBDOS ; Get value of TON and RTC address LXI D,7 ; Offset from RTC to TON DAD D ; HL now contains TON address PUSH H ; Save for now LHLD RECDNO SHLD RCNT CALL XTIM ; Calculate transfer time POP H ; Get TON address MOV A,M ; And current value INR A ; Bump it one ADD C ; Add transfer time MOV M,A ; And put it in BYE5 RET ;..... ; ; ; Catches anything on the modem input and ignores, so can wait for what ; we expect to receive ; CATCH: CALL KDINST ; Check modem status for any characters RZ ; If none, all checked CALL KDINP ; Else get the garbage character JMP CATCH ; Keep going until none remaining ;..... ; ; ; Check next character to see if a space or non-space, file name error ; if no ASCII character. ; CHKFSP: LDA BCHFLG ; Requesting batch mode now? ORA A JZ CHKFSP2 ; Exit if not LDA SNDFLG ; Sending batch? ORA A JZ CHKFSP2 ; If yes, exit DCR B JZ CHKFSP1 INR B JMP CHKFSP2 ; CHKFSP1:POP H ; Do not return to LOGDU RET ; Return instead to SNDFL ;... ; ; CHKFSP2:DCR B JZ NFN ; Error if end of chars. MOV A,M CPI 33 RNC ; Ok if valid character so return INX H JMP CHKFSP ; Loop for next character ;..... ; ; ; Check next character to see if a space or non-space, go to menu if a ; command error. ; CHKSP: LDA BCHFLG ; Requesting batch mode? ORA A JZ CHKSP2 ; Exit if not LDA SNDFLG ; Sending in batch mode now? ORA A JZ CHKSP2 ; If yes, exit DCR B JZ CHKSP1 INR B JMP CHKSP2 ; CHKSP1: POP H ; Don't return to LOGDU RET ; Return to SNDFIL ;... ; ; CHKSP2: DCR B JZ OPTERR INX H MOV A,M ; Get the character there CPI 32 ; Space character? RET ; JZ = space, JNZ = non-space ;..... ; ; ;----------------------------------------------------------------------- ; ; I/O drivers using BYE5's extends BDOS calls ; CONIN: LDA BYE5 ORA A JNZ CONIN1 MVI C,1 ; Else get the character from console JMP 5 ; CONIN1: PUSH B PUSH D PUSH H ; Save the registers MVI C,67 ; Console input call CALL 5 ; Character returned in 'A' CPI 24 ; Local abort requested? JNZ CONIN2 ; No, else STA SYSABT ; Store it ; CONIN2: JMP BDOSEX ; Restore registers ; KONOUT: LDA BYE5 ORA A JZ CONOUT PUSH B PUSH D PUSH H MVI C,68 ; Console output call (char is in 'E') CALL 5 JMP BDOSEX ; CONSTAT:LDA BYE5 ORA A JNZ CONST1 MVI C,11 JMP 5 ; CONST1: PUSH B PUSH D PUSH H MVI C,66 CALL 5 ; Console status call JMP BDOSEX ; KDCARCK:LDA BYE5 ORA A JZ MDCARCK PUSH B PUSH D PUSH H MVI C,65 ; Carrier check CALL 5 JMP BDOSEX ; KDINP: LDA BYE5 ORA A JZ MDINP PUSH B PUSH D PUSH H MVI C,64 ; Modem input CALL 5 ; Character returned in 'A' JMP BDOSEX ; KDINST: LDA BYE5 ORA A JZ MDINST PUSH B PUSH D PUSH H MVI C,61 ; Modem input status CALL 5 JMP BDOSEX ; KDOUTP: PUSH PSW LDA BYE5 ORA A JZ MDOUTP POP PSW PUSH B PUSH D PUSH H MVI C,63 ; Modem output MOV E,A ; Put character in 'E' CALL 5 JMP BDOSEX ; KDOUTST:LDA BYE5 ORA A JZ MDOUTST PUSH B PUSH D PUSH H MVI C,62 ; Modem output status CALL 5 ; BDOSEX: POP H POP D POP B RET ; Restore registers and return ;..... ; ; ; Ok, it's one of our commands, Let's handle it ; CKBDOS: LDA BYE5 ; Using BYE5? ORA A JNZ 5 ; If yes, handle normally through BYE5 ; MOV A,C ; Get the BDOS selection CPI 69 JZ CKBD4 CPI 75 ; WRTLOC not used without BYE5 JZ CKBD4 CPI 79 ; TON and RTC not used without BYE5 JZ CKBD4 CPI 81 ; MXTIME on system needs BYE5 JZ CKBD4 CPI 83 ; TOS needs BYE5 JZ CKBD4 CPI 85 ; ACCESS to files needs BYE52 JZ CKBD4 RET ; CKBD4: XRA A RET ;..... ; ; ;----------------------------------------------------------------------- ; ; ;----------------------------------------------------------------------- ; ; Finished, clean up and return to CP/M ; EXIT: MVI C,75 MVI E,0 CALL CKBDOS ; Reset WRTLOC flag if using BYE5 LDA TIMEON ORA A JZ EXIT1 LDA CLOCK ORA A JZ EXIT1 LDA DTOS ORA A JZ EXIT1 CALL KDCARCK ; Still have a carrier? JZ EXIT1 ; If not can't use "RECV:" CALL WAIT1 ; Insures other end is finished MVI C,83 CALL CKBDOS ; Tell BYE5 to print time-on-system ; ; ; See if BYE5's DISKLOG was originally on, if yes, turn it back on ; EXIT1: LDA DSKFLG ; See if DISKLOG was originally on ORA A JZ EXIT2 ; If not, all done MVI C,86 ; BYE5 DISKLOG status byte MVI E,1 ; Done with KMD now, turn it back on CALL 5 ; ; ; Restore original drive/user area ; EXIT2: LDA OLDDRV ; Restore the original drive CALL RECDRX LDA OLDUSR ; Restore the original number CALL RECARE CALL RSDMA ; Reset to default DMA address LDA TIMEON ORA A JZ EXIT3 LDA TLIMIT ; Restore MXTIME/status MVI C,81 MOV E,A CALL CKBDOS ; EXIT3: XRA A ; Clear the register and carry bit LHLD STACK ; Get original return adress back SPHL ; Put on the stack pointer ; ; ;----------------------------------------------------------------------- ; message file routine ; LDA MSGFLG ; Message file upload? ORA A RZ ; If not, all done ; CALL ILPRTB ; Show to remote also DB 13,10,10 DB '++ Loading special message file handler ++' DB 13,10,10,0 INR A STA CONONL ; Set to local display only MVI C,0 ; Number of characters (stuff in TBUF) LXI D,0080H+1 ; Start of buffer LDA DSKSAV ; Get current drive ADI 65 ; Convert binary to ASCII CALL MSGSTR ; Stuff in command line buffer LDA USRSAV ; Get current user CPI 10 ; <10? JC MSGF1 ; Yes ; ORA A ; Clear flags DAA ; Decimal adjust RAR ; Shift down tens digit RAR RAR RAR ANI 15 ; Mask out tens digit ADI '0' ; Make it ASCII CALL MSGSTR LDA USRSAV ORA A ; Clear flags DAA ; Decimal adjust ANI 15 ; Mask out singles digit ; MSGF1: ADI '0' ; Make it ASCII CALL MSGSTR MVI A,':' ; Put in a colon CALL MSGSTR LXI H,93 ; Get uploaded file name MVI B,8 ; 8 characters in file name ; MSGF2: MOV A,M CPI 32 ; Skip spaces CNZ MSGSTR INX H DCR B JNZ MSGF2 MOV A,M CPI 32 JZ MSGF4 MVI A,46 ; Add a period between filename and ext. CALL MSGSTR MVI B,3 ; 3 characters in file extent ; MSGF3: MOV A,M CPI 32 ; Done if a space JZ MSGF4 CPI 0 JZ MSGF4 CALL MSGSTR INX H DCR B JNZ MSGF3 ; MSGF4: MOV A,C STA 0080H ; Save number of character in TBUF MVI A,194 ; Stuff JNZ instruction STA 0000H ; Make BYE5 load/run msg file utility ORA A ; Make sure NZ flag set so JNZ will jump JMP 0000H ;..... ; ; MSGSTR: STAX D ; Short routine to stuff A in (DE) and INX D ; Increment pointer and character count INR C RET ;..... ; ; end of message file routine ;----------------------------------------------------------------------- ; ; ; Include the filename when transferring a file. Check to see if from ; an .ARC, .ARK or .LBR group first. ; FILNAM: CALL ILPRT DB 13,10,'File name - ',0 JMP FILN1 ; FILNAM1:CALL ILPRT DB 13,10,'File name: ',0 ; FILN1: LDA OPTSAV ; See if a file from .ARC, .ARK or .LBR CPI 65 JNZ FILN2 ; If not exit LXI H,MEMFCB ; Get the member name CALL SHONM1 ; Display CALL ILPRT DB ' from ',0 ; Then show group name ; FILN2: LXI H,93 JMP SHONM1 ;..... ; ; ; Check to see if SYSOP has typed a function key ; FUNCHK: PUSH PSW CALL CONSTAT ; See if SYSOP has typed a key ORA A CNZ CONIN ; Yes, treat as function key POP PSW RET ;..... ; ; ; Get the current speed of the remote user ; GTSPD: PUSH H LHLD MSPEED ; Address of MSPEED MOV A,M ; Get the value there POP H RET ;..... ; ; GTSPD1: CALL GTSPD ; Get the current speed CPI 5 ; See if 1200 bps RET ;..... ; ; ; Get WHEEL byte ; GTWHL: PUSH H LHLD WHEEL ; Address of WHEEL byte MOV A,M ; Get the value there ORA A ; Set the zero flag POP H RET ;..... ; ; ; Get Disk and User from DUSAVE and log in if valid. ; GETDU: LDA BCHFLG ; Requesting batch mode? ORA A JZ GETDU1 ; If not, exit LDA SNDFLG ; Sending batch? ORA A JNZ GETDU2 ; If not, exit ; GETDU1: CALL CHKFSP ; See if a file name is included SHLD SAVEHL ; Save location of the filename ; GETDU2: LDA PRVTFL ; Uploading to a private area? ORA A JNZ TRAP1 ; If yes, going to a specified area LXI H,DUSAVE ; Point to drive/user LDA OLDDRV ; Get current drive STA DUD ADI 65 STA RCVDRV MOV A,M ; Get 1st character CPI 48 JC GETDU3 CPI 58 JC NUMER1 ; GETDU3: STA RCVDRV ; Allows SYSOP to upload to any drive CPI 64 JC NUMER ; Satisfied with current drive SUI 65 STA DUD LDA ZCPR ORA A JZ GETDU4 CALL GTWHL ; WHEEL set for SYSOP use? LDA DUD ; Get the value back JNZ GETDU6 ; GETDU4: LDA USEMAX ORA A JNZ GETDU5 LDA MAXDRV MOV C,A LDA DUD ; Get the value back CMP C JNC ILLDU ; Drive selection not available JMP GETDU6 ; GETDU5: LDA DUD PUSH H LHLD DRIVMAX ; Point to max drive byte INR M CMP M ; And check it PUSH PSW ; Save flags from the CMP DCR M ; Restore max drive to normal POP PSW ; Restore flags from the CPM JNC ILLDU POP H ; GETDU6: INX H ; Get 2nd character ; NUMER: MOV A,M CPI 58 JZ OK7 ; Colon for drive only, no user number CALL CKNUM ; Check if numeric MOV B,A ; Save character LDA BCHFLG ; Using batch mode? ORA A JZ NUMER1 ; Skip next part if not using batch LDA SNDFLG ; Receiving in batch? ORA A JNZ NUMER1 ; Yes, can use normal drive/user ; NODU: CALL ERXIT DB '++ D/U can not be specified for batch files ++','$' ; NUMER1: MOV A,B ; Get the value back SUI 48 ; Convert ASCII to binary STA DUU ; Save it INX H ; Get 3rd character if any MOV A,M CPI 58 JZ OK1 LDA DUU CPI 1 ; Is first number a '1'? JNZ ILLDU MOV A,M CALL CKNUM SUI 38 STA DUU INX H ; Get 4th (and last character) if any MOV A,M CPI 58 JNZ ILLDU ; OK1: LDA OPTSAV ; Get the option back CPI 82 ; Receiving a file? LDA DUU ; Get desired user area JZ OK2 ; Yes, can not use special download area LDA SPLDRV SUI 65 MOV C,A LDA DUD ; Get desired drive CMP C ; Special download drive requested? JNZ OK2 ; If none, exit LDA SPLUSR MOV C,A LDA DUU ; Get user area requested CMP C ; Special download area requested? JZ OK5 ; If yes, process request ; OK2: LDA ZCPR ORA A JZ OK3 CALL GTWHL ; WHEEL set for SYSOP use? LDA DUU ; Restore desired user area STA RCVUSR ; Allows SYSOP to upload anywhere JNZ OK5 ; If yes, let him have all user areas ; OK3: LDA USEMAX ORA A JNZ OK4 LDA MAXUSR ADI 1 MOV C,A LDA DUU ; Restore desired user area CMP C ; Check for maximum user download area JNC ILLDU ; Error if more (and not special area) JMP OK5 ; OK4: LDA DUU PUSH H LHLD USRMAX ; Point at maximum user byte CMP M ; And check it JNC ILLDU POP H ; OK5: MOV E,A LDA SETAREA ORA A JNZ OK6 MOV A,E STA NOTUSR+1 ; Store requested user area MVI A,62 ; 'MVI A,--' instruction STA NOTUSR ; OK6: MVI C,32 CALL 5 ; Set to requested user area ; OK7: LDA DUD ; Get drive MOV E,A LDA SETAREA ORA A JNZ OK8 MOV A,E ; Get the value back ADI 65 STA NOTDRV+1 ; Store requested drive MVI A,62 ; 'MVI A,--' instruction STA NOTDRV ; OK8: MVI C,14 CALL 5 ; Set to requested drive JMP TRAP1 ; Now find file selected ;..... ; ; ; Downloading from the special private area, set that drive/user ; SETSPL: LDA SPLUSR MVI C,32 ; Set to requested user area MOV E,A ; Get the special download user area CALL 5 LDA SPLDRV SUI 65 MVI C,14 MOV E,A ; Get the special download drive JMP 5 ; Set to requested drive, return ;..... ; ; ; Shows available space on upload disk/area. Uses KDRV data area which ; must be loaded before calling this routine. (So KSHOW will work with ; user specified disk if SETAREA option is not set YES.) ; ; Print the free space remaining for the received file ; KDRV: DB 0 ; Drive stored here before calling KSHOW ; KSHOW: LDA KDRV ; Get drive ('A','B','C',etc.) SUI 65 ; Convert to numeric (0,1,2,etc.) MVI C,14 ; Select the directory drive to retrieve MOV E,A ; Stuff in E for BDOS call CALL 5 ; The proper allocation vector MVI C,31 ; It's 2.X or MP/M...request DPB CALL 5 INX H INX H MOV A,M ; Get block shift STA BLKSHF INX H ; Bump to block mask MOV A,M INX H INX H MOV E,M ; Get max block # INX H MOV D,M XCHG SHLD BLKMAX ; Save it XCHG INX H MOV E,M ; Get directory size INX H MOV D,M XCHG ; ; ; Calculate # of K free on selected drive now so that the FREE figure ; will not reflect either the creation or additions to the SD.DIR file ; (which we would probably erase or move anyway). MVI C,12 ; Get CP/M version number CALL 5 MOV A,L ; Get returned version number CPI 48 ; 3.0? JC FREE20 ; Use old method if not LDA KDRV ; Get drive # SBI 65 ; Change from ASCII to binary MVI C,46 MOV E,A ; Use new Compute Free Space BDOS call CALL 5 MVI C,3 ; Answer is a 24-bit integer ; FRE3L1: LXI H,130 ; Answer is in 1st 3 bytes of DMA adr MVI B,3 ; Convert it from sectors to K ORA A ; By dividing by 8 ; FRE3L2: MOV A,M RAR MOV M,A DCX H DCR B JNZ FRE3L2 ; Loop for 3 bytes DCR C JNZ FRE3L1 ; Shift 3 times LHLD 128 ; Now get result in K JMP SAVFRE ; Go store it ; FREE20: MVI C,27 ; Get address of allocation vector CALL 5 XCHG LHLD BLKMAX ; Get its length INX H LXI B,0 ; Init block count to 0 ; GSPBYT: PUSH D ; Save alloc address LDAX D MVI E,8 ; Set to process 8 blocks ; GSPLUP: RAL ; Test bit JC NOTFRE INX B ; NOTFRE: MOV D,A ; Save bits DCX H ; Count down blocks MOV A,L ORA H JZ ENDALC ; Quit if out of blocks MOV A,D ; Restore bits DCR E ; Count down 8 bits JNZ GSPLUP ; Do another bit POP D ; Bump to next byte.. INX D ; Of alloc. vector JMP GSPBYT ; Process it ; ENDALC: POP D ; Clear stack of allocation vector ptr. MOV L,C ; Copy block to HL MOV H,B LDA BLKSHF ; Get block shift factor SUI 3 ; Convert from sectors to K JZ SAVFRE ; Skip shifts if 1K blocks... ; ; Return free in HL FREKLP: DAD H ; Multiply blocks by K/BLK DCR A JNZ FREKLP ; ; ; Print the amount of free space remaining on the selected drive ; SAVFRE: CALL DECOUT CALL ILPRT DB 'k free space is available',0 RET ;..... ; ; ; Log into drive and user (if specified). If none mentioned, it falls ; through to 'TRAP' routine for normal use. ; LOGDU: LXI H,0080H ; Point to default buffer command line MOV B,M ; Store number of characters in command INR B ; Add in current location ; LOG1: CALL CHKSP ; Skip spaces to find 1st command JZ LOG1 ; LOG2: CALL CHKSP ; Skip 1st command (non-spaces) JNZ LOG2 INX H CALL CHKFSP ; Skip spaces to find 2nd command SHLD SAVEHL ; Save start address of the 2nd command ; ; ; Now point to the first byte in the argument, i.e., if it was of format ; similar to: B6:HELLO.DOC then we point at the drive character 'B'. ; LXI D,DUSAVE MVI C,4 ; Drive/user is 4 characters maximum ; CPLP: MOV A,M CPI 33 ; Space or return, finished JC TRAP STAX D INX H INX D CPI 58 JZ GETDU ; If colon, get drive/user and log in DCR B ; One less position to check DCR C ; One less to go JNZ CPLP ; ; ; Check for no file name or ambiguous name ; TRAP: LDA SPLFL ; Downloading from a private area? ORA A CNZ SETSPL ; If yes, set special drive/user area ; TRAP1: CALL MOVEFCB ; Move the filename into the file block LXI H,93 ; Point to file name MOV A,M ; Get first character of file name CPI 32 ; Any there? JZ NFN ; If not, display error messagename MVI B,11 ; 11 characters to check ; TRLOOP: MOV A,M ; Get char from FCB CPI '?' ; Ambiguous? JZ TRERR ; Yes, exit with error message CPI '*' ; Even more ambiguous? JZ TRERR ; Yes, exit with error message CPI '_' ; CP/M doesn't like underlines JNZ TRLOOP1 ; If not, exit MVI M,'-' ; Convert to a hyphen ; TRLOOP1:INX H ; Point to next character DCR B ; One less to go JNZ TRLOOP ; Not done, check some more RET ;..... ; ; NFN: CALL ERXIT ; Print message, exit DB '++ No file name requested ++','$' ;..... ; ; TRERR: LDA BCHFLG ORA A RNZ ; Wildcards are ok in batch mode CALL ERXIT ; Print message, exit DB CR,LF,'++ Wild-card options are not valid ++','$' ;..... ; ; CKNUM: CPI '0' JC ILLDU ; Error if less than ascii '0' CPI '9'+1 RC ; Error if more than ascii '9' ;..... ; ; ILLDU: CALL ERXIT DB '++ Improper drive/user combination ++','$' ;..... ; ; ; Receive a record - returns with carry bit set if EOT received ; RCVRECD:XRA A ; Initialize error count to zero STA ERRCT ; RCVRPT: CALL FUNCHK ; Check function keys LDA SYSABT ; Want to abort? ORA A JNZ ABORT ; If yes, exit MVI B,10 ; 10-seconds to get first character LDA FRSTIM ; Have we started, yet? ORA A JNZ $+5 ; If yes, skip next line MVI B,5+1 ; Check every 6 seconds until started CALL RECV ; Get any character received JC RCVSTOT ; Timeout error if no character received CPI 1 ; See if it is SOH JZ RCVSOH ; Got SOH, get record CPI 2 ; See if it is STX for 1k blocks JZ RCVSTX ; Got STR, get record CPI 24 ; Was it a CTL-X to abort? CZ CKCAN ; If yes, check for aborting ORA A ; Get another character, if a null JZ RCVRPT ; Ignore nulls CPI 123 ; V.22 synch character, ignore JZ RCVRPT CPI 251 ; V.22 synch character with high bit set JZ RCVRPT CPI 4 ; See if end of transmission STC ; Set carry RZ ; Return with carry set CPI 67 ; Ignore our own character coming back JZ RCVRPT CPI 75 ; Ignore our own character coming back JZ RCVRPT CPI 21 ; Ignore our own character coming back JZ RCVRPT CALL ILPRTL ; Show locally only DB 13,10,0 MOV A,B CALL HEXO CALL ILPRTL DB 'H received not SOH ',13,10,0 ; ; ; Didn't get SOH or EOT or did not get valid header so purge the line, ; then send NAK. ; RCVSR: CALL WAIT1 ; Get anything coming in and discard LDA SYSABT ; Want to abort? ORA A JNZ ABORT ; If yes, exit LDA FRSTIM ; Get first time switch ORA A ; Has first 'SOH' been received? MVI A,21 JNZ RCVSR1 ; Yes, then send 'NAK' LDA CRCFLG ; Get the 'CRC' flag ORA A ; 'CRC' in effect? MVI A,21 ; Put 'NAK' in 'A' register JZ RCVSR1 ; No, send the 'NAK' for checksum MVI A,67 ; Tell sender we have 'CRC' CALL SEND LDA KFLG ; Requesting 1k transmissions? ORA A JZ RCVSR1 ; If not, exit MVI A,75 ; Tell sender we also have 1k capability ; RCVSR1: CALL SEND ; The 'NAK' or 'CRC' request LDA ERRCT ; Get the error count INR A ; Increment error count STA ERRCT ; Store new value MOV B,A ; Keep the error count for now LDA FRSTIM ; Have we gotten under way yet? ORA A MOV A,B ; get the value back JZ RCVSR2 ; If not, exit CPI 10 ; 10 errors the limit, once under way JNC ABORT ; Abort if over the limit CALL RDCOUNT ; Display record count before repeating JMP RCVRPT ; Less than 10, keep going ; RCVSR2: CPI 7 ; 7 times for 1k/CRC yet? (42 seconds) JC RCVRPT ; Keep trying if less XRA A ; Else flip to checksum mode STA CRCFLG MOV A,B ; Get the count back CPI 3 ; Another 3 times for checksum? JC RCVRPT ; If less, try again, quit at 60 seconds JMP ABORT ;..... ; ; ; Aborts with 1 CTL-X if first time flag is not set, two otherwise ; CKCAN: LDA FRSTIM ; First time flag set yet? ORA A JZ CKCAN1 ; If not, Abort and close file LDA XCANCL ; 3 or more CTL-X's to cancel? ORA A RZ ; If not, ignore and return MVI B,2 ; Maximum of 2 seconds for extra CTL-X CALL RECV RC ; No additional character, ignore CTL-X CPI 24 ; If a 2nd CTL-X, abort and close file RNZ ; If this was not a CTL-X, ignore MVI B,2 ; Maximum of 2 seconds for extra CTL-X CALL RECV RC ; No additional character, ignore CTL-X CPI 24 ; If a 3rd CTL-X, abort and close file RNZ ; If this was not a CTL-X, ignore ; CKCAN1: POP H ; Reset stack for CALL CKCAN JMP ABORT ; Go abort ;..... ; ; ; Timed out on receive ; RCVSTOT:LDA FRSTIM ; First time flag set yet? ORA A JZ RCVSR ; If not, don't show an error CALL ILPRTL ; Show locally only DB '++ Timeout waiting for character ++',13,10,0 JMP RCVSR ; Bump error count, etc. ;..... ; ; ; Got a STX - set KFLG for 1k ; RCVSTX: STA KFLG ; Set the 1k flag STA CRCFLG ; Insure in CRC mode for 1k blocks JMP RCVS1 ; ; ; Got SOH - get block number, block number complemented ; RCVSOH: XRA A STA KFLG ; If SOH, clear the 1k flag ; RCVS1: MVI B,5 ; Timeout = 5 seconds MOV A,B ; Get something to store STA FRSTIM ; Indicate first 'SOH' or 'STX' recvd. CALL RECV ; Get block number JC RCVSTOT ; Got timeout MOV D,A ; Save block number MVI B,5 ; Timeout = 5 seconds CALL RECV ; Get complimented record number JC RCVSTOT ; Timeout CMA ; Get the complement CMP D ; Same as original block number? JZ RCVDATA ; Yes, get data ; ; ; Got bad record number in header ; CALL ILPRTL ; Show locally only DB '++ Error in header ++',13,10,0 JMP RCVSR ; Go check error limit and send NAK ;..... ; ; RCVDATA:MOV A,D ; Get record number STA RCVCNT ; Save it MVI C,0 ; Initialize checksum LXI H,0 ; Initialize CRC SHLD CRCVAL ; Clear CRC counter LXI D,128 ; For 128 character blocks LDA KFLG ; Using 1k blocks? ORA A JZ $+6 ; If not, skip next line LXI D,1024 ; If using 1k blocks LHLD RECPTR ; Get buffer address ; RCVCHR: MVI B,5 ; 5 sec timeout CALL RECV ; Get the character JC RCVSTOT ; Timeout MOV M,A ; Store the character INX H ; Point to next character DCX D ; One less to go MOV A,D ; See if 'D' and 'E' are both empty ORA E JNZ RCVCHR ; No, get next character LDA CRCFLG ; Using 'CRC'? ORA A JNZ RCVCRC ; If yes go get 'CRC' ; ; ; Verify checksum ; MOV D,C ; Save checksum MVI B,5 ; Timeout length CALL RECV ; Get checksum JC RCVSTOT ; Timeout CMP D ; Checksum ok? JZ CHKSNUM ; Yes, exit CALL ILPRTL ; Show locally only DB '++ Checksum error ++',13,10,0 JMP RCVSR ; Go check the error limit and send NAK ;... ; ; ; Got a record, it's a duplicate if equal to the previous number, it's ; OK if previous + 1 record ; CHKSNUM:LDA RCVCNT ; Get received record number MOV B,A ; Save it LDA RCDCNT ; Get previous record number CMP B ; Rrevious record repeated? JZ RCVACK ; If yes 'ACK' to catch up INR A ; Increment by 1 for 120 character block CMP B ; Match this one we just got? JNZ ABORT ; No match, stop the sender, exit RET ; Else return with carry not set, was ok ;..... ; ; ; Receive the Cyclic Redundancy Check characters (2 bytes) and see if ; the CRC received matches the one calculated. If they match, get next ; record, else send a NAK requesting the record be sent again. ; RCVCRC: MVI E,2 ; Number of bytes to receive ; RCVCRC2:MVI B,5 ; 5 second timeout CALL RECV ; Get CRC byte JC RCVSTOT ; Timeout DCR E ; Decrement the number of bytes JNZ RCVCRC2 ; Get both bytes CALL CRCCHK ; Check received CRC against calc'd CRC ORA A ; Is CRC okay? JZ CHKSNUM ; Yes, go check record numbers CALL ILPRTL ; Show locally only DB '++ CRC error ++',13,10,0 JMP RCVSR ; Go check error limit and send NAK ;..... ; ; ; Previous record repeated, due to the last ACK being garbaged. ACK it ; so sender will catch up ; RCVACK: CALL SNDACK ; Send the ACK JMP RCVRECD ; Get next block ;..... ; ; ; Send an ACK for the record ; SNDACK: MVI A,6 ; Get 'ACK' JMP SEND ; And send it ;..... ; ; ; Send the record header ; ; Send SOH, block number and complemented block number (3 bytes total) ; SNDHDR: LDA KFLG ; Sending 1k blocks? ORA A MVI A,2 ; If yes, send a STX rather than SOH JNZ $+5 MVI A,1 ; Send start of header CALL SEND ; SNDHNM: LDA RCDCNT ; Send the current record number CALL SEND LDA RCDCNT ; Get the record number again CMA ; Complemented JMP SEND ; From SENDHDR ;..... ; ; ; Send the data record ; SNDREC: MVI C,0 ; Initialize checksum LXI H,0 ; Initialize CRC SHLD CRCVAL LDA KFLG ; Sending 1k blocks? ORA A LXI D,1024 JNZ $+6 ; If yes, skip the next line LXI D,128 LHLD RECPTR ; Get buffer address ; SENDC: MOV A,M ; Get a character CALL SEND ; Send it INX H ; Point to next character DCX D MOV A,E ORA D JNZ SENDC ; If DE not zero, keep going RET ; From SENDREC ;..... ; ; ; Send the CRC or checksum value, whichever appropriate ; SNDCHK: LDA CRCFLG ; See if sending 'CRC' or 'checksum' ORA A JNZ SNDCRC ; If not zero, send the 'CRC' value ; ; ; Send the checksum ; SNDCKS: MOV A,C ; Send the checksum JMP SEND ; From SNDCKS ;..... ; ; ; Send the two Cyclic Redundancy Check characters. Call FINCRC to cal- ; culate the CRC which will be in 'DE' upon return. ; SNDCRC: CALL FINCRC ; Calculate the 'CRC' for this record MOV A,D ; Put first 'CRC' byte in accumulator CALL SEND ; Send it MOV A,E ; Put second 'CRC' byte in accumulator CALL SEND ; Send it XRA A ; Set zero return code RET ;..... ; ; ; After a record has been sent, and accepted, move the pointers forward ; 128 or 1024 characters for the next record. ; SETPTR: LXI D,128 ; For 128 character blocks LDA KFLG ; See if last block sent was 1k ORA A JZ $+6 ; If not, skip next line LXI D,1024 ; Else set for 1024 character blocks LHLD RECPTR ; Get the buffer pointer DAD D ; Increment for the record just sent SHLD RECPTR ; New buffer address for next block RET ;..... ; ; ; After a library transmission has been made, decrement the remaining ; records in that library file, then reset the 1k flag if less than 8 ; remaining. ; SETLBR: LDA KFLG LXI D,65535 ORA A JZ $+6 LXI D,65528 LHLD RCNT ; Alter the records-sent count DAD D SHLD RCNT ; One less transmission to go ORA A ; 'K' flag already zero? RZ ; If yes, skip the rest ; ; ; See if enough records left to use 1k protocol if requested ; SETFLG: LHLD RCNT MOV A,H ; Anything in the 'H' register? ORA A RNZ MOV A,L ; Get number of records in 'L' register CPI 8 ; At least 8 yet? RNC ; If 8 or more, keep going XRA A ; Reset the 'K' flag STA KFLG RET ;..... ; ; ; After a record is sent, a character is returned telling if it was re- ; ceived properly or not. An ACK allows the next record to be sent. A ; NAK causes the current record to be resent. If no character (or any ; character other than ACK or NAK) is received after a short wait (10 ; to 12 seconds), a timeout error message is shown and the record will ; be resent. ; GTACK: CALL CATCH ; GTACK1: MVI B,12 ; 12-seconds more for an ACK or NAK CALL RECV ; Go wait for a character JC GTATOT ; No character, timed out CPI 6 ; Was it an ACK? RZ ; If yes, return CPI 21 ; Was it a NAK? JZ GTACK2 CPI 123 ; V.22 synch character, ignore JZ GTACK1 ; If yes, ignore CPI 251 ; V.22 synch character, ignore JZ GTACK1 CPI 24 ; CTL-X to cancel attempt? CZ CKCAN ; GTACK2: MOV B,A ; Save the character LDA CHKEOT ; Sending EOT? ORA A JNZ ACKERR ; If yes, don't show error (for KMD) CALL ILPRTL DB '++ ',0 MOV A,B CPI 21 JZ GTACK3 CALL HEXO CALL ILPRTL DB 'H',0 JMP GTACK4 ; GTACK3: CALL ILPRTL DB 'NAK',0 ; GTACK4: CALL ILPRTL DB ' received not ACK ++',13,10,0 ; ; ; Timeout or error on ACK - bump error count then resend the record if ; error limit is not exceeded ; ACKERR: LDA ACCERR ; Count accumulated errors on ACK INR A ; Add in this error STA ACCERR LDA ERRCT ; Get count INR A ; Bump it STA ERRCT ; Save back CPI 10 ; At limit? JNC ACKMSG ; If yes, send error message and abort LDA ACKCHK ; Checking after a batch header? ORA A JNZ $+6 ; If yes, skip next line CALL RDCOUNT ; Else show the record count for repeat MOV A,B CPI 21 ; NAK ? JNZ GTACK1 ; If not ignore and wait for ACK or NAK RET ; And go back ;..... ; ; ; Reached error limit ; ACKMSG: CALL WAIT1 ; Wait for any input to stop MVI A,24 ; Tell remote we are quitting CALL SEND CALL SEND CALL SEND MVI B,2 ; Wait for remote to perhaps quit too CALL RECV MVI A,8 CALL SEND ; Clear any CTL-X's from buffer CALL SEND CALL SEND XRA A ; Reset flag to show remote also STA CONONL CALL ERXIT DB 13,10,'++ FILE TRANSFER ABORTED ++','$' ;..... ; ; ; Timed out, with no character - set the carry bit and return ; GTATOT: CALL ILPRTL DB '++ Timeout - no character received ++',13,10,0 JMP ACKERR ;..... ; ; ; Check the total error count vs. records sent, switch from 1k to 128 ; character transmissions if higher than operator selected value. ; GTRATIO:LDA KFLG ; Using 1k blocks? ORA A RZ ; If not, skip this routine LDA ERRCT ; See if we got any errors last record CPI 4 JNC GTRATIO1 ; If 4 or more, switch to 128 size LDA ACCERR ; See if up to minimum errors yet CPI 3 ; Had as many as three errors yet? RC ; If not, don't get excited too quickly LHLD RECDNO ; Get current record number increment LXI D,65528 ; Have not successfully sent this 1k yet DAD D ; Subtract the current increment, then XCHG ; Put in DE for now LHLD ACCERR ; Number of non-'ACK' errors in HL XCHG ; Back to normal CALL DVHLDE ; Get ratio in BC of records/hit CALL GTSPD1 ; Get current speed MVI A,70 ; for 1200 bps JZ $+5 ; If 1200, skip next line MVI A,42 ; for 2400 bps CMP C ; Compare with actual ratio RC ; Return if less hits than allowed ; GTRATIO1: XRA A ; Else reset the system to 128 STA KFLG CALL ILPRTL DB 13,10,'Aborting 1k blocks, too many hits',13,10,0 RET ;..... ; ; CKABORT:CALL CONSTAT ORA A RZ CALL CONIN CPI 24 RNZ ; ; ; Aborts send or receive routines and returns to command line ; ABORT: LXI SP,STACK CALL WAIT1 ; 1-second delay to clear input CALL CATCH LDA EOTFLG ; Timed out after only one 'EOT'? ORA A JNZ RCVQT ; Accept the file as a valid "EOT" then MVI A,24 ; Show you are cancelling CALL SEND ; They may quit also with enough CTL-X CALL SEND CALL SEND CALL WAIT1 ; 1-second delay to clear input CALL CATCH MVI A,8 CALL SEND CALL SEND CALL SEND ; ABORTX: CALL CATCH LDA OPTSAV CPI 'R' JZ RCVSABT CALL ERXIT ; Exit with abort message DB 13,10,'++ KMD ABORTED ++','$' ;..... ; ; ; Error limit exceeded, so abort ; RCVSABT:CALL CLOSFIL ; Keep whatever we got CALL DELFILE ; Delete received file LXI SP,STACK ; Reset the stack CALL ILPRTL DB 13,10,13,10,'++ RECEIVED FILE CANCELLED ++' DB 13,10,'++ UNFINISHED FILE DELETED ++',0 JMP ERXIT5 ;..... ; ; ; Deletes the received file (used if receive aborts) ; DELFILE:MVI C,19 ; Get function LXI D,92 ; Point to file CALL 5 ; Delete it INR A ; Delete ok? RNZ ; Yes, return CALL ERXIT ; No, abort DB 13,10,'++ Can''t delete received file ++','$' ;..... ; ; ; Increment record number ; INCRNO: PUSH H PUSH D XRA A STA EOTFLG ; Make sure the flag is not set LHLD RCDCNT ; Increment the transmission count INX H SHLD RCDCNT LXI D,1 ; Increment one record only LDA KFLG ; Sending 1k blocks? ORA A JZ INCRN1 ; If not, exit LXI D,8 ; If yes, increment count by 8 ; INCRN1: LHLD RECDNO ; Get current record count DAD D ; Increment that count properly SHLD RECDNO CALL RDCOUNT POP D POP H RET ;..... ; ; ; Display the record count on the local CRT ; RDCOUNT:LHLD RECDNO ; Get the record number for display LDA OPTSAV ; See if receive or send mode CPI 82 JZ RMSG CALL ILPRTL ; Show locally only DB 13,'Sending: # ',0 JMP REST ; RMSG: CALL ILPRTL ; Show locally only DB 13,'Received # ',0 ; REST: LHLD RECDNO CALL DECOUT CALL ILPRTL DB ' ',0 CALL FUNCHK ; Check for function keys RET ; From INCRNO ;..... ; ; ; See if file exists - if it exists, ask for a different name. ; CHEKFIL:LDA SETAREA ORA A JNZ CHEKF1 LDA PRVTFL ; Receiving in private area? ORA A JZ CHEKF2 ; CHEKF1: CALL RECAREA ; Set the designated area up ; CHEKF2: MVI C,17 ; See if the file already exists LXI D,92 ; Point to control block CALL 5 INR A RZ ; No, everything normal, so return ; CHEKF3: MVI B,1 CALL RECV JNC CHEKF3 ; Wait until no more characters LDA BCHFLG ; Using batch mode now? STA CONONL ; If not, send message to modem also ORA A JZ CHEKF4 ; If not, exit ; MVI A,4 CALL SEND CALL DELAY MVI A,4 CALL SEND CALL DELAY MVI A,4 CALL SEND CALL DELAY ; CHEKF4: MVI A,24 ; Else inform user and abort CALL SEND ; Several cancel requests CALL DELAY MVI A,24 CALL SEND CALL DELAY MVI A,8 CALL SEND MVI A,13 CALL SEND MVI A,10 CALL SEND CALL ERXIT ; Exit, print error message DB '++ File exists, use a different name ++','$' ;..... ; ; ; Makes the file to be received ; MAKEFIL:XRA A ; Set extent and record number to 0 STA 104 STA 124 MVI C,22 ; Get BDOS FNC LXI D,92 ; Point to FCB CALL 5 ; To the make INR A ; 0FFH=bad? RNZ ; Open ok ; ; ; Directory full - can't make file ; CALL ERXIT DB '++ Can''t create file - ' DB 'directory may be full ++','$' ;..... ; ; ; Computes record count, and saves it until a successful file-open. ; CNREC: MVI C,35 ; Computes file size LXI D,92 CALL 5 ; Read first LHLD 125 ; Get the file size SHLD RCNT ; Save total record count RET ;..... ; ; ;----------------------------------------------------------------------- ; opens file to send ; ; Opens the file to be sent ; OPNFIL: XRA A STA 104 ; For proper open STA 124 MVI C,15 ; Get function LXI D,92 ; Point to file CALL 5 ; Open it INR A ; Open ok? JNZ OPNOK ; If yes, exit LDA OPTSAV ; Get command line option CPI 65 JNZ NONAME ; Exit, if not ; OPNFIL1:LXI H,101 ; Point to the filetype CMP M JNZ OPNFIL2 MVI M,76 INX H MVI M,66 INX H MVI M,82 JMP OPNFIL ;... ; ; OPNFIL2:MVI M,65 INX H MVI M,82 INX H MVI M,67 MVI C,15 ; Get function LXI D,92 ; Point to file CALL 5 ; Open it INR A ; Open ok? JZ NOTLBR ;... ; ; OPNOK: LDA ZCPR ORA A JZ OPNOK1 CALL GTWHL ; WHEEL set for SYSOP use? JNZ OPNOK3 ; If non-zero skip all restrictions ; OPNOK1: LDA OPTSAV ; Sending a file now? CPI 83 ; If using 'S' check for tags JNZ OPNOK2 ; If using 'A' let them alone LDA 93 ; First character of file name ANI 128 ; Check bit 7 JNZ OPNOT1 ; If bit 7 is set, file is tagged LDA 94 ; Also check 'F2' for a tag ANI 128 ; Is it set? JNZ OPNOT ; If yes, cannot be downloaded ; OPNOK2: LDA NOSYS ORA A JNZ OPNOK3 LDA 102 ANI 128 JNZ NONAME ; If $SYS then fake a "file not found" ; OPNOK3: LDA BCHFLG ; Requesting batch mode? ORA A JNZ OPNOK4 ; If yes, skip library stuff LDA OPTSAV ; Get the primary option back CPI 65 ; This a .ARC, .ARK or .LBR member file? JNZ OPNOK4 ; If not, exit ; OPNOKX: CALL RSDMA ; Reset to default DMA address MVI C,20 ; Read first file record LXI D,92 CALL 5 ORA A ; Read ok? JNZ RDERR ; If not, error LDA 101 ANI 7FH CPI 65 JZ CMLP4 LHLD 142 ; Get filesize SHLD DIRSIZ ; Store LXI H,0080H MOV A,M ORA A JNZ NOTLBR JMP CKDIR ;... ; ; OPNOK4: LDA ZCPR ORA A JZ OPNOK5 CALL GTWHL ; WHEEL set for SYSOP use? ORA A ; Is it set? JNZ OPNOK7 ; If yes, skip the # and .COM check ; OPNOK5: LDA NOLBS ORA A JNZ OPNOK6 LXI H,103 MOV A,M ; Check for protect attribute ANI 127 ; Remove CP/M 2.x attributes CPI 35 ; Chk for '#' as last first JZ OPNOT ; If '#', can not send, show why ; OPNOK6: LDA NOCOMS ORA A JNZ OPNOK7 LXI H,103 MOV A,M ; Check for protect attribute ANI 127 ; Remove CP/M 2.x attributes CPI 'M' ; If not, check for '.COM' JNZ OPNOK7 ; If not, ok to send DCX H MOV A,M ; Check next character ANI 127 ; Strip attributes CPI 'O' ; 'O'? JNZ OPNOK7 ; If not, ok to send DCX H MOV A,M ; Now check 1st character ANI 127 ; Strip attributes CPI 'C' ; 'C' as in '.COM'? JNZ OPNOK7 ; If not, continue CALL ERXIT ; Exit with message DB 13,10,'++ Can''t send a .COM file ++','$' ;... ; ; OPNOK7: CALL ILPRT ; Print the message DB 13,10,'File open: ',0 LHLD RCNT ; Get record count CALL DECOUT ; Print decimal number of records PUSH H CALL ILPRT DB ' records (',0 POP H ; Get # of 128 byte records MOV A,H ORA L JZ ZEROLN ; Can't send 0-length files LXI D,8 ; Divide by 8 CALL DVHLDE ; To get # of 1024 byte blocks MOV A,H ORA L ; Check if remainder MOV H,B ; Get quotient MOV L,C JZ $+4 ; If 0 remainder, exact kilobytes INX H ; Else, increment to next k CALL DECOUT ; Show # of kilobytes LDA SNDFLG ; Receiving batch mode now? ORA A RNZ ; If yes, all done ; ; ; Show transfer time, first for 1k blocks, then for 128 (skip the 1k ; times for slower than 1200 bps.)for 1200 bps ; CALL ILPRT DB 'k)',13,10,'Send time: ',0 CALL GTSPD1 ; Get current speed JC XMDSPD ; Skip KMD speed if less than 1200 bps ; KMDSPD: CALL KTIM ; Get file transfer time in BC (minutes) CALL STORTIM ; Store for comparing time remaining CALL OPNOK8 CALL ILPRT DB ' - 1k size',13,10,'Send time: ',0 ; XMDSPD: LXI H,XECTBL ; Use 128 size values SHLD RECTBL+1 CALL XTIM ; Get file transfer time in BC (minutes) LDA KFLG ; If 'SK' set, 1k time already stored ORA A JNZ $+6 CALL STORTIM CALL OPNOK8 LXI H,KECTBL ; Restore to original 1k values SHLD RECTBL+1 CALL ILPRT DB ' - 128 size',13,10,0 LDA BCHFLG ORA A CNZ CUMSTS ; Show how many files remain after this LDA FSTFLG ORA A RNZ ; CALL FILNAM1 CALL ILPRT DB 13,10,'File open, ready to send' DB 0 LDA ARCTYP ORA A JZ XMDSP4 CALL ILPRT DB 13,10,'Rename file ',0 MVI D,8 LXI H,MEMFCB ; XMDSP1: MOV A,M CPI 32 JZ XMDSP2 CALL TYPE DCR D INX H JNZ XMDSP1 ; XMDSP2: LDA 103 STA XMDSP3 CALL ILPRT DB 46,65,82 ; XMDSP3: DW 0 ; XMDSP4: LDA XCANCL ; 3 or more CTL-X's to cancel? ORA A JZ XMDSP5 ; If not, exit CALL ILPRT DB 13,10,'Aborts with several CTL-X',0 ; XMDSP5: CALL ILPRT DB 13,10,0 RET ;..... ; ; OPNOK8: PUSH H ; Save seconds in 'L' LDA ZCPR ORA A JZ OPNOK9 CALL GTWHL ; WHEEL set for SYSOP use? JNZ SKPTIM ; If its not then skip the limit ; OPNOK9: LDA TLIMIT ; See if special user ORA A JZ SKPTIM ; Yes, skip this MOV D,C ; Store minutes in 'D' for now INR D ; Next full minute, will drop seconds LDA TIMEON ; TIMEON flag set? ORA A MOV A,D ; Get minutes of program JZ OPNOK10 ; If not do not increment time LXI H,TON ADD M ; Add time on to xfer time, TON will ; OPNOK10:STA MINUTE ; Store value for later comparison ORA A MOV A,B ; Get high byte of minute if >255 JNZ OPNOK11 ; Flag set from 'INR A' ? INR A ; If not zero, do not increment ; OPNOK11: STA MINUTE+1 ; SKPTIM: MOV L,C MOV H,B CALL DECOUT ; Print decimal number of minutes CALL ILPRT DB ':',0 POP H ; Get seconds CALL ZERO ; See if 10 or more seconds CALL DECOUT ; Print the seconds portion CALL ILPRT DB ' at ',0 LXI H,SPTBL ; Start of baud rate speeds MVI D,0 ; Zero the 'D' register CALL GTSPD ; Get current speed ADD A ; Index into the baud rate table ADD A MOV E,A ; Now have the index factor in 'DE' DAD D ; Add to 'HL' ; OPNOK13:MOV A,M ; Get the character at HL CPI '$' ; Ready to quit? JZ OPNOK14 ; If yes, exit CALL CTYPE ; Display on CRT, modem if permitted INX H ; Next baud rate character JMP OPNOK13 ; Go handle it ; OPNOK14:CALL GTSPD1 ; Get current speed JC OPNOK15 ; If less than 1200, exit CALL ILPRT ; Adds a extra zero for 1200, 2400, 4800 DB '0',0 ; and 9600 bps speeds ; OPNOK15:CALL ILPRT DB ' bps',0 LDA ZCPR ORA A JZ OPNOK16 CALL GTWHL ; WHEEL set for SYSOP use? JNZ SKPEM ; If not then no time limits ; OPNOK16:LDA TLIMIT ; Check for special user or user with ORA A ; unlimited time JZ SKPEM LDA MINUTE+1 ; Get minute count high byte ORA A ; Check if zero JNZ OVRTIM ; If not, is over 255 minutes LDA MINUTE ; Get minute count MOV B,A ; Into B LDA TLIMIT ; Mxtime allowed INR A ; Plus 1 SBB B ; Subtract file time from MXTIME JNC SKPEM ; If less, it's ok to continue ; OVRTIM: CALL ILPRT DB 13,10,13,10,'+++ KMD ABORTED - send time exceeds the ',0 LXI H,OVRMSG LDA TLOS ; Show minutes remaining CALL DEC8 CALL ERXIT1 ; OVRMSG: DB 0,0,0 DB ' minutes remaining',13,10,'$' ; SKPEM: RET ;..... ; ; KTABLE: DW 5,14,21,27,32,54,101,190,330,525,0 KECTBL: DB 192,69,46,36,30,18,9,5,3,2,0 XTABLE: DW 5,13,19,25,30,48,86,141,210,280,0 XECTBL: DB 192,74,51,38,32,20,11,8,5,3,0 SPTBL: DB '110$','300$','450$','600$','710$','120$','240$' DB '480$','960$','1920$' ;..... ; ; ; Pass record count in RCNT: returns file's approximate download/upload ; time in minutes in BC, seconds in 'L', also stuffs the # of mins/secs ; values in PGSIZE if LOGCAL is YES. ; KTIM: LXI H,KTABLE JMP FILTIM ; XTIM: LXI H,XTABLE ; Point to baud factor table ; FILTIM: CALL GTSPD ; Get current speed MVI D,0 MOV E,A ; Set up for table access DAD D ; Index to proper factor DAD D MOV E,M INX H MOV D,M LHLD RCNT ; Get number of records ; FILTIM1:CALL DVHLDE ; Divide HL by value in DE (records/min) PUSH H ; Save remainder ; RECTBL: LXI H,KECTBL ; Point to divisors for seconds calc. MVI D,0 CALL GTSPD ; Get speed indicator MOV E,A DAD D ; Index into table MOV A,M ; Get multiplier POP H ; Get remainder CALL MULHLA ; Multiply 'H' by 'A' CALL SHFTHL CALL SHFTHL CALL SHFTHL CALL SHFTHL MVI H,0 ; HL now = seconds (L=secs,H=0) MOV A,L CPI 60 JC RECTB1 SUI 60 MOV L,A INR C ; RECTB1: MOV A,C ; See if any minutes ORA B RNZ ; If yes, exit MOV A,L ; See if any seconds ORA A RNZ ; If yes, exit INR A ; Else show at least one second MOV L,A RET ;... ; ; STORTIM:LDA LOGCAL ORA A JZ STORT1 MOV A,C ; Add minutes of length (to 0 or 1) STA PGSIZE ; Save as LSB of minutes MOV A,B ; Get MSB of minutes STA PGSIZE+1 ; Save as MSB of minutes (>255?) MOV A,L ; Get LSB of seconds (can't be >59) STA PGSIZE+2 ; Save for LOGCALL ; STORT1: RET ; End of FILTIM routine ;..... ; ; ; Divides 'HL' by value in 'DE' - upon exit: BC=quotient, HL=remainder ; DVHLDE: PUSH D ; Save divisor MOV A,E CMA ; Negate divisor MOV E,A MOV A,D CMA MOV D,A INX D ; 'DE' is now two's complemented LXI B,0 ; Init quotient ; DIVL1: DAD D ; Subtract divisor from divident INX B ; Bump quotient JC DIVL1 ; Loop until sign changes DCX B ; Adjust quotient POP D ; Retrieve divisor DAD D ; Readjust remainder RET ;..... ; ; ; Multiply the value in 'HL' by the value in 'A', return with answer in ; 'HL'. ; MULHLA: XCHG ; Multiplicand to 'DE' LXI H,0 ; Init product INR A ; MULLP: DCR A RZ DAD D JMP MULLP ;..... ; ; ; Shift the 'HL' register pair one bit to the right ; SHFTHL: MOV A,L RAR MOV L,A ORA A ; Clear the carry bit MOV A,H RAR MOV H,A RNC MVI A,128 ORA L MOV L,A RET ;..... ; ; ZERO: MOV A,L ; Get the number of seconds CPI 10 ; 10 seconds or more? RNC ; If yes, disregard CALL ILPRT DB '0',0 RET ;..... ; ; ; Check to see if there is a .LBR file directory with that name and ; complain if not. ; CKDIR: MVI B,11 ; Maximum length of file name MVI A,32 ; First entry must be all blanks INX H ; CKDLP: CMP M JNZ NOTLBR DCR B INX H JNZ CKDLP ; ; ; The first entry in the .LBR directory is indeed blank. Now see if the ; directory size is more than 0. ; MOV D,M ; Get directory starting location INX H ; Which must be 0000H... MOV A,M ORA D JNZ NOTLBR ; Directory does not start in record 0 INX H MOV A,M ; Get size of directory INX H ORA M JZ NOTLBR ; Directory must be >0 records LXI H,0080H ; Point to directory ; ; ; The next routine checks the .LBR directory for the specified member. ; Name one sector at a time. ; CMLP: MOV A,M ; Get member active flag ORA A ; 00=active, anything else can be... MVI B,11 ; Regarded as invalid (erased or blank) INX H ; Point to member name JNZ CMLP1 ; No match if inactive entry ; CMLP0: LDAX D ; Now compare the file name specified... CMP M ; Against the member file name JNZ CMLP1 ; Exit loop if no match found INX H INX D DCR B JNZ CMLP0 ; Check all 11 characters MOV E,M ; Got the file - get file address INX H MOV D,M XCHG SHLD INDEX ; Save file address in .LBR XCHG INX H MOV E,M ; Get the file size INX H MOV D,M XCHG SHLD RCNT ; Save size a # of records LHLD INDEX ; Get file address SHLD 125 ; Place it into random field XRA A STA 127 ; Must zero the 3rd byte STA 124 ; Also zero FCB record # MVI C,33 ; Read random LXI D,92 ; Point to FCB of .LBR file CALL 5 JMP OPNOK7 ; No need to error check ;... ; ; ; Come here if no file name match and another sector is needed ; CMLP1: INX H ; Skip past the end of the file entry DCR B JNZ CMLP1 LXI B,20 ; Point to next file entry DAD B LXI D,MEMFCB ; Point to member name again MOV A,H ; See if we checked all 4 entries ORA A JZ CMLP ; No, check next LHLD DIRSIZ ; Get directory size MOV A,H ORA L JNZ CMLP3 ; Continue if still more to check ; CMLP2: CALL ERXIT DB 13,10 DB '++ Requested member file not found ++','$' ;... ; ; CMLP3: DCX H ; Decrement dirctory size SHLD DIRSIZ MVI C,20 ; Read next sector of directory to TBUF LXI D,92 CALL 5 ORA A ; Read ok? JNZ NOTLBR ; If not, error or end of file LXI H,0080H ; Set our pointers for compare LXI D,MEMFCB JMP CMLP ; Check next sector ;... ; ; ; Lookup requested member file in .ARC archive file ; CMLP4: LXI H,0080H ; Init ptr to first record start MVI B,3 ; Allow up to 3 extra bytes at start MVI A,26 ; Search for initial header mark... ; ; ; Note that 1-3 extra bytes are tolerated before first member file ; header in .ARC file (this for "self-unpacking" archives) ; CMLP5: CMP M ; Is this a header marker? JZ CMLP7 ; Exit loop if found one INR L ; Else, bump record pointer DCR B JNZ CMLP5 ; Loop for number of allowed extra bytes ; ; ; Loop back here for next member in archive ; CMLP6: MOV A,M ; Get next char in file CPI 26 ; Is it archive header marker? JNZ NOTLBR ; No, go report this is not an archive ; CMLP7: LXI D,DBUF ; Init pointer to file transfer buffer STAX D ; Store header marker first INX D ; Bump store pointer INR L ; Point to next byte in current record CZ ARCRD ; If end of record, read the next MOV A,M ; Get next byte (compression version) ORA A ; But is it zero (archive end-of-file)? JZ CMLP2 ; Yes, go report member not in archive STA ARCTYP MVI B,28 ; Setup count for 28 more header bytes MOV C,A ; Save compression version CPI 1 ; But is it version 1? JNZ CMLP8 ; No, skip to store header INR A ; Yes, change it to version 2 MVI B,24 ; But header size is 4 bytes less ; CMLP8: STAX D ; Loop to store header in file buffer... INX D DCR B JZ CMLP9 INR L CZ ARCRD MOV A,M JMP CMLP8 ; CMLP9: SHLD ARCPTR ; Save input record ptr LXI H,DBUF+15 ; Point to compressed file size DCR C ; Was it version 1? MVI C,4 ; (Set count for 4 more bytes) CZ MOVER ; If yes, move to uncompressed size LXI D,DBUF+2 ; Point to file name in header LXI H,MEMFCB ; Point to requested member name MVI B,11 ; Setup count for file name and type ; CMLQ1: LDAX D ; Get next name char ANI 127 ; Ensure no flags, is it end of name? JZ CMLQ2 ; Yes, go use a blank INX D ; Bump name ptr CALL UCASE ; Ensure it's upper case CPI 46 ; But is it type separator? JNZ CMLQ3 ; No, skip MOV A,B ; Get count of chars left CPI 4 ; Reached type yet? JC CMLQ1 ; Yes, bypass the period DCX D ; Else, backup to re-read the period CMLQ2: MVI A,32 ; Use a blank to fill out name or type ; CMLQ3: CMP M ; Name char matches requested name? JNZ FRD ; No, exit INX H ; Point to next requested name character DCR B JNZ CMLQ1 ; Loop until all chars matched ; ; ; Matched requested archive member file ; LXI H,DBUF+18 ; Point to high byte of (4-byte) size MOV A,M ; Must be zero ORA A ; Is it? JNZ NOTLBR ; No, assume not an archive file DCX H ; Middle two bytes of size -> DE MOV D,M DCX H MOV E,M DCX H ORA M ; Fetch (and test) low byte of size XCHG ; Whole page count -> HL DAD H ; Compute record count JC NOTLBR ; But abort if too big for CP/M JP CMLQ4 ; Skip if byte count < 128 INX H ; Else, add one more record ANI 7FH ; And reduce byte count ; CMLQ4: LXI D,1 ; Need at least one more record ADI 30 ; (Overhead is 31 extra bytes) JP CMLQ5 ; Skip unless need two extra records ANI 127 ; Else, get offset of last byte INR E ; Set one more record needed ; CMLQ5: DAD D ; Get total records to send JC NOTLBR ; But abort if too many SHLD RCNT ; Save record count for file send SHLD ARCCNT ; Save it for RDARC STA ARCLST ; Save last record byte count (-1) JMP OPNOK7 ; Skip to common file send stuff ; ; ; No match, so skip to next member in archive ; FRD: LHLD DBUF+16 ; Get no. whole pages to skip DAD H ; Compute no. records to skip LDA DBUF+15 ; Get no. extra bytes to skip ORA A ; More than 128? JP FRD1 ; INX H ; Yes, add one more record ANI 127 ; Reduce byte count ; FRD1: XCHG ; Record offset -> DE LHLD ARCPTR ; Get pointer to last byte of header INR L ; Point to first byte of member file ADD L ; Add byte offsets JP FRD2 ; But skip if overflows current record ; MOV L,A ; Now have pointer to start of next header MOV A,D ; Check record offset ORA E ; JZ CMLP6 ; If zero, loop (still in same record) JMP FRD3 ; Else, go read a new record ; FRD2: ORI 128 ; Get proper byte offset in DMA buffer MOV L,A ; Now have pointer to start of next header INX D ; Bump record offset due to overflow ; FRD3: SHLD ARCPTR ; Save buffer pointer for next header LHLD ARCREC ; Get number of current record in buffer DAD D ; Add record offset SHLD ARCREC ; Save new record number SHLD 125 ; Place it into random field in FCB XRA A STA 127 ; Must zero the 3rd byte MVI C,33 ; Read random record LXI D,92 CALL 5 ORA A ; Any error? JNZ CMLP2 ; If yes, go report member not found LXI H,124 ; Point to current record in extent INR M ; Bump for later sequential read LHLD ARCPTR ; Restore buffer pointer JMP CMLP6 ; Loop to test next member file ;... ; ; NONAME: CALL ERXIT DB 13,10,'++ No file with that name ++','$' ;..... ; ; NOTLBR: CALL ERXIT DB 13,10,'++ No .ARK or .ARC or .LBR file ' DB 'with that name ++','$' ;..... ; ; OPNOT: CALL ERXIT ; Exit with message DB 13,10,'++ File is not for distribution, sorry. ++','$' ; OPNOT1: CALL ERXIT ; Exit with message DB 13,10,'++ Only individual library member files may be ' DB 'transferred ++','$' ;..... ; ; ZEROLN: CALL ERXIT DB 13,10,'++ Can''t send a 0-length file ++','$' ;..... ; ; end of open file, set time routine ;----------------------------------------------------------------------- ; ; ; Closes the received file ; CLOSFIL:MVI C,16 ; Get function LXI D,92 ; Point to file CALL 5 ; Close it INR A ; Close ok? RNZ ; Yes, return CALL ERXIT ; No, abort DB '++ Can''t close file ++','$' ;..... ; ; ; Plays error message if program has been modified (CRC check is wrong) ; CRCERR: DB 13,10,'++ Unmodified program required ++',13,10,'$' ; ; ; Multiplies the record count by 128, saves the new 24-bit value in ; the 3-byte CHRCNT area. ; CHARLN: XCHG ; Put the record count in DE pair LXI B,128 ; Set B=0, C=128 LXI H,0 ; Set H=0, L=0 ; CHAR1: DAD D ; Add DE to HL JNC CHAR2 ; Exit if carry not set INR B ; Else increment the overflow register ; CHAR2: DCR C ; One less to go JNZ CHAR1 ; Go do another if not finished ; SHLD CHRCNT ; Store the value MOV A,B STA CHRCNT+2 ; Store the overflow register ; ; ; Now have the character count in binary in up to three bytes, convert ; to decimal and put in the batch header block. ; LXI H,CHRCNT CALL DECOVT ; Put character count into header & CRT LHLD HDRADR MVI M,32 INX H RET ;..... ; ; ; 24-bit decimal output routine - handles values up to (but not includ- ; ing) 640k, or 655,360 characters. ; DECOVT: PUSH B ; Save the registers PUSH D LXI B,65526 LXI D,65535 ; DECOV1: CALL ADD3 INX D JC DECOV1 ; LXI B,10 CALL ADD3 MOV A,D ORA E PUSH H MOV A,M MOV M,E MOV E,A INX H MOV A,M MOV M,D MOV D,A INX H MVI M,0 POP H CNZ DECOVT ; Recursive call.. ; MOV A,E ADI '0' ; ; ; Store the decimal number in the batch header block at HDRADR address ; PUSH H LHLD HDRADR MOV M,A ; Store the character at that address INX H SHLD HDRADR ; New address for next character POP H POP D ; Restore the registers POP B RET ;..... ; ; ; Part of 24-bit decimal output routine. Uses BC, HL registers. Saves ; and restores the HL entry address. ; ADD3: PUSH H ORA A ; Clear carry MOV A,M ; Do 24-bit add ADC C MOV M,A INX H MOV A,M ADC B MOV M,A INX H MOV A,M ADC B MOV M,A POP H RET ;..... ; ; ; Decimal output routine - call with decimal value in 'HL' ; DECOUT: PUSH B PUSH D PUSH H LXI B,65526 LXI D,65535 ; 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 RET ;..... ; ; ; Prints a hex value in 'A' on the CRT ; HEXO: PUSH PSW RAR RAR RAR RAR CALL NIBBL POP PSW ; NIBBL: ANI 15 CPI 10 JC ISNUM ADI 7 ; ISNUM: ADI '0' ; Add in ASCII bias JMP CTYPE ;..... ; ; ; Move (HL) to (DE), length in (B) ; MOVE: MOV A,M ; Get a byte STAX D ; Put at new home INX D ; Bump pointers INX H DCR B ; Decrement byte count JNZ MOVE ; If more, do it RET ; If not, return ;..... ; ; ;----------------------------------------------------------------------- ; read a record, refill buffer if empty ; ; Update record read ; RDRECD: LDA RECNBF ; See how many records in the buffer ORA A JZ RDBLOCK ; If none, go get some LDA KFLG ; Using 1k blocks? ORA A JZ RDREC1 ; If not, exit ; ; ; Using 1k blocks, switch to 128 if less than 8 records left ; LDA RECNBF ; See how many records in buffer CPI 8 JNC RDREC2 ; If 8 or more stay in 1k blocks XRA A ; Else there are 1-7 records left STA KFLG ; Reset the 1k flag for 128 ; RDREC1: LDA RECNBF ; Get number of records in buffer DCR A ; Decrement it for 128 character blocks STA RECNBF ; Store the new value RET ; From 'READRED' ;... ; ; ; Using 1k blocks, get set to send another one ; RDREC2: SUI 8 ; Subtract 1k worth STA RECNBF RET ;..... ; ; ; Buffer is empty - read in another block of 16k ; RDBLOCK:LDA EOFLG ; Get 'EOF' flag CPI 1 ; Is it set? STC ; To show 'EOF' RZ ; Got 'EOF' ; CALL RDBLK1 JMP RDRECD ; Pass record to caller ;..... ; ; ; Read up to 16k from the disk file into the buffer, ready to send ; RDBLK1: MVI C,0 ; Records in block LXI D,DBUF ; To disk buffer ; RDRECLP:PUSH B PUSH D LDA OPTSAV ; Get command line option CPI 65 ; Using an .ARC, .ARK or .LBR file? JNZ RDBLK2 ; Skip if not ; RDBLKX: LXI H,101 ; Point to the filetype MOV A,M ANI 7FH ; Strip off any R/O attribute CPI 65 ; Archive library? JZ RDARC ; No, go read record from archive ; RDBLK2: CALL SETDMA ; Set DMA address MVI C,20 LXI D,92 CALL 5 ; RDBLK3: POP D POP B ORA A ; Read ok? JNZ REOF ; If not, error or end of file LXI H,128 ; Add length of one record DAD D ; To next buffer XCHG ; Buffer to 'DE' INR C ; More records? MOV A,C ; Get count LDA BUFSIZ ADD A ADD A ADD A CMP C JNZ RDRECLP ; Read more ; ; ; Buffer is full or got EOF ; RDBFULL:STA RECNBF ; Store record count LXI H,DBUF ; Get the beginning buffer address SHLD RECPTR ; Save for next record JMP RSDMA ; Reset DMA to default from CALL RDBLK1 ;..... ; ; REOF: DCR A ; 'EOF'? JNZ RDERR ; Got 'EOF' INR A STA EOFLG ; Set EOF flag MOV A,C JMP RDBFULL ;..... ; ; ; Read error ; RDERR: CALL ERXIT DB '++ File read error ++','$' ;..... ; ; end of read record routine ;----------------------------------------------------------------------- ; read a member record from .ARK archive file ; ; Archive file records are ALWAYS read into the default DMA buffer at ; TBUF, and then moved into the main file transfer buffer at DBUF. ; (This because the records we send are not aligned on exact 128-byte ; boundaries in the .ARC file, and we must add additional header and ; trailer info in the first and last records we send.) The goal of this ; code is to simulate the direct file buffer reads which are used for ; sending other types of files (including .LBR members). ; RDARC: LXI B,32768 ; Set byte cnts (move B=128, clear C=0) LHLD ARCCNT ; Get count of records left to send DCX H ; Reduce by one SHLD ARCCNT ; Save new count MOV A,H ; Is this the last record? ORA L JNZ RDARC1 ; Skip if not last record ; LDA ARCLST ; Get no. bytes - 1 in last record MOV C,B ; Assume must clear all bytes MOV B,A ; Save no. bytes to move ORA A ; But is it zero? JZ RDARC3 ; Yes, skip XRA C ; Get minus no. bytes to clear at end MOV C,A ; Save for later ; RDARC1: LHLD ARCPTR ; Setup input record pointer LDA ARCFST ; Is this the first record? ORA A JNZ RDARC2 ; Skip if not the first ; LXI D,DBUF+29 ; Already have header in DBUF, so MOV A,B ; update buffer pointer, SUI 29 ; and reduce count by 29 bytes MOV B,A ; (will not be 0, even if last record) STA ARCFST ; Set not-first time now ; RDARC2: INR L ; Point to next byte in input record CZ ARCRD ; But refill input buff if reached end MOV A,M ; Fetch input byte STAX D ; Move to output buffer INX D ; Bump output pointer DCR B ; Reduce count JNZ RDARC2 ; Loop until moved all bytes ; SHLD ARCPTR ; Save input record pointer XRA A ; Clear return code CMP C ; Any bytes to clear at end? JZ RDARC4 ; No, skip ; RDARC3: STAX D ; Store zero (.ARC EOF code) in buffer INX D ; Bump output buffer pointer INR C JNZ RDARC3 ; Loop to zero rest of final record ; RDARC4: LDA ARCEOF ; Get EOF flag JMP RDBLK3 ; Go back to RDBLK1 loop ;..... ; ; ; Read raw .ARC file record ; (No need to set DMA address, it's already set to default) ; This routine is used for all .ARC file input, except for the initial ; record (which is read by OPNOK before it goes to CMLP4). ; ARCRD: PUSH B ; Save registers PUSH D LHLD ARCREC ; Get current input record number INX H ; Bump it SHLD ARCREC ; Save number of next record to read MVI C,20 ; Read next record LXI D,92 CALL 5 POP D ; Restore registers POP B LXI H,0080H ; Return pointer to record start (0080H) ORA A ; Early EOF? (should not happen) RZ ; No, return ; ARCRD1: MOV M,H ; Else, fill record with zero... INR L JNZ ARCRD1 ; STA ARCEOF ; Set EOF flag MVI L,0080H ; Point back to record start (TBUF) RET ; Return ; ;..... ; ; end of archive read record routines ;----------------------------------------------------------------------- ; ; Writes the record into a buffer. If/when 16k has been written, writes ; the block to disk. ; ; Entry point "WRBLOCK" flushes the buffer at EOF ; WRRECD: LHLD RECPTR ; Get buffer address LXI D,128 ; 128 charactrs/record LDA KFLG ; Using 1k blocks? ORA A JZ $+6 ; If not, skip next line LXI D,1024 ; 1k/record DAD D ; To next buffer SHLD RECPTR ; Save buffer address LDA KFLG ; Using 1k blocks? ORA A JZ WRREC1 ; If not, exit LDA RECNBF ; Get number of records in buffer ADI 8 ; Increment it 8 records for 1k JMP WRREC2 ; WRREC1: LDA RECNBF ; Get number of records in buffer INR A ; increment it for 1 record ; WRREC2: STA RECNBF ; Store the new value MOV C,A LDA BUFSIZ ADD A ADD A ADD A CMP C RNZ ; No, return ; ; ; Writes a block to disk ; WRBLOCK:LDA RECNBF ; Number of records in the 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 CALL SETDMA ; Set DMA to buffer MVI C,21 LXI D,92 ; Then write the block CALL 5 POP B POP D POP H ORA A JNZ WRERR ; Oops, error LXI H,128 ; Length of 1 record DAD D ; 'HL'= next buff XCHG ; To 'DE' for setdma DCR C ; More records? JNZ DKWRLP ; Yes, loop XRA A ; Get a zero STA RECNBF ; Reset number of records LXI H,DBUF ; Reset buffer buffer SHLD RECPTR ; Save buffer address ; RSDMA: LXI D,0080H ; Reset DMA address ; SETDMA: MVI C,26 ; Set DMA address JMP 5 ;..... ; ; WRERR: CALL RSDMA ; Reset DMA to normal MVI C,24 ; Cancel CALL SEND ; Sender CALL SEND CALL SEND CALL RCVSABT ; Kill receive file CALL ERXIT ; Exit with msg: DB '++ Error writing file ++','$' ;..... ; ; ; Receive a character - timeout time is in 'B' in seconds. Entry via ; 'RCVDG' deletes garbage characters on the line. For example, having ; just sent a record calling 'RECVDG' will delete any line-noise-induced ; characters "long" before the ACK/NAK would be received. ; RECV: PUSH D ; Save 'DE' registers LDA MHZ ; Get the clock speed MOV E,A XRA A ; Clear the 'A' register ; MSLOOP: ADD B ; Number of seconds DCR E ; One less MHz. to go JNZ MSLOOP ; If not zero, continue MOV B,A ; Put total value back into 'B' ; MSEC: LDA BYE5 ; Using BYE5? ORA A JNZ MSEC1 ; If yes, exit LXI D,7300 ; 1 sec. delay for "stand-alone" overlay JMP MWTI ; MSEC1: LDA DSKFLG ; "DISKLOG" option in BYE5 set YES? LXI D,2600 ; 1 second DCR loop count ORA A JZ MWTI ; If not, exit LXI D,1725 ; Shorter loop if using "DISKLOG" ; MWTI: CALL KDINST ; Input from modem ready JNZ MCHAR ; Yes, get the character DCR E ; Count down for timeout JNZ MWTI DCR D JNZ MWTI DCR B ; More seconds? JNZ MSEC ; Yes, wait ; ; ; Test for the presence of carrier - if none, go to 'CARCK' and continue ; testing for specified time. If carrier returns, continue. If it does ; not return, exit. ; CALL KDCARCK ; Is carrier still on? CZ CARCK ; If not, test for 15 seconds ; ; ; Modem timed out receiving - but carrier is still on. ; POP D ; Restore 'DE' STC ; Carry shows timeout RET ;... ; ; ; Get character from modem. ; MCHAR: CALL KDINP ; Get data byte from modem POP D ; Restore 'DE' ; ; ; Calculate Checksum and CRC ; PUSH PSW ; Save the character CALL UPDCRC ; Calculate CRC ADD C ; Add to checksum MOV C,A ; Save checksum POP PSW ; Restore the character ORA A ; Carry off: no error RET ; From 'RECV' ;..... ; ; ; Common carrier test for receive and send. If carrier returns within ; one second, normal program execution continues. Otherwise it aborts ; to CP/M via EXIT. ; CARCK: MVI E,1*10 ; Value for 1 second delay ; CARCK1: CALL DELAY ; Kill .1 seconds CALL KDCARCK ; Is carrier still on? RNZ ; Return if carrier on DCR E ; Has 15 seconds expired? JNZ CARCK1 ; If not, continue testing ; ; ; Report to local console ; CALL ILPRTL ; Report loss of carrier locally only DB 13,10,10,'++ Carrier lost ++',13,10,0 LDA OPTSAV ; Get option CPI 82 ; If not receive JNZ EXIT ; Then abort now, else CALL DELFILE ; Delete the file we started JMP EXIT ; From CARCK back to CP/M prompt ;..... ; ; ; Delay - 100 millisecond delay. ; DELAY: PUSH B ; Save 'BC' PUSH D LDA MHZ ; Get the clock speed MOV E,A ; DELAY1: LXI B,4167 ; Value for 100 ms. delay/clock speed ; DELAY2: DCX B ; Update count MOV A,B ; Get MSP byte ORA C ; Count = zero? JNZ DELAY2 ; If not, continue DCR E ; One less 'clock loop' to go JNZ DELAY1 ; Reset the inner loop POP D ; Restore the registers POP B RET ; Return to CARCK1 ;..... ; ; ; Delay to let all incoming stop for one second ; WAIT1: MVI B,1 ; For 1-second CALL RECV ; See if any characters still coming in JNC WAIT1 ; If yes, keep looping RET ; If none for 1-second, all done ;..... ; ; ;----------------------------------------------------------------------- ; ; Asks user to add description of an uploaded file ; ASK: LDA OPTSAV ; Get the option CPI 82 RNZ ; If not receiving a file, exit LDA PRVTFL ; Sending to "private area"? ORA A RNZ ; If yes, do not ask for description ; LDA FILCNT ; Any batch files received? ORA A JZ ASK1 ; If not, exit LXI H,NAMBUF SHLD NBSAVE CALL BCHDCR ; If yes get the filname ; ASK1: CALL SHONM ; Show the file name CALL DILPRT DB ' - this file is for:',13,10,0 MVI C,9 ; Display the file descriptors LXI D,FILDES CALL 5 CALL DILPRT DB 13,10,'Select a category: ',0 ; ASK1A: CALL SENBEL CALL INPUT ; Get a character CPI 48 JC ASK1A CPI 58 JNC ASK1A CALL TYPE STA KIND ; ASK2: CALL DILPRT DB 13,10,13,10 DB 'Please describe this file in 7 lines or less. ' DB 'Tell what equipment it is',13,10,'for and what ' DB 'the program does. Hit an extra CR on a blank ' DB 'line to quit.',13,10,13,10,0 ; ; ; Get the file name from FCB, skip any blanks ; LXI H,HLINE ; Store short line with dashes CALL DSTOR1 ; Store and show MVI B,8 ; Get FILENAME LXI D,93 LXI H,OLINE CALL LOPFCB LDAX D CPI 32 ; Any file extent? JZ AFIND1 ; If not, skip the period and extent MVI A,46 MOV M,A ; Separate FILENAME and EXTENT CALL TYPE INX H MVI B,3 ; Get EXTENT name CALL LOPFCB AFIND1: LDA KIND ; Get the answer INR A SUI 30H ; Convert to binary MOV C,A ; Store for now LXI D,FILDES ; ALOOP: LDAX D CPI '$' JZ ASK1 CPI 10 ; New line yet? INX D JNZ ALOOP ; Look for a LF ; DCR C ; One less line to go JNZ ALOOP ; INX D INX D INX D INX D CALL DKIND ; CALL DSTOR ; Put FILENAME line into memory and show CALL DILPRT DB 13,10,'0: ---------1---------2---------3' DB '---------4---------5---------6---------',13,10,0 XRA A STA ANYET ; Reset the flag for no information yet MVI C,48 ; EXPLN: INR C MOV A,C CPI 56 JNC EXPL1 CALL TYPE MVI A,32 CALL OUTCHR CALL OUTCHR CALL OUTCHR CALL DILPRT DB ': ',0 CALL DESC ; Get a line of information CALL DSTOR JMP EXPLN ; EXPL1: MVI A,13 ; All finished, put in an extra CR-LF CALL OUTCHR MVI A,10 CALL OUTCHR XRA A CALL OUTCHR CALL DILPRT DB 13,10,' Repeating to verify:',13,10,13,10,0 LHLD BUFADR ; Get starting address of description ; EXPL1A: MOV A,M ; Get the character ORA A ; Is it a '0' to terminate? JZ EXPL1B ; If yes, exit CALL TYPE ; Show character on CRT, send to modem INX H ; Next location JMP EXPL1A ; Go do next charcter ; EXPL1B: LHLD OUTPTR DCX H ; Skip the '0' SHLD OUTPTR ; Store address at end of this entry ; EXPL2: CALL DILPRT DB 13,'Is this ok (Y/N)? ',0 CALL INPUT ANI 5FH ; Change to upper case CPI 'Y' JZ EXPL4 ; Exit if this description was ok CPI 'N' JNZ EXPL2 CALL TYPE ; EXPL3: LHLD BCHPTR ; Else restart at beginning of text SHLD OUTPTR ; Start over at this address JMP ASK2 ; Go do this one again ;... ; ; ; See if any more batch files need descriptions ; EXPL4: CALL TYPE LXI H,92 ; Zero the FCB area for next file CALL INITFCB1 LDA FILCNT ; Any more file names left in buffer? ORA A JZ EXPL5 ; If not, all finished LHLD BCHADR ; Get the current output address SHLD BUFADR ; Store for next verify LHLD OUTPTR ; Get end of current description SHLD BCHPTR ; Store for start of next one JMP ASK1-3 ; Get the next file description ; ; ; Now open the file and put this at the beginning ; EXPL5: LDA 0004H ; Get current drive/user STA DRUSER ; Store ; ; ; Set drive/user to the area listed above ; LDA USER ; Get requested user number MVI C,32 MOV E,A ; Put user number into 'E' register CALL 5 LDA DRIVE ; Get requested drive SUI 65 MVI C,14 MOV E,A CALL 5 ; ; ; Open source file ; CALL DILPRT DB 13,10,0 MVI C,15 LXI D,FILE ; Open FOR text file CALL 5 INR A ; Check for no open JNZ OFILE ; File exists, exit MVI C,22 ; None exists, make a new file LXI D,FILE CALL 5 INR A JZ NOROOM ; Exit if cannot open new file ; OFILE: LXI H,FILE ; Otherwise use same filename LXI D,DEST ; With .$$$ extent for now MVI B,9 CALL MOVE ; ; ; Open the destination file ; XRA A STA DEST+12 STA DEST+32 LXI H,16*1024 ; Size of output buffer SHLD OUTSIZ ; Set for comparison MVI C,19 ; Delete any existing file that name LXI D,DEST CALL 5 MVI C,22 ; Now make a new file that name LXI D,DEST CALL 5 ; INR A JZ NOROOM ; Cannot open file, no directory room CALL DILPRT DB 13,10,'wait a moment...',0 ; ; ; Read sector from source file ; READLP: MVI C,26 LXI D,0080H CALL 5 MVI C,20 LXI D,FILE ; Read from FOR text file CALL 5 ORA A ; Read ok? JNZ RERROR LXI H,0080H ; Read buffer address ; ; ; Write sector to output file (with buffering) ; WRDLOP: MOV A,M ; Get byte from read buffer ANI 127 ; Strip parity bit CPI 127 ; Del (rubout)? JZ NEXT ; Yes, ignore it CPI 26 ; End of file marker? JZ TDONE ; Transfer done, close, exit CALL OUTCHR ; NEXT: INR L ; Done with sector? JZ READLP ; If yes get another sector JMP WRDLOP ; No, get another byte ;..... ; ; ; Handle a backspace character while entering a character string ; BCKSP: CALL TYPE MOV A,B ; Get position on line ORA A JNZ BCKSP1 ; Exit if at initial column CALL SENBEL ; Send a bell to the modem MVI A,32 ; Delete the character JMP BCKSP3 ; BCKSP1: DCR B ; Show one less column used DCX H ; Decrease buffer location MVI A,32 MOV M,A ; Clear memory at this point CALL TYPE ; Backspace the "CRT" ; BCKSP2: MVI A,8 ; Reset the "CRT" again ; BCKSP3: JMP TYPE ; Write to the "CRT", done ;..... ; ; ; Asks for line of information ; DESC: XRA A STA FIRST MOV B,A LXI H,OLINE ; DESC1: CALL INPUT ; Get keyboard character CPI 13 JZ DESC5 CPI 9 JZ DESC7 CPI 8 ; Backspace character? JZ DESC2 CPI 127 ; Delete character? JNZ DESC3 ; DESC2: CALL BCKSP JMP DESC1 ; Get the next character ; DESC3: CPI 32 ; Space character? JC DESC1 ; If non-printing character, ignore JNZ DESC4 LDA FIRST ; Any non-space characters yet? ORA A JZ DESC1 ; If not, ignore this space MVI A,32 ; Restore the value ; DESC4: STA ANYET ; Show a character has been sent now STA FIRST MOV M,A CALL TYPE ; Display the character INX H INR B MOV A,B CPI 70 ; Do not exceed line length JC DESC1 CALL SENBEL ; Send a bell to the modem CALL BCKSP2 CALL BCKSP1 ; Do not allow a too-long line JMP DESC1 ; DESC5: LDA ANYET ; Any text typed on first line yet? ORA A JNZ DESC6 ; If yes, exit POP H JMP EXPL3 ; Ask again for a description ; DESC6: MVI M,13 MOV A,M CALL TYPE INX H ; Ready for next character MVI M,10 MOV A,M CALL TYPE ; Display the line feed INX H MOV A,B ; See if at first of line ORA A RNZ ; If not, ask for next line POP H ; Clear "CALL" from stack JMP EXPL1 ; DESC7: MOV A,B ; At end of line now? CPI 68 JNC DESC1 ; If yes, disregard MVI M,32 MOV A,M CALL TYPE INX H INR B MOV A,B ANI 7 JNZ DESC7 JMP DESC1 ; Ask for next character ;..... ; ; ; Print message then exit to CP/M ; DEXIT: MVI C,9 ; Print message POP D ; Get message address CALL 5 JMP RESET ; Reset the drive/user, then finished ;..... ; ; ; Inline print routine - prints string pointed to by stack until a zero ; is found. Returns to caller at the next address after the zero ter- ; minator. ; DILPRT: XTHL ; Save HL, get message address ; DILPLP: MOV A,M ; Get character INX H ; Next character in the string ORA A JZ DILPL1 CALL TYPE ; Output it JMP DILPLP ; DILPL1: XTHL ; Restore HL, ret address RET ; Return past the end of the message ;..... ; ; DKIND: LDAX D ; Get the character from the string CALL TYPE ; Otherwise display the character MOV M,A ; Put in the buffer CPI 10 ; Done yet? RZ ; Exit if a LF, done INX D ; Next position in the string INX H ; Next postion in the buffer JMP DKIND ; Keep going until a LF ;..... ; ; DSTOR: LXI H,OLINE ; DSTOR1: MOV A,M CALL OUTCHR CPI 10 RZ INX H JMP DSTOR1 ;..... ; ; ; Disk is full, save original file, erase others. ; FULL: MVI C,19 LXI D,DEST CALL 5 CALL DEXIT DB 13,10,'++ DISK FULL, ABORTING, SAVING ORIGINAL FILE','$' ;..... ; ; ; Get a character, if none ready wait up to 3 minutes, then exit from ; the program. ; INPUT: PUSH H ; Save current values PUSH D PUSH B MVI A,3 ; Wait up to 3 minutes ADD A ; Double the number, bell each 30 sec. MOV H,A ; Put in 'H' for 1/2 minute loops ; INPUT1: LXI D,300 ; Outer loop count 600 loops per min. ; INPUT2: LDA MHZ ; Get the clock speed MOV L,A ; Put in 'L' for 'clock loops' ; INPUT3: LDA DSKFLG ; Is the BYE5 "DISKLOG" option YES? LXI B,68 ; Gives about 100 ms. ORA A JZ INPUT4 ; If not, handle normally LXI B,45 ; Else compensate for increased delay ; INPUT4: PUSH H PUSH D ; Save the outer delay count PUSH B ; Save the inner delay count MVI C,6 ; Get console character, if any MVI E,255 CALL 5 ANI 127 ; Remove any parity POP B ; Restore the inner delay count POP D ; Restore the outer delay count POP H ; Restore the Number of minutes count ORA A ; Have a character yet? JNZ INPUT5 ; If yes, exit and get it ; DCX B MOV A,C ; See if inner loop is finished ORA B JNZ INPUT4 ; If not loop again ; DCR L ; One less clock loop to go JNZ INPUT3 ; DCX D MOV A,E ORA D JNZ INPUT2 ; If not reset inner loop and go again ; ; ; No character received, ding the bell each 1/2 minute ; PUSH H CALL SENBEL ; Ding the bell on the remote, only POP H DCR H JNZ INPUT1 ; ; ; Out of time, no character so abort ; MVI A,13 CALL OUTCHR MVI A,10 CALL OUTCHR LXI SP,STACK ; Restore the stack LDA KIND ; Permits use with other requests CPI 1 JZ EXIT ; Skip doing any 'FOR' file work CALL EXPL5 ; Finish appending previous information JMP EXIT ; File is closed, return to CP/M ; INPUT5: POP B POP D POP H RET ; Got a character, return with it ;..... ; ; ; Stores the Filename/extent in the buffer temporarily ; LOPFCB: LDAX D ; Get FCB FILENAME/EXT character CPI 33 ; Skip any blanks JC LOPF1 MOV M,A ; Store in OLINE area CALL TYPE ; Display on CRT INX H ; Next OLINE position ; LOPF1: INX D ; Next FCB position DCR B ; One less to go JNZ LOPFCB ; If not done, get next one RET ;..... ; ; ; No room to open a new file ; NOROOM: CALL DEXIT DB 13,10,'NO DIR SPACE: OUTPUT','$' ;..... ; ; ; Output error - cannot close destination file ; OERROR: CALL DEXIT DB 13,10,'CANNOT CLOSE OUTPUT','$' ;..... ; ; ; See if there is room in the buffer for this character ; OUTCHR: PUSH H PUSH PSW ; Store the character for now LHLD OUTSIZ ; Get buffer size XCHG ; Put in 'DE' LHLD OUTPTR ; Now get the buffer pointers MOV A,L ; Check to see if room in buffer SUB E MOV A,H SBB D JC OUT3 ; If room, go store the character LXI H,0 ; Otherwise reset the pointers SHLD OUTPTR ; Store the new pointer address ; OUT1: XCHG ; Put pointer address into 'DE' LHLD OUTSIZ ; Get the buffer size into 'HL' MOV A,E ; See if buffer is max. length yet SUB L ; By subtracting 'HL' from 'DE' MOV A,D SBB H JNC OUT2 ; If less, exit and keep going ; ; ; No more room in buffer, stop and transfer to destination file ; LHLD OUTADR ; Get the buffer address DAD D ; Add pointer value XCHG ; Put into 'DE' CALL SETDMA MVI C,21 LXI D,DEST CALL 5 ORA A JNZ FULL ; Exit with error, if disk is full now LXI D,128 LHLD OUTPTR DAD D SHLD OUTPTR JMP OUT1 ; OUT2: CALL RSDMA LXI H,0 SHLD OUTPTR ; OUT3: XCHG LHLD OUTADR DAD D XCHG POP PSW ; Get the character back STAX D ; Store the character XCHG SHLD BCHADR LHLD OUTPTR ; Get the buffer pointer INX H ; Increment them SHLD OUTPTR ; Store the new pointer address POP H RET ;..... ; ; RERROR: CPI 1 ; File finished? JZ TDONE ; Exit, then MVI C,19 ; Erase destination file, keep original LXI D,DEST CALL 5 CALL DEXIT DB '++ SOURCE FILE READ ERROR ++$' ;..... ; ; ; Reset the Drive/User to original ; RESET: LDA DRUSER ; Get original drive/user area back RAR RAR RAR RAR ANI 15 ; Just look at the user area MVI C,32 MOV E,A CALL 5 LDA DRUSER ; Get the original drive/user back ANI 15 ; Just look at the drive for now MVI C,14 ; Restore original drive MOV E,A CALL 5 CALL DILPRT ; Print CRLF before quitting DB 13,10,0 RET ; To: CALL ASK ;..... ; ; ; Send a bell just to the modem ; SENBEL: CALL KDOUTST ; Is modem ready for another character? JZ SENBEL ; If not, wait MVI A,7 JMP KDOUTP ; Send to the modem only ;..... ; ; ; Shows the Filename/extent ; SHONM: XRA A STA CONONL CALL DILPRT DB 13,10,13,10,0 LXI H,93 ; SHONM1: MVI B,8 ; Maximum size of file name CALL SHONM2 MOV A,M ; Get the next character CPI 32 ; Any file extent? RZ ; If not, finished MVI A,46 CALL CTYPE MVI B,3 ; Maximum size of file extent ; SHONM2: MOV A,M ; Get FCB FILENAME/EXT character CPI 32 ; Skip any blanks CNZ CTYPE INX H ; Next FCB position DCR B ; One less to go JNZ SHONM2 ; If not done, get next one RET ;..... ; ; ; Transfer is done - close destination file ; TDONE: LHLD OUTPTR MOV A,L ANI 127 JNZ TDONE1 SHLD OUTSIZ ; TDONE1: MVI A,26 ; Fill remainder of record with ^Z's PUSH PSW CALL OUTCHR POP PSW JNZ TDONE MVI C,16 ; Close FOR text file LXI D,FILE CALL 5 MVI C,16 ; Close FOR.$$$ text file LXI D,DEST CALL 5 INR A JZ OERROR ; ; ; Rename both files as no destination file name was specified ; LXI H,FILE+1 ; Prepare to rename old file to new LXI D,DEST+17 MVI B,16 CALL MOVE MVI C,19 ; Delete original FOR text file LXI D,FILE CALL 5 MVI C,23 LXI D,DEST ; Rename FOR.$$$ to FOR text file CALL 5 JMP RESET ; Reset the drive/user, finished ;..... ; ; ; Send character in 'A' register to console ; TYPE: PUSH B PUSH D PUSH H PUSH PSW MVI C,2 ; Write to console MOV E,A ; Character to 'E' for CP/M CALL 5 POP PSW POP H POP D POP B RET ;..... ; ; end of file description area ;----------------------------------------------------------------------- ; ; Send a character to the modem ; SEND: PUSH PSW ; Save the character CALL UPDCRC ; Calculate CRC ADD C ; Calculate checksum MOV C,A ; Save cksum ; SEND1: CALL KDOUTST ; Is transmit ready JZ SEND2 ; No, check carrier POP PSW ; Modem is ready JMP KDOUTP ; So send it ;... ; ; ; Xmit status not ready, so test for carrier before looping - if lost, ; go to CARCK and give it up to 15 seconds to return. If it doesn't, ; return abort via EXIT. ; SEND2: PUSH D ; Save 'DE' CALL KDCARCK ; Is carrier still on? CZ CARCK ; If not, continue testing it POP D ; Restore 'DE' JMP SEND1 ; Else, wait for xmit ready ;..... ; ; ; Waits for initial NAK - to ensure no data is sent until the receiving ; program is ready, this routine waits for the first timeout-nak or the ; letter 'C' for CRC from the receiver. If CRC is in effect then Cyclic ; Redundancy Checks are used instead of checksums. 'E' contains the ; number of seconds to wait. If the first character received is CANCEL ; (CTL-X) then the send will be aborted as though it had timed out. ; WAITNAK:CALL FUNCHK ; Check function keys CALL SNDABT ; Check for local abort MVI A,1 ; Be sure the flag gets set STA CONONL ; Show future diplays to local CRT only MOV B,A ; Timeout delay CALL RECV ; Wait up to 1 second for character JC WAITN1 ; No character this time CPI 67 ; Was it a 'CRC' request? JZ WAITK CPI 75 ; Requesting 1k? JZ SETK ; Exit if yes, otherwise set CRC CPI 21 ; A 'NAK' indicating checksum? JZ SETNAK ; Yes go put checksum in effect CPI 24 ; Was it a cancel (CTL-X)? JZ ABORT ; Yes, abort ; WAITN1: DCR E ; Finished yet? JZ ABORT ; Yes, abort JMP WAITNAK ; No, loop ; WAITK: LDA BCHFLG ; In batch mode? ORA A JNZ SETK ; If yes, don't wait for a 'K' MVI B,1 ; Got a 'C', wait up to 1 second for 'K' CALL RECV JC SETCRC ; Didn't get anything so not using 1k ANI 127 CPI 123 JZ WAITK ; Disregard noisy lines CPI 75 ; Requesting 1k? JZ SETK ; Exit if yes, otherwise set CRC ; ; ; Turn on the flag for CRC ; SETCRC: LDA KFLG ; KFLG manually set from 'SK'? ORA A JNZ SETK ; If yes, keep it set ; SETC1: XRA A STA KFLG ; Defaults to 128 character blocks INR A STA CRCFLG ; Insures in CRC mode CALL ILPRTL DB 13,10,'CRC requested',13,10,0 RET ;..... ; ; ; Turn on the flag for 1k blocks and insure in CRC mode ; SETK: CALL GTSPD1 ; Get current speed JC SETC1 ; Don't allow 1k if less than 1200 bps STA KFLG ; Set the flag for 1k blocks STA CRCFLG ; Insures in 'CRC' mode CALL ILPRTL DB 13,10,'1k requested',13,10,0 RET ;..... ; ; ; Turn on checksum flag, insure sending 128 character blocks ; SETNAK: LDA BCHFLG ; In batch mode now? ORA A JNZ SETNAK1 ; If yes, exit XRA A STA CRCFLG ; Make sure in checksum mode STA KFLG ; Defaults to 128 character blocks CALL ILPRTL DB 13,10,'checksum requested',13,10,0 RET ; From WAITNAK ;... ; ; SETNAK1:CALL ILPRTL DB 13,10,'checksum not used for batch mode',13,10,0 JMP WAITNAK ; If yes, ignore checksum request ;..... ; ; ; This routine moves the filename from the default command line buffer ; to the file control block (FCB). ; MOVEFCB:LHLD SAVEHL ; Get position on command line CALL GETB ; Get numeric position LXI D,93 CALL MOVENAM ; Move name to FCB XRA A STA 104 ; Zero extent STA 124 ; Zero record number ; LDA OPTSAV ; This going to be a library file? CPI 65 ; This an .ARC or .ARK file? RNZ ; If not, finished ; ; ; Handles library entries, first checks for proper .ARC or .ARK extent. ; If no extent was included, it adds one itself. ; MOVEFC1:SHLD SAVEHL LXI H,101 ; Point to the filetype MOV A,M CPI 32 JZ NOEXT ; No extent, make one CPI 65 ; Check 1st character in extent JNZ CHKLBR ; If not 'ARK', go see if it's 'LBR' INX H MOV A,M CPI 82 ; Check 2nd character in extent JNZ LBRERR INX H MOV A,M CPI 67 ; Check 3rd character in extent JZ MOVEF1 CPI 75 JNZ LBRERR ;..... ; ; ; Get the name of the desired file in the library ; MOVEF1: LHLD SAVEHL ; Get current position on command line CALL CHKMSP ; See if valid library member file name INR B ; Increment for move name LXI D,MEMFCB ; Store member name in special buffer JMP MOVENAM ; Move from command line to buffer, done ;..... ; ; ; Check for any spaces prior to library member file name, if none (or ; only spaces remaining), no name. ; CHKMSP: DCR B JZ MEMERR MOV A,M CPI 33 RNC INX H JMP CHKMSP ;..... ; ; ; Gets the count of characters remaining on the command line ; GETB: MOV A,L SUI 0080H+2 ; Start location of 1st command MOV B,A ; Store for now LDA 0080H ; Find length of command line SUB B ; Subtract those already used MOV B,A ; Now have number of bytes remaining RET ;..... ; ; ; If not .ARK or .ARC extent, check if .LBR extent ; CHKLBR: CPI 76 ; Check 1st character in extent JNZ LBRERR INX H MOV A,M CPI 66 ; Check 2nd character in extent JNZ LBRERR INX H MOV A,M CPI 82 ; Check 3rd character JZ MOVEF1 ; If all ok, go get the member name ; ; LBRERR: CALL ERXIT DB 13,10,'++ Invalid .ARK or .ARC or .LBR name ++','$' ;..... ; ; MEMERR: CALL ILPRT DB 13,10,13,10 DB '++ No .ARK or .ARC or .LBR member file requested ++' DB 13,10,0 JMP OPTERR ;..... ; ; ; Add .ARK extent to the library file name ; NOEXT: LXI H,101 ; Point to the filetype MVI M,65 INX H MVI M,82 INX H MVI M,75 JMP MOVEF1 ; Now get the library member name ;..... ; ; ; Move a file name from the 'TBUF' command line buffer into FCB ; MOVENAM:MVI C,1 ; MOVEN1: MOV A,M CPI 33 ; Name ends with space or return JC FILLSP ; Fill with spaces if needed CPI 46 JZ CHKFIL ; File name might be less than 8 chars. STAX D ; Store INX D ; Next position to store the character INR C ; One less to go MOV A,C CPI 12+1 JNC NONAME ; 11 chars. maximum filename plus extent ; MOVEN2: INX H ; Next char. in file name DCR B JZ OPTERR ; End of name, see if done yet JMP MOVEN1 ;..... ; ; ; See if any spaces needed between file name and .ext ; CHKFIL: CALL FILLSP ; Fill with spaces JMP MOVEN2 ;..... ; ; FILLSP: MOV A,C CPI 9 RNC ; Up to 1st character in .ext now MVI A,32 ; Be sure there is a blank there now STAX D INR C INX D JMP FILLSP ; Go do another ;..... ; ; CTYPE: PUSH B ; Save all registers PUSH D PUSH H MOV E,A ; Character to 'E' in case BDOS (normal) LDA CONONL ; Want to bypass 'BYE' output to modem? ORA A JNZ CTYPEL ; Yes, go directly to CRT, then MVI C,2 ; BDOS console output, to CRT and modem CALL 5 ; Since 'BYE' intercepts the char. POP H ; Restore all registers POP D POP B RET ;..... ; ; CTYPEL: MOV C,E ; BIOS needs it in 'C' CALL KONOUT ; BIOS console output routine, not BDOS POP H ; Restore all registers saved by 'CTYPE' POP D POP B RET ;..... ; ; ; Inline print of message, terminates with a 0 ; ILPRTB: XRA A JMP ILPRTL+2 ; ILPRTL: MVI A,1 STA CONONL ; 1=local only, 0=both local and remote ; ILPRT: XTHL ; Save HL, get HL=message ; ILPLP: MOV A,M ; Get the character INX H ; To next character ORA A ; End of message? JZ ILPRET ; Yes, return CALL CTYPE ; Type the message JMP ILPLP ; Loop ;..... ; ; ILPRET: XTHL ; Restore HL RET ; Past message ;..... ; ; ; Exit, printing message following CALL ; ERXIT: CALL ILPRT DB 13,10,0 ; ERXIT1: MVI C,11 ; Check keyboard status CALL 5 ORA A ; Have a character? JZ ERXIT3 ; No character, display string MVI C,1 ; Get the character CALL 5 CPI 19 ; CTL-S to pause? JNZ ERXIT4 ; Abort if any other character ; ERXIT2: MVI C,11 ; Check keyboard status CALL 5 ORA A JZ ERXIT2 ; If no character yet, wait MVI C,1 ; Go get the character CALL 5 CPI 19 ; CTL-S to resume the display? JNZ ERXIT4 ; Anything else, abort ; ERXIT3: POP H ; Get address of next character MOV A,M ; Get character INX H ; Increment to next character PUSH H ; Save address CPI '$' ; End of message? JZ ERXIT4 ; If '$' is end of message CALL CTYPE ; Else print character on console JMP ERXIT1 ; And repeat until abort/end ; ERXIT4: LDA HLPFLG ; Did we come here from the help guide? ORA A JNZ EXTHLP ; If yes, go finish the help guide ; ERXIT5: CALL ILPRT DB 13,10,0 ; Extra line feed looks nice at end ; ERXIT6: XRA A ; Reset message flag STA MSGFLG ; Prevents normal termination CALL CATCH ; Clear the input XRA A STA OPTSAV ; Reset option to zero for TELL STA MSGFLG ; Reset the message file upload flag JMP EXIT ; Get out of here ;..... ; ; ; Following shows the help guide and kicks in an extra line if allowing ; message uploads. ; EXTHLP: LDA MSGFIL ; Allowing message uploads? ORA A JZ EXTHLP1 ; If not, skip next section CALL ILPRT DB ' KMD RM HELLO.MSG direct message uploads ' DB 13,10,0 ; EXTHLP1:CALL ILPRT ; Finish the help guide DB 13,10 DB ' KMD fully supports XMODEM protocol and is compat' DB 'ible with modem pgms',13,10,' using YMODEM protocol.' DB 0 LDA MSGFIL ; Allowing message uploads? ORA A JZ ERXIT5 ; If not, finished with help guide LHLD WHEEL ; See if wheel byte is set MOV A,M ; Get WHEEL byte value ORA A JNZ ERXIT6 ; If set, skip extra CRLF JMP ERXIT5 ; If not, add extra CRLF ;..... ; ; ; Restore the old user area and drive from a received file ; RECAREA:CALL RECDRV ; Ok set the drive to its place LDA PRVTFL ; Private area wanted? ORA A LDA PRUSR ; Yes, set to private area JNZ RECARE LDA USR ; Ok now set the user area ; RECARE: MOV E,A ; Stuff it in E MVI C,32 ; Tell BDOS what we want to do JMP 5 ; Now do it ;..... ; ; RECDRV: LDA PRVTFL ORA A LDA PRDRV ; Get private upload drive JNZ RECDR1 LDA DRV ; Or forced upload drive ; RECDR1: SUI 65 ; Adjust it ; RECDRX: MOV E,A ; Stuff it in E MVI C,14 ; Tell BDOS JMP 5 ; Do it ;..... ; ; ;======================================================================= ; ; CRC SUBROUTINES ; ;======================================================================= ; ; CRCCHK: PUSH H ; Check 'CRC' bytes of received message LHLD CRCVAL MOV A,H ORA L POP H RZ ; Return with zero flat set if ok MVI A,255 ; Else clear the flag to show an error RET ;..... ; ; FINCRC: PUSH PSW ; Finish 'CRC' calculation for last xmsn XRA A CALL UPDCRC CALL UPDCRC PUSH H LHLD CRCVAL MOV D,H MOV E,L POP H POP PSW RET ;..... ; ; UPDCRC: PUSH PSW ; Update 'CRC' store with byte in 'A' 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 16 MOV H,A MOV A,L XRI 33 MOV L,A ; SKIPIT: DCR B JNZ UPDLOOP SHLD CRCVAL POP H POP B POP PSW RET ;..... ; ; end of CRC routines ;----------------------------------------------------------------------- ; start of LOGCAL routines ; ; Main log file routine, adds record to log file ; LOGCALL:MVI C,25 ; Get current disk CALL 5 ; (where down/upload occurred) STA DSKSAV MVI C,32 ; Get current user area MVI E,255 ; (where down/upload occurred) CALL 5 STA USRSAV XRA A STA FCBCALLER+12 STA FCBCALLER+32 LDA LASTDRV SUI 65 STA DEFAULT$DISK LDA LASTUSR STA DEFAULT$USER LXI D,FCBCALLER CALL OPENF ; Open LASTCALR file JNZ LOGC1 CALL ILPRT DB '++ NO LASTCALR???' ; ERROR msg, then go send EOT DB ' FILE FOUND ++',0 RET ; LOGC1: MVI C,36 ; Get random record # LXI D,FCBCALLER ; (for first record in file) CALL 5 MVI C,26 LXI D,DBUF ; Set DMA to DBUF CALL 5 MVI C,33 LXI D,FCBCALLER ; Read first (and only) record CALL 5 LXI H,DBUF ; Set pointer to beginning of record LDA CLOCK ORA A JZ LOGC2 LXI D,0 ; Zero DE LDA LCNAME ; Offset-1 to start of caller's name DCR A ; Now correct offset MOV E,A ; To E DAD D ; HL now points to start of name ; LOGC2: SHLD CALLERPTR MVI C,26 LXI D,LOGBUF ; Set DMA address to LOGBUF CALL 5 XRA A STA FCBLOG+12 STA FCBLOG+32 LDA LOGDRV SUI 65 STA DEFAULT$DISK LDA LOGUSR STA DEFAULT$USER LXI D,FCBLOG CALL OPENF ; Open log file JNZ LOGC5 ; If file exists, skip create MVI C,22 ; Create a new file if needed LXI D,FCBLOG CALL 5 INR A JNZ LOGC3 ; No error, continue CALL ILPRT ; File create error DB '++ NO DIR SPACE: LOG ++',0 RET ; Go back and send EOT ;... ; ; LOGC3: MVI C,36 ; Set random record # LXI D,FCBLOG ; (for first record in file) CALL 5 ; LOGC4: LXI H,LOGBUF ; Store CR,LF,EOF for 'NEW' file MVI M,13 INX H MVI M,10 INX H MVI M,26 JMP LOGC6 ;... ; ; LOGC5: MVI C,26 LXI D,LOGBUF ; Set DMA to LOGBUF CALL 5 MVI C,35 ; Get file length LXI D,FCBLOG CALL 5 LHLD FCBLOG+33 ; Back up to last record MOV A,L ORA H JZ LOGC4 ; Unless zero length file DCX H SHLD FCBLOG+33 MVI C,33 ; And read it LXI D,FCBLOG CALL 5 ; LOGC6: CALL RSTLP ; Initialize LOGPTR and LOGCNT ; LOGC7: CALL GETLOG ; Get characters out of last record CPI 26 JNZ LOGC7 ; Until EOF LDA LOGCNT ; Then backup one character DCR A STA LOGCNT LHLD LOGPTR DCX H SHLD LOGPTR LDA LOGOPT ; Get option back and put in file CALL PUTLOG CALL GTSPD ; Get current speed ADI 48 CALL PUTLOG CALL PUTSP ; Blank LDA PGSIZE ; Now the program size in minutes.. CALL PNDEC ; Of transfer time (mins) MVI A,':' CALL PUTLOG ; ':' LDA PGSIZE+2 CALL PNDEC ; And seconds CALL PUTSP ; Blank ; ; ; Log the drive and user area as a prompt ; LDA 92 ORA A JNZ WDRV LDA DSKSAV INR A ; WDRV: ADI 64 CALL PUTLOG LDA USRSAV CALL PNDEC MVI A,'>' ; Make it look like a prompt CALL PUTLOG LDA OPTSAV CPI 65 JNZ WDRV1 LXI H,MEMFCB ; Name of file in library MVI B,11 CALL PUTSTR CALL PUTSP ; ' ' ; WDRV1: LXI H,93 ; Now the name of the file MVI B,11 CALL PUTSTR LDA OPTSAV CPI 65 JNZ WDRV2 MVI C,1 JMP SPLOOP ; WDRV2: MVI C,13 ; SPLOOP: PUSH B CALL PUTSP ; Put ' ' POP B DCR C JNZ SPLOOP LHLD RECDNO ; Get record count LXI D,8 ; Divide record count by 8 CALL DVHLDE ; To get # of 1024 byte blocks MOV A,H ORA L ; Check if remainder MOV H,B ; Get quotient MOV L,C JZ EXKB2 ; If 0 remainder, exact kb INX H ; Else increment to next kb ; EXKB2: CALL PNDEC3 ; Print to log file (right just xxxk) LXI H,LOGK ; 'k ' MVI B,2 CALL PUTSTR LDA CLOCK ORA A JZ CLOOP XRA A STA COMMA ; Reset field counter CALL GETDATE ; IF RTC, get current date PUSH B ; (save DD/YY) CALL PNDEC ; Print MM MVI A,47 ; '/' CALL PUTLOG POP PSW ; Get DD/YY PUSH PSW ; Save YY CALL PNDEC ; Print DD MVI A,47 ; '/' CALL PUTLOG POP B ; Get YY MOV A,C CALL PNDEC ; Print YY CALL PUTSP ; ' ' CALL GETTIME ; IF RTC, get current time STA MNSAV ; Save min MOV A,B ; Get current hour CALL PNDEC ; Print hr to file MVI A,58 ; With ':' CALL PUTLOG ; Between HH:MM LDA MNSAV ; Get min CALL PNDEC ; And print min CALL PUTSP ; Print a space ; CLOOP: CALL GETCALLER ; And the caller CPI 26 JZ QUIT CPI 13 ; Do not print 2nd line of 'LASTCALR' JNZ CLOP1 ; CEND: CALL PUTLOG MVI A,10 CALL PUTLOG ; And add a LF JMP QUIT ;... ; ; CLOP1: MOV B,A LDA CLOCK ORA A MOV A,B ; Get the character back JZ CLOP1A CPI 32 ; Space? JNZ CLOP1A ; No, check for comma MVI A,44 ; Convert space to comma to check field ; CLOP1A: CPI 44 ; Comma? JNZ CLOP2 LDA CLOCK ORA A JZ CLOP1B LDA COMMA ; Increment number of commas INR A STA COMMA MOV B,A LDA NAMELEN ; Get length of name CMP B ; Is this comma at the end of name? JNZ CLOP1B ; No, send a ' ' and keep going MVI A,13 JMP CEND ; Yes, stop taking data from LASTCALR ; CLOP1B: MVI A,32 ; Instead send a space ; CLOP2: CALL PUTLOG ; Send character to the output file JMP CLOOP ; Loop around for next character in name ; QUIT: MVI A,26 ; Put in EOF CALL PUTLOG LDA LOGCNT ; Check count of chars in buffer CPI 1 JNZ QUIT ; Fill last buffer & write it MVI C,16 LXI D,FCBCALLER ; Close lastcaller file CALL 5 INR A JZ QUIT1 LHLD FCBLOG+33 ; Move pointer back to show DCX H ; Actual file size SHLD FCBLOG+33 MVI C,16 LXI D,FCBLOG ; Close log file CALL 5 INR A RNZ ; If OK, return ; QUIT1: CALL ILPRT ; If error, oops DB '++ CANNOT CLOSE LOG ++',0 RET ; Go back and send EOT ;..... ; ; ;-------------------------------------------------------------- ; LOGXAL support routines ; ; Gets a single byte from DBUF ; GETCALLER: LHLD CALLERPTR MOV A,M INX H SHLD CALLERPTR RET ;..... ; ; ; Gets a single byte from log file ; GETLOG: LDA LOGCNT INR A STA LOGCNT CPI 129 JZ EOLF LHLD LOGPTR MOV A,M INX H SHLD LOGPTR RET ;..... ; ; EOLF: LHLD FCBLOG+33 INX H SHLD FCBLOG+33 LXI H,LOGBUF+1 SHLD LOGPTR MVI A,1 STA LOGCNT MVI A,26 RET ;..... ; ; ; Open file with FCB pointed to by DE (disk/user passed in DEFAULT$DISK ; and DEFAULT$USER) ; OPENF: PUSH D ; Save FCB address LDA DEFAULT$DISK ; Get disk for file CALL RECDRX ; Log into it LDA DEFAULT$USER ; Get default user CALL RECARE ; Log into it POP D ; Get FCB address ; LDA CPM3 ORA A JZ OPENF1 PUSH D ; Save FCB address LXI D,0080H MVI C,26 CALL 5 ; Set DMA to 0080H MVI C,17 ; Search for first match POP D ; Get back pointer to FCB PUSH D ; Save FCB pointer again CALL 5 INR A ; Did a file match? POP D RZ ; No, return PUSH D DCR A ; A=directory code (0-3) ADD A ; *2 ADD A ; *4 ADD A ; *8 ADD A ; *16 ADD A ; *32 MOV E,A MVI D,0 LXI H,0080H ; Add (32*dir code) to default DMA DAD D ; to find first match filename POP D ; DE=FCB PUSH D ; Save DE again INX H ; Move HL past user # byte in buffer INX D ; Move DE past drive # in FCB MVI B,11 CALL MOVE ; Move name found to FCB POP D ; And continue with the open ; OPENF1: MVI C,15 ; Open file CALL 5 CPI 255 ; Not present? RET ; Return to caller ;..... ; ; ; Write character to log file ; PUTLOG: LHLD LOGPTR ; Get pointer MOV M,A ; Put data INX H ; Increment pointer SHLD LOGPTR ; Update pointer MOV B,A ; Save character in B LDA LOGCNT ; Get count INR A ; Increment it STA LOGCNT ; Update count CPI 129 ; Check it RNZ ; If not EOB, return PUSH B ; Save character MVI C,34 LXI D,FCBLOG ; Else, write this sector CALL 5 ORA A JZ ADVRCP ; If ok, cont. CALL ILPRT DB '++ DISK FULL - CANNOT ADD TO LOG ++',0 RET ;... ; ; ADVRCP: LHLD FCBLOG+33 ; Advance record number INX H SHLD FCBLOG+33 CALL RSTLP ; Reset buffer pointers POP PSW ; Get saved character JMP PUTLOG ; Put it in buffer and return ;... ; ; RSTLP: LXI H,LOGBUF ; Reset pointers SHLD LOGPTR ; And return MVI A,0 STA LOGCNT RET ;..... ; ; ; Print number in decimal format (into log file) IN: HL=binary number ; OUT: nnn=right justified with spaces ; PNDEC3: MOV A,H ; Check high byte ORA A JNZ DECOT ; If on, is at least 3 digits MOV A,L ; Else, check low byte CPI 100 JNC TEN CALL PUTSP ; TEN: CPI 10 JNC DECOT CALL PUTSP JMP DECOT ;..... ; ; ; Puts a single space in log file, saves PSW/HL ; PUTSP: PUSH PSW PUSH H MVI A,32 CALL PUTLOG POP H POP PSW RET ;..... ; ; ; Print number in decimal format (into log file) ; PNDEC: CPI 10 ; Two column decimal format routine JC ONE ; One or two digits to area number? JMP TWO ; ONE: PUSH PSW MVI A,48 CALL PUTLOG POP PSW ; TWO: MVI H,0 MOV L,A ; DECOT: PUSH B PUSH D PUSH H LXI B,65526 LXI D,65535 ; DECOT2: DAD B INX D JC DECOT2 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOT MOV A,E ADI 48 CALL PUTLOG POP H POP D POP B RET ;..... ; ; ; Put string to log file ; PUTSTR: MOV A,M PUSH H PUSH B CALL PUTLOG POP B POP H INX H DCR B JNZ PUTSTR RET ;..... ; ; end of LOGCAL routine ;----------------------------------------------------------------------- ; start of TIMEON routine ; ; Calculate time on system and inform user. BYE5 will handle logoff if ; MXTIME is exceeded. ; KTIME: MVI E,255 MVI C,81 ; Ask for MXTIME CALL CKBDOS STA TLIMIT ; And save it MVI E,0 MVI C,81 ; Stop BYE5 from checking time just now CALL CKBDOS MVI C,79 ; Ask for TON and RTC address CALL CKBDOS STA TON ; Save TON LDA CLOCK ORA A JZ KTIME1 LDA DTOS ORA A JZ KTIME1 MVI C,83 CALL CKBDOS ; Have BYE5 print time-on system ; KTIME1: PUSH B LDA TON ; Get time-on-system MOV B,A ; Save it LDA TLIMIT ; Get MXTIME SUB B ; MXTIME-TOS=TLOS (Time-Left-On-System) STA TLOS ; And store it POP B RET ; End of routine in any case ;..... ; ; TON: DB 0 ; Storage for time-on-system TLIMIT: DB 0 ; Storage for MXTIME and status TLOS: DB 0 ; Storage for time-left-on-system ;..... ; ; ; ; DEC8 will convert an 8 bit binary number in A to 3 ASCII bytes. HL ; points to the MSB location where the ASCII bytes will be stored. Any ; leading zeros are suppressed, so store spaces in your buffer before ; calling. ; DEC8: PUSH B PUSH D MVI E,0 ; Leading zero flag MVI D,100 ; DEC81: MVI C,47 ; DEC82: INR C SUB D ; 100 or 10 JNC DEC82 ; Still + ADD D ; Now add it back MOV B,A ; Remainder MOV A,C ; Get 100/10 CPI 49 ; Zero? JNC DEC84 ; Yes MOV A,E ; Check flag ORA A ; Reset? MOV A,C ; Restore byte JZ DEC85 ; Leading zeros are skipped ; DEC84: MOV M,A ; Store it in buffer pointed at by HL INX H ; Increment storage location MVI E,0FFH ; Set zero flag ; DEC85: MOV A,D SUI 90 ; 100 to 10 MOV D,A MOV A,B ; Remainder JNC DEC81 ; Do it again ADI 48 ; Make ASCII MOV M,A ; And store it POP D POP B RET ;..... ; ; end of TIMEON routine ;----------------------------------------------------------------------- ; ; The routine here should read your real-time clock ; and return with the following information: ; ; register: A - current minute (0-59) ; B - current hour (0-23) ; GETTIME:MVI C,79 ; Ask for TON and RTC address CALL CKBDOS MOV A,M ; Get hours on system CALL BCDBIN ; Convert BCD value to binary PUSH PSW ; Save hours on stack INX H ; Point to minutes MOV A,M ; Get minutes CALL BCDBIN ; Convert BCD to binary POP B ; Get hours in B (minuntes in A) RET ;..... ; ; GETDATE:MVI C,79 ; Get RTC address CALL CKBDOS LXI D,4 ; Offset to YY DAD D ; HL=YY Address MOV A,M ; Get YY CALL BCDBIN ; Convert to binary STA YYSAV ; Save YY INX H ; Point to MM MOV A,M ; Get MM CALL BCDBIN ; Convert BCD to binary STA MMSAV ; Save it INX H ; Point to DD MOV A,M ; Get it CALL BCDBIN ; Convert it to binary MOV B,A ; Stuff DD in B LDA YYSAV ; Get YY MOV C,A ; Put YY in C LDA EDATE ORA A LDA MMSAV ; Get MM in A JZ GETD1 MOV D,B MOV B,A MOV A,D ; Return with dd/mm/yy vice mm/dd/yy ; GETD1: RET ; And return ;..... ; ; ; Convert BCD value in A to binary in A ; BCDBIN: PUSH PSW ; Save A ANI 240 ; Mask high nibble RRC ; Move to low nibble RRC RRC RRC MOV C,A ; And stuff in C (C=A) MVI B,9 ; X10 (*9) ; BCDBL: ADD C ; Add original value to A DCR B ; Decrement B JNZ BCDBL ; Loop nine times (A+(C*9)=A*10) MOV B,A ; Save result in B POP PSW ; Get original value ANI 15 ; Mask low nibble ADD B ; +B gives binary value of BCD digit A RET ; Return ;..... ; ; ;----------------------------------------------------------------------- ; ; The following allocations are used by the LOGCALL routines ; PGSIZE: DB 0,0,0 ; Program length in minutes and seconds LOGOPT: DB '?' ; Primary option stored here DEFAULT$DISK: DB 0 ; Disk for open stored here DEFAULT$USER: DB 0 ; User for open stored here FCBCALLER: DB 0,'LASTCALR???',0 ; Last caller file FCB ; DB 0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 ; CALLERPTR: DW LOGBUF ; FCBLOG: DB 0,'KMD LOG' ; Log file FCB DB 0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0 LOGPTR: DW DBUF LOGCNT: DB 0 ; DSKSAV: DB 0 ; Up/download disk saved here USRSAV: DB 0 ; Up/download user saved here ; LOGK: DB 'k ' ; YYSAV: DB 0 MMSAV: DB 0 DDSAV: DB 0 MNSAV: DB 0 ;..... ; ; ;----------------------------------------------------------------------- ; ; Temporary storage area ; FILE: DB 0,'FOR ',0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEST: DB 0,' $$$',0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; CHRCNT: DB 0,0,0 ; 24-bit batch character counter DUSAVE: DB 0,0,0,0 ; Buffer for drive/user MEMFCB: DB ' ' ; Library name (16 bytes required) ;..... ; ; ; Batch stuff ; BCHADR: DW DBUF ; For multiple descriptions BCHPTR: DW 0 BGNMS: DW 0 ; Start address of filenames in TBUFF BLOKK: DW 0 ; # of 2k blocks required by remote BUFADR: DW DBUF ; For multiple file display ; ; BCHFLG: DB 0 ; Batch mode flag DISKNO: DB 0 FCBBUF: DB 0,0,0,0,0,0,0,0 ; Batch filename from command line DB 0,0,0,0,0,0,0 FILCNT: DB 0 ; # of files in batch mode FSTFLG: DB 0 ; Set to 1 when command line scan done FTYCNT: DB 0 MFFLG1: DB 0 MFNAM5: DB 0,0,0,0,0,0,0,0,0,0,0,0 MFNAM6: DB 0,0,0,0,0,0,0,0,0,0,0,0 NAMECT: DB 0 ; # of names on command line NBSAVE: DB 0,0 ; Start address in NAMBUF for next file SHOCNT: DB 0 ; Counter to show files left SNDFLG: DB 0 ; TOTREC: DB 0,0 ; Total records to be sent ;..... ; ; ACKCHK: DB 0 ; Lets batch header use GTACK routine AFBYTE: DB 0 ; Access flags byte storage ANYET: DB 0 ; Any description typed yet? ARCEOF: DB 0 ; .ARC end-of-file flag ARCFST: DB 0 ; .ARC first record flag ARCLST: DB 0 ; .ARC last record byte count (-1) ARCTYP: DB 0 ; Flag if an .ARC lbr BLKSHF: DB 0 BYE5: DB 0 ; If zero, requires external overlay CHKEOT: DB 0 ; Prevents locking up after an EOT CRCFLG: DB 0 ; For sending checksum rather than CRC CONONL: DB 0 ; CTYPE console-only flag COMMA: DB 0 ; Field counter for logcal CPMDRV: DB 0 ; Drive to store CP/M files on DSKFLG: DB 0 ; Flag for BYE5's DISKLOG routine DRUSER: DB 0 ; Original drive/user, for return DUD: DB 0 ; Specified disk DUU: DB 0 ; Specified user EOFLG: DB 0 ; 'EOF' flag (1=yes) EOTFLG: DB 0 ; 'EOT' flag, insures intended finish ERRCT: DB 0 ; Error count FIRST: DB 0 ; Used in file description FRSTIM: DB 0 ; Turned on after first 'SOH' received GOTONE: DB 0 ; Prevents asking for a description HLPFLG: DB 0 ; Lets the help guide finish playing KIND: DB 0 ; Asks what kind of file this is KFLG: DB 0 ; For sending 1k blocks MSGFLG: DB 0 ; Special flag for message file uploads OLDDRV: DB 0 ; Save the original drive number OLDUSR: DB 0 ; Save the original user number OPTSAV: DB 0 ; Save option here for carrier loss PRVTFL: DB 0 ; Private user area option flag RCVCNT: DB 0 ; Record number received RCVDRV: DB 0 ; Requested drive number RCVTRY: DB 0 ; Keeps track of number of attempts RCVUSR: DB 0 ; Requested user number RWHEEL: DB 0 ; Shows wheel byte is set SPLFL: DB 0 ; Special flag for private downloads SYSABT: DB 0 ; Local Sysop transfer abort with ^X ; ACCERR: DW 0 ; No 'ACK' error count for 1k ratio ARCCNT: DW 0 ; .ARC record count ARCPTR: DW 0 ; .ARC input record pointer ARCREC: DW 0 ; .ARC current record number BLKMAX: DW 0 CRCVAL: DW 0 ; Current CRC value DIRSIZ: DW 0 ; Directory size HDRADR: DW 0 ; Current location in batch header block INDEX: DW 0 ; Index into directory MINUTE: DW 0 ; Transfer time in mins for MAXTIM OUTADR: DW DBUF OUTPTR: DW 0 OUTSIZ: DW 16*1024 ; Size of 'DESCRIB' buffer (use caution) RCNT: DW 0 ; Record count RECDNO: DW 0 ; Current record number RCDCNT: DW 0 ; Used in sending the record header RECPTR: DW DBUF RECNBF: DW 0 ; Number of records in the buffer SAVEHL: DW 0 ; Saves TBUF command line address ; ; HLINE: DB '----',13,10 OLINE: DS 80 ; Temporary buffer to store line DS 60 ; Area for stack ;..... ; ; ; 16k disk buffer ; ORG ($+255)/256*256 ; CMDBUF: DS 128 ; Store TBUFF here in batch mode STACK EQU CMDBUF-2 NAMBUF: DS 24*128 ; Allow room for 256 batch filenames DBUF: DS 128*128 ; 16k disk buffer BUFSTR EQU DBUF+126 ; For file length in batch mode LOGBUF EQU DBUF+128 ; For use with LOGCAL ; ; END