home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
NUBYE
/
NUKMD111.ARK
/
NUKMD111.FL2
< prev
next >
Wrap
Text File
|
1987-02-03
|
89KB
|
3,777 lines
; 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, stop the sender and 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 ++',CR,LF,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,ACK ; 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,STX ; If yes, send a STX rather than SOH
JNZ $+5
MVI A,SOH ; 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: LDA CRCFLG
ORA A
JNZ SNDREC1
CALL CATCH
;
SNDREC1: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
ORA A ; Last block 1k?
JZ $+6 ; No, 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,-1
ORA A
JZ $+6
LXI D,-8
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 (about
; 10 seconds), a timeout error message is shown and the record will be
; re-sent.
;
GTACK: CALL MDINST ; See if a character is ready, now
JZ GTACK1 ; If not exit
CALL MDINP ; Get the character in 'A' register
CPI ACK ; See if an ACK already
RZ ; If yes, return
CPI NAK ; See if a NAK
JZ GTACK2 ; If yes, print error, then resend
CPI CANCEL ; ^X to cancel attempt?
JZ GTCAN
;
GTACK1: MVI B,1 ; 1 second for an ACK or NAK
CALL RECV ; Go wait for a character
JC GTACK2 ; No character, timed out
CPI ACK ; ACK?
RZ ; Yes
CPI NAK ; NAK?
JZ GTACK3
CPI CANCEL ; ^X to cancel?
JZ GTCAN
;
GTACK2: 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 ACK ; ACK?
RZ ; Yes
CPI 7BH ; V.22 synch character?
JZ GTACK2 ; Yes, ignore
CPI 0FBH ; V.22 synch character?
JZ GTACK2 ; Yes, ignore
CPI CANCEL ; ^X to cancel?
JZ GTCAN
;
GTACK3: MOV B,A ; Save the character
LDA CHKEOT ; EOT?
ORA A
JNZ ACKERR ; If yes, don't show error (for NUKMD)
CALL ILPRTL
DB '++ ',0
MOV A,B
CPI NAK
JZ GTACK4
CALL HEXO
CALL ILPRT
DB 'H',0
JMP GTACK5
;
GTACK4: CALL ILPRT
DB 'NAK',0
;
GTACK5: CALL ILPRT
DB ' received not ACK ++',CR,LF,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
CALL RDCOUNT ; Else show the record count for repeat
STC ; Make sure carry is set for repeat
RET ; And go back
;
; Reached error limit
;
ACKMSG: CALL WAIT1 ; Wait for any input to stop
MVI A,CANCEL ; Tell remote we are quitting
CALL SEND
CALL SEND
CALL SEND
MVI B,1 ; Wait for remote to perhaps quit too
CALL RECV
MVI A,BS
CALL SEND ; Clear any ^X's from buffer
CALL SEND
CALL SEND
XRA A ; Reset flag to show remote also
STA CONONL
CALL ERXIT
DB CR,LF,'++ TRANSFER ABORTED ++','$'
;
; Timed out, with no character - set the carry bit and return
;
GTATOT: CALL EOTCHK ; See if EOT has been received
CALL ILPRTL
DB '++ Timeout - no character received ++',CR,LF,0
JMP ACKERR
;
; Two or more ^X's will cancel the file transfer
;
GTCAN: MVI B,2 ; Up to two seconds for another ^X
CALL RECV
MVI A,CANCEL ; Get original character back
JC GTACK3 ; If no more ^X, display the first
CPI CANCEL ; Second one?
JZ ACKMSG ; Yes, abort
JMP GTACK3 ; ...else, display the character
;
; 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,-8 ; 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
LDA MSPEED ; Get current speed
CPI 5 ; 1200 baud?
MVI A,71-1 ; (for 1200 bps)
JZ $+5 ; If 1200, skip next line
MVI A,43-1 ; (for 2400 bps)
CMP C ; Compare with actual ratio
RC ; Return if less hits than allowed
;
GTRATIO1:
MVI A,1
STA NOISY ; Show noisy lines caused switch
XRA A ; Else reset the system to 128
STA KFLG
CALL ILPRTL
DB CR,LF
DB '++ Noisy line -- switching to 128-byte Xmodem protocol ++'
DB CR,LF,0
RET
;
CKABORT:CALL CONSTAT
ORA A
RZ
CALL CONIN
CPI CANCEL
RNZ
;
; Aborts send or receive routines and returns to command line
;
ABORT: LXI SP,STACK
CALL WAIT1 ; 1-second delay to clear input
MVI A,CANCEL ; Show you are cancelling
CALL SEND ; They may quit also with enough ^X
CALL SEND
CALL SEND
CALL WAIT1 ; 1-second delay to clear input
MVI A,BS
CALL SEND
CALL SEND
CALL SEND
;
ABORTX: CALL CATCH
LDA OPTSAV
CPI 'R' ; Receiving?
JZ RCVSABT ; Yes, so delete and abort
CALL ERXIT ; Exit with abort message
DB CR,LF,'++ NUKMD ABORTED ++','$'
;
; Check to see if an EOT has been received -- if so, remote end doesn't
; wait for ACK of first EOT sent to us.
;
EOTCHK: LDA EOTFLG ; Get status
ORA A ; EOT received?
RZ ; Return if not
CALL ILPRTL ; Else, display local only for sysop
DB CR,LF,LF
DB '++ User''s terminal program doesn''t wait for EOT ACK ++'
DB CR,LF,0
JMP RCVEOT ; All finished -- save file
;
; Increment record number
;
INCRNO: PUSH H
PUSH D
XRA A
STA EOTFLG ; Clear end of transmission flag
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:
LDA OPTSAV
CPI 'R' ; Receiving?
JZ RMSG ; Yes, else...
LDA KFLG
ORA A ; Ymodem download?
JZ XSCNT ; No, else...
CALL ILPRTL
DB CR,'Snd Ymdm # ',0 ; Sending
JMP REST
;
XSCNT: CALL ILPRTL
DB CR,'Snd Xmdm # ',0 ; Sending
JMP REST
;
RMSG: LDA KFLG
ORA A ; Ymodem upload?
JZ XRCNT ; No, else...
CALL ILPRTL
DB CR,'Rcv Ymdm # ',0
JMP REST
;
XRCNT: CALL ILPRTL
DB CR,'Rcv Xmdm # ',0
;
REST: LDA KFLG
ORA A ; Ymodem xfr?
LHLD RECDNO
JZ $+6 ; No, so skip next line
CALL DIVREC
CALL DECOUT
CALL ILPRT
DB ' ',0
CALL FUNCHK ; Check for function keys
RET ; From INCRNO
;
; See if file exists - if it exists, ask for a different name.
;
CHEKFIL: IF NOT SETAREA
LDA PRVTFL ; Receiving in private area?
ORA A
CNZ RECAREA ; If yes, set drive and user area
ENDIF
;
IF SETAREA
CALL RECAREA ; Set the designated area up
ENDIF
;
CALL FILSCH ; See if file exists
INR A
RZ ; No, so return
MVI A,CANCEL ; Else inform user and abort
CALL SEND ; Several cancel requests
CALL SEND
CALL SEND
;
CHEKF1: MVI B,1
CALL RECV
JNC CHEKF1 ; Wait until no more characters
LDA BCHFLG ; Using batch mode now?
STA CONONL ; If not, send message to modem also
ORA A
JZ CHEKF2 ; If not, exit
MVI A,CANCEL
CALL SEND
CALL SEND
CALL SEND
MVI A,BS
CALL SEND
;
CHEKF2: CALL ERXIT ; Exit, print error message
DB '++ File exists -- use a different name ++','$'
;
; Creates the file to be received
;
MAKEFIL:XRA A ; Set extent and record number to 0
STA FCBEXT
STA FCBRNO
MVI C,MAKE ; Get BDOS FNC
CALL FCBSET ; Make file
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,FILSIZ ; Computes file size
CALL FCBSET ; Read first
LHLD RANDOM ; Get the file size
SHLD RCNT ; Save total record count
RET
;
; -----
;
; Opens the file to be sent
;
OPNFIL: XRA A ; Set extent and rec number to 0
STA FCBEXT ; For proper open
STA FCBRNO
LXI D,FCB ; Point to file
CALL OPENIT ; Open it
INR A ; Open ok?
JNZ OPNOK ; If yes, exit
LDA OPTSAV
CPI 'L' ; .LBR?
JZ NOLBR ; Abort, not found
CPI 'A' ; .ARK/.ARC?
JNZ NONAME ; Abort, no match
;
IF NOT ARCEXT
JMP NOARK ; Abort, .ARK/.ARC not found
ENDIF
;
; If the file doesn't open with .ARK (default if no type specified),
; try .ARC and abort if still no good.
;
IF ARCEXT
ARCSET: LXI H,FCBTYP ; Point to type
MVI M,'A'
INX H
MVI M,'R'
INX H
MVI M,'C' ; Force .ARC type
LXI D,FCB ; Point to file
CALL OPENIT ; Open it
INR A ; Open ok?
JNZ OPNOK ; Yes, continue
ENDIF
;
NOARK: CALL ERXIT
DB CR,LF,'++ No .ARK/.ARC file found with that name ++','$'
;
NOLBR: CALL ERXIT
DB CR,LF,'++ No .LBR file found with that name ++','$'
;
NONAME: CALL ERXIT
DB CR,LF,'++ No file found with that name ++','$'
;
ZEROLN: CALL ERXIT
DB CR,LF,'++ Can''t send a 0-length file ++','$'
;
OPNOK: IF ZCPR
LDA WHEEL ; Check wheel status if ZCPR
ORA A ; Is it zero
JNZ OPNOK1 ; If non-zero skip all restrictions
ENDIF
;
IF TAGLBR
LDA LBRARC
ORA A ; Member extraction?
JNZ OPNOK0 ; Yes, skip SYS check
LDA FCB+1 ; First character of file name
ANI 80H ; Check bit 7
JNZ OPNOT1 ; If bit 7 is set, file is tagged
LDA FCB+2 ; Also check 'F2' for a tag
ANI 80H ; Is it set?
JNZ OPNOT ; If yes, cannot be downloaded
ENDIF
;
OPNOK0: IF SNDSYS
LDA FCB+10
ANI 80H
JNZ NONAME ; If $SYS then fake a "file not found"
ENDIF
;
JMP OPNOK1 ; If not, ok to send file
;
OPNOT: CALL ERXIT ; Exit with message
DB CR,LF,'++ File is not for download ++','$'
;
OPNOT1: CALL ERXIT ; Exit with message
DB CR,LF
DB '++ Download internal .LBR/.ARK/.ARC member files ++'
DB '$'
;
OPNOK1: LDA BCHFLG ; Requesting batch mode?
ORA A
JNZ OPNOK2 ; If yes, skip .LBR/.ARK/.ARC stuff
LDA LBRARC
ORA A ; Member extraction?
JZ OPNOK2 ; No, skip .LBR/.ARK/.ARC stuff
;
OPOK1A: CALL RSTDMA ; Reset to default DMA
CALL REDFCB ; Read FCB
ORA A ; Read ok?
JNZ RDERR ; No, abort
LDA FCBTYP ; Get 1st character of filename type
ANI 7FH ; Strip high bit
CPI 'A' ; ARK/ARC?
JZ CKARC ; Yes, skip .LBR stuff
LHLD 8EH ; Get filesize
SHLD DIRSIZ ; Store
LXI H,TBUF
MOV A,M
ORA A
JZ CKDIR ; Check directory present?
;
NOTLBR: CALL ERXIT
DB CR,LF,'++ Bad .LBR directory -- please tell Sysop ++','$'
;
; 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,' ' ; 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 0...
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,TBUF ; 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 ; 0 = Active, anything else can be...
MVI B,11 ; regarded as invalid (erased or blank)
INX H ; Point to member name
JNZ NOMTCH ; No match if inactive entry
;
CKLP: LDAX D ; Now compare the file name specified...
CMP M ; Against the member file name
JNZ NOMTCH ; Exit loop if no match found
INX H
INX D
DCR B
JNZ CKLP ; 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 RANDOM ; Place it into random field
XRA A
STA RANDOM+2 ; Must zero the 3rd byte
STA FCBRNO ; Also zero FCB record #
LXI D,FCB ; Point to FCB of .LBR file
CALL RRANDM ; Read it
JMP OPNOK3 ; No need to error check
;
; Come here if no file name match and another sector is needed
;
NOMTCH: INX H ; Skip past the end of the file entry
DCR B
JNZ NOMTCH
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 INLBR ; Continue if still more to check
;
NOMBER: CALL ERXIT
DB CR,LF
DB '++ Requested file member not found ++','$'
;
INLBR: DCX H ; Decrement dirctory size
SHLD DIRSIZ
CALL REDFCB ; Read next sector of DIR to TBUF
ORA A ; Read ok?
JNZ NOTLBR ; If not, error or end of file
LXI H,TBUF ; Set our pointers for compare
LXI D,MEMFCB
JMP CMLP ; Check next sector
;
; .ARK/.ARC file routine -- for "self-unpacking" archive files (i.e.
; MS-DOS ARC and PKARK .COM files and CP/M's NOAH in the future), up
; to 3 extra bytes are allowed before finding the ARCMaRK.
;
CKARC: LXI H,TBUF ; Set pointer for compare
MVI B,3 ; Allow up to 3 xtra bytes at start
MVI A,ARCMRK
;
CKARC1: CMP M ; Header marker found?
JZ CKARC3 ; Yes, else...
INR L ; Bump record pointer
DCR B
JNZ CKARC1 ; Loop through extra bytes allowed
;
CKARC2: MOV A,M ; Get next character
CPI ARCMRK ; Header marker?
JNZ ARCERR ; No, report
;
CKARC3: LXI D,DBUF ; Disk buffer pointer
STAX D ; Store header marker
INX D
INR L ; Bump to next byte
CZ ARCRD ; Read next record if end
MVI B,HDRSIZ ; Set up counter (normal header size)
MOV A,M ; Get next char (compression type 1-8)
STA ARCVER ; Store it
MOV C,A ; Save in C for counter
CPI 1 ; Compare against vers 1 (old type)
JC NOMBER ; EOF, report member not found
JNZ CKARC4 ; >1, so skip next
INR A ; 1, so bump to type 2
MVI B,HDRSIZ-4 ; Set up counter (old header size)
;
CKARC4: STAX D ; Store byte
INX D
DCR B ; Decrement header byte counter
JZ CKARC5 ; Header stored, so continue
INR L ; Bump to next byte
CZ ARCRD
MOV A,M ; Get next header byte
JMP CKARC4 ; Loop until header is stored
;
CKARC5: SHLD ARCPTR ; Store current record pointer
LXI H,DBUF+15 ; Get compression type
DCR C ; If compression type was 1...
MVI C,4 ; ...4 more bytes
CZ MOVER ; ...move up to uncompressed file size
LXI D,DBUF+2 ; Get next member filename
LXI H,MEMFCB ; Get requested member filename
MVI B,11 ; Length of filename.ext
;
CKARC6: LDAX D ; Get next byte
ANI 7FH ; Strip high bit
JZ CKARC7 ; Fill with blanks if end of name
INX D ; Bump to next byte
CALL UCASE ; Ensure it's upper case
CPI '.' ; Type separator?
JNZ CKARC8 ; No, compare
MOV A,B ; Get character count
CPI 4
JC CKARC6 ; Yes, so bypass separator
DCX D ; Backup to '.'
;
CKARC7: MVI A,' ' ; Use blank to fill file name and/or type
;
CKARC8: CMP M ; Match requested member name?
JNZ ONWARD ; No, skip to next member
INX H ; Bump to next byte
DCR B ; One less for filename.ext length
JNZ CKARC6 ; Loop until all characters are compared
LXI H,DBUF+18 ; Point to MSB of size
MOV A,M
ORA A ; 0?
JNZ ARCERR ; Abort - corrupt header?
DCX H
MOV D,M ; Middle two bytes of size to DE
DCX H
MOV E,M
DCX H ; Point to LSB of size
ORA M ; Test it
XCHG ; Page count
DAD H
JC TOOBIG ; Abort, too big
JP CKARC9 ; Skip next if byte count <128
INX H ; Add another record
ANI 7FH ; Reduce byte count
;
CKARC9: LXI D,1 ; Need one more byte
ADI 30
JP CKARCA ; Skip next unless xtra 2 records needed
ANI 7FH ; Last byte offset
INR E ; Show one more record needed
;
CKARCA: DAD D ; Total records
JC TOOBIG ; Abort, too big
SHLD RCNT ; Save record count
SHLD ARCCNT
STA ARCLST ; Save last record count -1
JMP OPNOK3 ; All done, continue with download
;
ARCERR: CALL ERXIT
DB CR,LF,' ++ Requested member not found, or ++'
DB CR,LF,'++ bad .ARK/.ARC header -- inform Sysop ++','$'
;
TOOBIG: CALL ERXIT
DB CR,LF,'++ Aborted -- too large for CP/M ++','$'
;
; Read record
;
ARCRD: PUSH B ; Save registers
PUSH D
LHLD ARCREC ; Get current record number
INX H ; Bump it
SHLD ARCREC ; Store next record number
CALL REDFCB ; Read next sector of DIR to TBUF
POP D ; Restore registers
POP B
LXI H,TBUF ; Set pointers
ORA A
RZ ; EOF
;
ARCRD1: MOV M,H ; Fill record with 0's
INR L
JNZ ARCRD1 ; Loop until record filled with 0's
STA ARCEOF ; Set EOF flag
MVI L,TBUF ; Set pointers
RET
;
; Increment to next .ARK/.ARC member
;
ONWARD: LHLD DBUF+16 ; # whole pages to skip
DAD H ; # records to skip
LDA DBUF+15 ; # extra bytes to skip
ORA A ; >128?
JP ONWRD1
INX H ; Add one more record
ANI 7FH ; Reuce byte count
;
ONWRD1: XCHG ; Record offset to DE
LHLD ARCPTR ; Set to last byte of header
INR L ; Bump to next byte
ADD L ; Add byte offset
JP ONWRD2 ; Skip next if overflows current record
MOV L,A ; Set to start of next record
MOV A,D ; Check record offset
ORA E
JZ CKARC2 ; Still in same record, so loop
JMP ONWRD3 ; Read new record
;
ONWRD2: ORI 80H ; Get proper byte offset in DMA
MOV L,A ; Point to next header
INX D
;
ONWRD3: SHLD ARCPTR ; Save buffer pointer
LHLD ARCREC ; Get current record number
DAD D ; Add record offset
SHLD ARCREC ; Save new record number
SHLD RANDOM
XRA A
STA 7FH ; Clear 3rd byte
LXI D,FCB
CALL RRANDM ; Read random record
ORA A ; Ok?
JNZ NOMBER ; No, report member not found
LXI H,FCBRNO ; Current record
INR M ; Bump for sequential read
LHLD ARCPTR ; Get buffer pointer
JMP CKARC2 ; Loop to next member (1)
;
; =====
;
OPNOK2: IF ZCPR
LDA WHEEL ; Check status of wheel if ZCPR
ORA A ; Is it set?
JNZ OPNOK3 ; If yes, skip filetype checks
ENDIF
;
IF SNDWILD OR SNDCOM
LXI H,FCB+11
MOV A,M ; Check for protect attribute
ANI 7FH ; Remove CP/M 2.x attributes
ENDIF
;
IF SNDWILD
CPI '#' ; Wild card designator (? or *)?
JZ OPNOT ; Yes, so tell user
ENDIF
;
IF SNDCOM
CPI 'M' ; M?
JNZ OPNOK3 ; If not, ok to send
DCX H
MOV A,M ; Check next character
ANI 7FH ; Strip attributes
CPI 'O' ; O?
JNZ OPNOK3 ; If not, ok to send
DCX H
MOV A,M ; Now check 1st character
ANI 7FH ; Strip attributes
CPI 'C' ; C?
JNZ OPNOK3 ; If not, continue
CALL ERXIT ; Exit with message
DB CR,LF,'++ Can''t download a .COM file ++','$'
ENDIF
;
OPNOK3: LHLD RCNT ; Get record count
MOV A,H
ORA L ; Are there any?
JZ ZEROLN ; No, else continue
LDA MSPEED
CPI 5 ; <1200?
JC TOOSLO ; Yes, so skip 1k mention
PUSH H ; Save RCNT
CALL ILPRT
DB CR,LF
DB 'Ymodem packets total > ',0
POP H ; Restore RCNT
CALL DIVREC ; Divide number of records by 8
CALL DECOUT ; Show # of kilobytes
;
TOOSLO: CALL ILPRT
DB CR,LF
DB 'Xmodem packets total > ',0
LHLD RCNT ; Get original count
CALL DECOUT
LDA SNDFLG ; Receiving batch mode now?
ORA A
RNZ ; If yes, all done
CALL ILPRT
DB CR,LF
DB 'Disk space you need > ',0
LHLD RCNT ; Get original count
CALL DIVREC ; Divide by 8 for k
CALL DECOUT
CALL ILPRT
DB 'k',0
;
; Show transfer time, first for 1k blocks, then for 128 (skip the 1k
; times for slower than 1200 bps.) for 1200 bps
;
KSPEED: LDA MSPEED
CPI 5 ; <1200 bps?
JC XSPEED ; Skip 1k display if yes
CALL ILPRT
DB CR,LF
DB 'Ymodem time / 1k packets > ',0
CALL KTIM ; Get file transfer time in BC (minutes)
CALL STORTIM ; Store for comparing time remaining
CALL OPNOK4
;
XSPEED: CALL ILPRT
DB CR,LF
DB 'Xmodem time / 128-byte packets > ',0
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 OPNOK4
LXI H,KECTBL ; Restore to original 1k values
SHLD RECTBL+1
CALL ILPRT
DB CR,LF,0
LDA BCHFLG
ORA A
CNZ CUMSTS ; Show how many files remain after this
LDA FSTFLG
ORA A
RNZ
LDA OPTSAV
CPI 'A' ; .ARK/.ARC?
JNZ SKPARC ; no, skip rename message
;
; If this is an .ARK/.ARC member exctraction, notify user that file must be
; named as an .ARK or .ARC file type (for NOAH, ARC, PKXARC, etc).
;
CALL ILPRT
DB CR,LF
DB 'You MUST name this file > ',0
MVI D,8 ; Set up for filename count (ignore type)
LXI H,MEMFCB ; Get requested member name
;
RENARC: MOV A,M
CPI ' ' ; Short name?
JZ RENAR1 ; Yes, fill in type
CALL TYPE
DCR D ; One less to go
INX H ; Get next character
JNZ RENARC ; Loop until done
;
RENAR1: LDA FCBEXT-1 ; Get last character of parent file type
STA RENAR2 ; Stuff it below to display
CALL ILPRT
DB '.AR' ; Common type characters
RENAR2: DW 0 ; Last will be either 'K' or 'C' (1)
;
SKPARC: CALL ILPRT
DB CR,LF,LF,'Your selection ready to Download'
DB CR,LF,' Abort: CTRL-X <pause> CTRL-X'
DB CR,LF,LF,0
CALL ILPRTL ; Display local
DB ' [ waiting ]',CR,0
RET
;
OPNOK4: PUSH H ; Save seconds in 'L'
;
IF ZCPR
LDA WHEEL ; Check wheel status if ZCPR
ORA A ; 0?
JNZ SKPTIM ; NO, so skip the limit
ENDIF
;
LDA TLIMIT ; See if special user
ORA A
JZ SKPTIM ; Yes, skip this
MOV A,C ; If limiting get length of this program
INR A ; Increment to next full minute
;
IF TIMEON
LXI H,TON
ADD M ; Add time on to xfer time, TON will
ENDIF
;
STA MINUTE ; Store value for later comparison
MOV A,B ; Get high byte of minute if >255
JNZ MXTMC2 ; If no carry from increment/add
INR A
;
MXTMC2: 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
;
IF ZCPR
LDA WHEEL ; Get WHEEL status
ORA A ; 0?
RNZ ; Yes, else check time limit
ENDIF
;
LDA TLIMIT ; Check user with unlimited time
ORA A ; 0?
RZ ; Yes
LDA MINUTE+1 ; Get minute count high byte
ORA A ; Check if zero
JNZ OVERTM ; 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
RNC ; If less, it's ok to continue
;
OVERTM: CALL ILPRT
DB CR,LF,LF,'+++ NUKMD ABORTED +++',CR,LF,LF
DB 'Required 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.',CR,LF,'$'
;
KTABLE: DW 5,14,21,27,32,53,101,190,330,525,0
KECTBL: DB 192,69,46,36,30,18,10,5,3,2,0
XTABLE: DW 5,13,19,25,30,48,85,141,210,280,0
XECTBL: DB 192,74,51,38,32,20,11,8,5,3,0
;
; 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: LDA MSPEED ; Get speed indicator
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
LDA MSPEED ; 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: IF LOGCAL
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
ENDIF
;
RET ; End of FILTIM routine
;
; This routine divides the total number of 1024-byte blocks by 8.
;
DIVREC: 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
RET
;
; 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 9+1 ; 10 seconds or more?
RNC ; If yes, disregard
CALL ILPRT
DB '0',0
RET
;
; end of open file, set time routine
; ----------------------------------
;
; Closes the received file
;
CLOSFIL:LXI D,FCB ; Point to file
CALL CLOSEF ; Close it
INR A ; Close ok?
RNZ ; Yes, return
CALL ERXIT ; No, abort
DB '++ No file or can''t close it ++','$'
;
; Decimal output routine - call with decimal value in 'HL'
;
DECOUT: PUSH B
PUSH D
PUSH H
LXI B,-10
LXI D,-1
;
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 0FH
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
CPI 'A' ; .ARK/.ARC member extraction?
JZ RDARC ; Yes, so skip rest
CALL DMASET ; Set DMA
CALL REDFCB ; Read FCB
;
RDBLK2: 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
CPI BUFSIZ*8 ; Done?
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
MVI C,STDMA ; Reset to default DMA
LXI D,TBUF
JMP BDOS ; from CALL RDBLK1
;
REOF: DCR A ; 'EOF'?
JNZ RDERR ; Got 'EOF'
MVI A,1
STA EOFLG ; Set EOF flag
MOV A,C
JMP RDBFULL
;
; .ARK/.ARC read file routine
;
RDARC: LXI B,32768 ; B=128 C=0
LHLD ARCCNT ; Get record count
DCX H ; Bump down one
SHLD ARCCNT ; Save new count
MOV A,H
ORA L ; Last record?
JNZ RDARC1 ; No, skip next
LDA ARCLST ; Get # bytes -1 in last record
MOV C,B
MOV B,A
ORA A
JZ RDARC3
XRA C
MOV C,A
;
RDARC1: LHLD ARCPTR ; Get record pointer
LDA ARCFST
ORA A ; First record?
JNZ RDARC2 ; No, skip next
LXI D,DBUF+29 ; Skip header
MOV A,B
SUI 29
MOV B,A
STA ARCFST ; Show not first time
;
RDARC2: INR L ; Next byte
CZ ARCRD ; Fill buffer if end
MOV A,M ; Get byte
STAX D
INX D
DCR B
JNZ RDARC2 ; Loop until all bytes moved
SHLD ARCPTR ; Save new pointer
XRA A ; Clear all
CMP C
JZ RDARC4 ; Skip next
;
RDARC3: STAX D ; Store EOF in buffer
INX D
INR C
JNZ RDARC3 ; Loop to zero final record
;
RDARC4: LDA ARCEOF ; Get flag status
JMP RDBLK2 ; Return for more
;
; Read error
;
RDERR: CALL ERXIT
DB '++ File read error ++','$'
;
; end of read record routine
; --------------------------
;
; 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 chars/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
CPI BUFSIZ*8 ; Is the buffer full, yet?
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 DMASET ; Set DMA to buffer
MVI C,WRITE
CALL FCBSET ; Write block
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: MVI C,STDMA
LXI D,TBUF ; Reset DMA address
JMP BDOS
;
WRERR: CALL RSDMA ; Reset DMA to normal
MVI C,CANCEL ; 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' regs.
MVI E,MHZ ; Get the clock speed
XRA A ; Clear the 'A' reg.
;
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: LXI D,3100 ; 1 second DCR loop count
;
MWTI: CALL MDINST ; 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 MDCARCK ; 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 MDINP ; 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
; TIMOUT seconds, normal program execution continues. Else, it will
; abort to CP/M via EXIT.
;
CARCK: MVI E,TIMOUT*10 ; Value for 15 second delay
;
CARCK1: CALL DELAY ; Kill .1 seconds
CALL MDCARCK ; 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 CR,LF,LF,0
LDA OPTSAV ; Get option
CPI 'R' ; If not receive
JNZ EXIT ; Then abort now, else
CALL ILPRT
DB CR,LF
DB '++ Deleting partial Upload -- Carrier Lost ++'
DB CR,LF,0
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'
LXI B,MHZ*4167 ; Value for 100 ms. delay
;
DELAY2: DCX B ; Update count
MOV A,B ; Get MSP byte
ORA C ; Count = zero?
JNZ DELAY2 ; If not, continue
POP B ; Restore 'BC'
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: IF MSGDSC OR DESCRIB
LDA OPTSAV
CPI 'R' ; Uploading?
RNZ ; No, so return
LDA PRVTFL
ORA A ; Upload private?
RNZ ; Yes, no descriptions required
ENDIF
;
IF (MSGDSC OR DESCRIB) AND RESUSR AND PUPOPT
LDA PUPFLG ; Get privileged status
ORA A ; Privileged xfr request?
RNZ ; Yes, skip description
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
MVI A,1
STA DSCFLG ; Show description file
ENDIF
;
IF MSGDSC OR DESCRIB
LDA FILCNT ; Files received batch?
ORA A
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
RZ ; Single file xfr -- all done here
ENDIF
;
IF (NOT MSGDSC) AND DESCRIB
JZ ASK1 ; If not, exit
ENDIF
;
IF MSGDSC OR DESCRIB
LXI H,NAMBUF ; ...else, get filename
SHLD NBSAVE
CALL BCHDCR
ENDIF
;
ASK1: IF MSGDSC AND (NOT DESCRIB)
CALL DILPRT
DB CR,LF,0
JMP ASK2
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
CALL SHONM ; Show the file name
CALL DILPRT
DB ' <<Description Entry Phase>>',CR,LF,0
ENDIF
;
IF MSGDSC OR DESCRIB
ASK2: LDA CHKASK
ORA A ; Already been this way?
JNZ ASK3 ; Yes, so skip rest
MVI A,1
STA CHKASK ; Show we've been here, now
CALL DILPRT
DB CR,LF,LF
DB 'If you have pre-typed your description(s) and '
DB 'wish to turn off wrap',CR,LF
DB 'during each description text transfer, answer (Y)es.'
DB CR,LF,LF
DB 'Turn off automatic end-of-line wrap? <N> ',0
CALL INPUT
ANI 5FH ; Change to upper case
CPI 'Y' ; Turn off wrap mode?
JNZ ASK1A ; No
CALL DILPRT ; ...else
DB 'YES',CR,LF,'(Automatic wrap mode is now OFF)',0
MVI A,72
STA XWRAP ; Turn off wrap mode
JMP ASK3 ; Ask for description
;
ASK1A: CALL DILPRT
DB 'NO',CR,LF,'(Automatic wrap mode is now ON)',0
;
ASK3: CALL DILPRT
DB CR,LF,LF
DB 'Please describe this file in 7 lines or less. '
DB 'Tell what equipment it is',CR,LF
DB 'for and what the program does. Hit an extra CR'
DB ' on a blank line to quit.',CR,LF,LF,0
;
; Get the file name from FCB, skip any blanks
;
LXI H,HLINE
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
CALL DSTOR2 ; Store initial header info
CALL LSTCLR ; Get caller's name
CALL DSTOR3 ; Store it in header
LXI H,HLINE1
CALL DSTOR2 ; Store remaining header info
LDA DSKSAV ; Get upload drive
INR A
ADI 'A'-1
CALL OUTCHR
LDA USRSAV ; Get upload user area
CALL PNDEC ; Convert and store
MVI A,':'
CALL OUTCHR
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
CALL DSTOR1 ; Store header info
ENDIF
;
IF MSGDSC OR DESCRIB
MVI B,8 ; Get FILENAME
LXI D,FCB+1
LXI H,OLINE
CALL LOPFCB
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
LDAX D
CPI ' ' ; Any file extent?
JZ AFIND1 ; If not, skip the period and extent
ENDIF
;
IF MSGDSC OR DESCRIB
MVI A,'.'
MOV M,A ; Separate FILENAME and EXTENT
CALL TYPE
INX H
MVI B,3 ; Get EXTENT name
CALL LOPFCB
ENDIF
;
AFIND1: IF DESCRIB AND (NOT MSGDSC) AND USEMENU AND XTRA1
LDA CHOICE
CPI 'B'
JZ AFIND2
ENDIF
;
IF DESCRIB AND (NOT MSGDSC) AND USEMENU AND XTRA2
CPI 'C'
JZ AFIND3
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
LDA KIND
CPI '0'
LXI D,KIND0+4
CZ DKIND ; File category 0
CPI '1'
LXI D,KIND1+4
CZ DKIND ; File category 1
CPI '2'
LXI D,KIND2+4
CZ DKIND ; File category 2
CPI '3'
LXI D,KIND3+4
CZ DKIND ; File category 3
CPI '4'
LXI D,KIND4+4
CZ DKIND ; File category 4
CPI '5'
LXI D,KIND5+4
CZ DKIND ; File category 5
CPI '6'
LXI D,KIND6+4
CZ DKIND ; File category 6
CPI '7'
LXI D,KIND7+4
CZ DKIND ; File category 7
CPI '8'
LXI D,KIND8+4
CZ DKIND ; File category 8
CPI '9'
LXI D,KIND9+4
CZ DKIND ; File category 9
ENDIF
;
IF DESCRIB AND (NOT MSGDSC) AND USEMENU AND XTRA1
JMP AFIND4 ; Skip next
AFIND2: LDA KIND
CPI '0'
LXI D,KIND0B+4
CZ DKIND ; File category 0
CPI '1'
LXI D,KIND1B+4
CZ DKIND ; File category 1
CPI '2'
LXI D,KIND2B+4
CZ DKIND ; File category 2
CPI '3'
LXI D,KIND3B+4
CZ DKIND ; File category 3
CPI '4'
LXI D,KIND4B+4
CZ DKIND ; File category 4
CPI '5'
LXI D,KIND5B+4
CZ DKIND ; File category 5
CPI '6'
LXI D,KIND6B+4
CZ DKIND ; File category 6
CPI '7'
LXI D,KIND7B+4
CZ DKIND ; File category 7
CPI '8'
LXI D,KIND8B+4
CZ DKIND ; File category 8
CPI '9'
LXI D,KIND9B+4
CZ DKIND ; File category 9
ENDIF
;
IF DESCRIB AND (NOT MSGDSC) AND USEMENU AND XTRA2
JMP AFIND4 ; Skip next
AFIND3: LDA KIND
CPI '0'
LXI D,KIND0C+4
CZ DKIND ; File category 0
CPI '1'
LXI D,KIND1C+4
CZ DKIND ; File category 1
CPI '2'
LXI D,KIND2C+4
CZ DKIND ; File category 2
CPI '3'
LXI D,KIND3C+4
CZ DKIND ; File category 3
CPI '4'
LXI D,KIND4C+4
CZ DKIND ; File category 4
CPI '5'
LXI D,KIND5C+4
CZ DKIND ; File category 5
CPI '6'
LXI D,KIND6C+4
CZ DKIND ; File category 6
CPI '7'
LXI D,KIND7C+4
CZ DKIND ; File category 7
CPI '8'
LXI D,KIND8C+4
CZ DKIND ; File category 8
CPI '9'
LXI D,KIND9C+4
CZ DKIND ; File category 9
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
LXI D,HLINE3
CALL DKIND
ENDIF
;
AFIND4: IF MSGDSC OR DESCRIB
CALL DSTOR ; Put FILENAME line into memory and show
LDA XWRAP
CPI 72 ; Word wrap off?
JNZ AFIND5 ; No, so skip typing guide
CALL DILPRT
DB CR,LF,' >---------1---------2---------3'
DB '---------4---------5---------6---------7<end',CR,LF,0
;
AFIND5: CALL DILPRT
DB CR,LF,0
XRA A
STA ANYET ; Reset the flag for no information yet
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
LXI H,HLINE2 ; Add blank line for MFMSG
CALL DSTOR1
ENDIF
;
IF MSGDSC OR DESCRIB
MVI C,'0'
EXPLN: INR C ; Begin line count at '1'
MOV A,C
CPI '7'+1 ; Reached limit?
JNC EXPL1 ; Yes, so finish up
CALL TYPE
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
MVI A,' ' ; Stuff spaces for FOR format
CALL OUTCHR
CALL OUTCHR
CALL OUTCHR
ENDIF
;
IF MSGDSC OR DESCRIB
CALL DILPRT
DB ': ',0
CALL DESC ; Get a line of information
CALL DSTOR
JMP EXPLN
;
EXPL1: LXI H,HLINE3 ; All done, add CR/LF
;
EXPL1A: MOV A,M ; Get next character
ORA A ; Finished?
CALL OUTCHR ; Transfer to buffer regardless
INX H ; Bump to next character
JZ EXPL1B ; Yes, all done, else...
JMP EXPL1A ; Loop
;
EXPL1B: CALL DILPRT
DB CR,LF
DB ' Please verify your description:'
DB CR,LF,LF,0
LHLD BUFADR ; Get starting address of description
;
EXPL1C: MOV A,M ; Put character in A
ORA A ; Finished?
JZ EXPL1D ; Yes, else...
CALL TYPE ; Show it
INX H ; Bump to next character
JMP EXPL1C ; Loop until done
;
EXPL1D: LHLD OUTPTR
DCX H
SHLD OUTPTR
;
EXPL2: CALL DILPRT
DB 'Is this correct? (Y/N) ',0
;
EXPL2A: CALL INPUT
ANI 5FH ; Change to upper case
CPI 'Y' ; Entry ok?
JZ EXPL4 ; Yes, so check for more and exit
CPI 'N' ; No?
JNZ EXPL2A ; Must be Y or N for fumble fingers...
CALL TYPE ; Display answer
;
EXPL3: LHLD BCHPTR ; Else restart at beginning of text
SHLD OUTPTR ; Start over at this address
JMP ASK3 ; Go do this one again
;
; See if any more batch files need descriptions
;
EXPL4: CALL TYPE ; Display answer
LXI H,FCB ; 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 (CALL BCHDCR)
ENDIF
;
; Now open the file and put this at the beginning
;
EXPL5: IF MSGDSC OR DESCRIB
LDA 4 ; Get current drive/user
STA DRUSER ; Store
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
LDA XPRUSR
MOV E,A ; Set user to UPLOADS area - private
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
MVI E,USER ; Set user to FOR file user area
ENDIF
;
IF MSGDSC OR DESCRIB
CALL USRSET ; Set according to E
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
LDA XPRDRV ; Set drive to UPLOADS drive - private
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
MVI A,DRIVE ; Set drive to FOR file drive
ENDIF
;
IF MSGDSC OR DESCRIB
SUI 41H
MOV E,A
CALL DRVSET
;
; Open source file
;
CALL DILPRT
DB CR,LF,0
LXI D,FILE ; Open FOR text file
CALL OPENIT
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
INR A ; Check for no open
JNZ OFILE ; File exists, exit
ENDIF
;
IF MSGDSC OR DESCRIB
MVI C,MAKE ; None exists, make a new file
LXI D,FILE
CALL BDOS
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,BSIZE ; Get Buffer allocated size
SHLD OUTSIZ ; Set for comparison
CALL DELDES ; Delete any matching file
MVI C,MAKE ; Now make a new file that name
LXI D,DEST
CALL BDOS
INR A
JZ NOROOM ; Cannot open file, no directory room
CALL DILPRT
DB CR,LF,'Saving your description, one moment...',0
;
; Read sector from source file
;
READLP: CALL RSTDMA ; Reset to default DMA
MVI C,READ
LXI D,FILE ; Read from FOR text file
CALL BDOS
ORA A ; Read ok?
JNZ RERROR
LXI H,TBUF ; Read buffer address
;
; Write sector to output file (with buffering)
;
WRDLOP: MOV A,M ; Get byte from read buffer
ANI 7FH ; Strip parity bit
CPI 7FH ; Del (rubout)?
JZ NEXT ; Yes, ignore it
CPI EOF ; 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,' ' ; Delete the character
JMP BCKSP3
;
BCKSP1: DCR B ; Show one less column used
DCX H ; Decrease buffer location
MVI A,' '
MOV M,A ; Clear memory at this point
CALL TYPE ; Backspace the "CRT"
;
BCKSP2: MVI A,BS ; Reset the "CRT" again
;
BCKSP3: JMP TYPE ; Write to the "CRT", done
;
; Asks for line of information
;
DESC: MVI B,0
LXI H,OLINE
;
DESC1: CALL INPUT ; Get keyboard character
CPI CR
JZ DESC4
CPI TAB
JZ DESC6
CPI BS
JNZ DESC2
CALL BCKSP
JMP DESC1 ; Get the next character
;
DESC2: CPI ' '
JC DESC1 ; If non-printing character, ignore
JZ DESC3 ; A space, so set skip next, else...
STA ANYET ; Show a character has been sent now
;
DESC3: MOV M,A
CALL TYPE ; Display the character
INX H
INR B
CPI ' '
JZ DESC3B
;
DESC3A: MOV A,B
CPI 71 ; 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
;
DESC3B: LDA XWRAP
CMP B ; Time for next line?
JC DESC5 ; Yes, else
JMP DESC3A
;
DESC4: LDA ANYET ; Any text typed on first line yet?
ORA A
JNZ DESC5 ; If yes, exit
POP H
JMP EXPL3 ; Ask again for a description
;
DESC5: MVI M,CR
MOV A,M
CALL TYPE
INX H ; Ready for next character
MVI M,LF
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
;
DESC6: MOV A,B
ADI 8
CPI 71 ; Would this be past the limit?
JC DESC7 ; No, so do tab function
JMP DESC5 ; ...else, start a new line
;
DESC7: MVI M,' '
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: POP D ; Get message address
CALL PRTSET ; Print message
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 LF ; Done yet?
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
RZ ; Exit if LF, done
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
JZ DKIND1 ; Exit if a LF, done
ENDIF
;
IF MSGDSC OR DESCRIB
INX D ; Next position in the string
INX H ; Next position in the buffer
JMP DKIND ; Keep going until a LF
;
DKIND1: LDA KIND ; Get the kind of file back
RET ; Finished
;
DSTOR: LXI H,OLINE
;
DSTOR1: MOV A,M
CALL OUTCHR
CPI LF
RZ
INX H
JMP DSTOR1
;
DSTOR2: MOV A,M ; Get next character
ORA A ; Finished?
RZ ; Yes, else continue
CALL OUTCHR ; Transfer to buffer
INX H ; Bump to next character
JMP DSTOR2 ; Loop until done
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
DSTOR3: MVI B,2 ; Set counter
INX H ; Skip first character (MSPEED)
INX H ; And second character (Special)
;
DSTR3: MOV A,M ; Get next character
ORA A ; Finished?
RZ ; Yes, else continue
CPI ';' ; Change to space
JNZ DSTR3A ; ...else no change
DCR B ; Stop after last name
RZ ; Return after last name stored
MVI A,' '
;
DSTR3A: CALL OUTCHR ; Transfer to buffer
INX H ; Bump to next character
JMP DSTR3 ; Loop until done
ENDIF
;
; Disk is full, save original file, erase others.
;
IF MSGDSC OR DESCRIB
FULL: CALL DELDES ; Delete new file, restore old
CALL DEXIT
DB CR,LF,'++ Disk full - saving original file ++','$'
ENDIF
;
; Get a character, if none ready wait up to DESWAIT minutes, then exit
; program.
;
INPUT: PUSH H ; Save current values
PUSH D
PUSH B
LDA DESWAIT
ADD A
MOV L,A ; Save it
;
INPUT1: LXI D,300 ; Approx 30 sec loop
;
INPUT2: LXI B,MHZ*77 ; Gives about 77 ms
;
INPUT3: PUSH H
PUSH D ; Save the outer delay count
PUSH B ; Save the inner delay count
MVI C,DIRCON ; Get console status
MVI E,0FFH
CALL BDOS
ANI 7FH
POP B ; Restore the inner delay count
POP D ; Restore the outer delay count
POP H
ORA A ; Have a character yet?
JNZ INPUT4 ; If yes, exit and get it
DCX B
MOV A,C ; See if inner loop is finished
ORA B
JNZ INPUT3 ; If not loop again
DCX D
MOV A,E
ORA D
JNZ INPUT2 ; If not reset inner loop and go again
PUSH H
CALL SENBEL ; 30 secs passed - no input
POP H
DCR L ; Countdown DESWAIT period
JNZ INPUT1 ; Start next 30 sec timer
;
IF MSGDSC OR DESCRIB
MVI A,CR
CALL OUTCHR
MVI A,LF
CALL OUTCHR
LXI SP,STACK ; Restore the stack
CALL EXPL5 ; Finish appending previous information
ENDIF
;
JMP EXIT ; File is closed, return to CP/M
;
INPUT4: POP B
POP D
POP H
RET ; Got a character, return with it
;
; Stores the Filename/extent in the buffer temporarily
;
IF MSGDSC OR DESCRIB
LOPFCB: LDAX D ; Get FCB FILENAME/EXT character
CPI ' '+1 ; 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 CR,LF,'++ No DIR space ++','$'
;
; Output error - cannot close destination file
;
OERROR: CALL DEXIT
DB CR,LF,'++ Can''t close output file ++','$'
;
; 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 DMASET ; Set DMA to buffer
MVI C,WRITE
LXI D,DEST
CALL BDOS
ORA A
JNZ FULL ; Exit with error, if disk is full now
LXI D,RLEN
LHLD OUTPTR
DAD D
SHLD OUTPTR
JMP OUT1
;
OUT2: CALL RSTDMA ; Reset to default DMA
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
CALL DELDES ; Delete new, restore old file
CALL DEXIT
DB '++ Source file read error ++','$'
ENDIF ; MSGDSC OR DESCRIB
;
; Reset the Drive/User to original
;
RESET: IF MSGDSC AND (NOT DESCRIB)
LDA DSCFLG
ORA A ; Upload description entered?
JZ RESET1 ; No, so skip next 7 lines, else...
MVI C,CURDRV ; Get current drive of 'UPLOADS'
CALL BDOS
STA DSKSAV ; Save for MFMSG
MVI E,0FFH
CALL USRSET ; Get 'UPLOADS' area
STA USRSAV ; Save for MFMSG
RESET1: ENDIF
;
IF MSGDSC OR DESCRIB
LDA DRUSER ; Get original drive/user area back
RAR
RAR
RAR
RAR
ANI 0FH ; Just look at the user area
MOV E,A
CALL USRSET ; Restore original user area
LDA DRUSER ; Get the original drive/user back
ANI 0FH ; Just look at the drive for now
MOV E,A
CALL DRVSET ; Restore original drive
CALL DILPRT ; Print CRLF before quitting
DB CR,LF,0
RET ; To: CALL ASK
;
; Shows the Filename/extent
;
SHONM: CALL DILPRT
DB CR,LF,LF,0
LXI H,FCB+1
MVI B,8 ; Maximum size of file name
CALL SHONM1
MOV A,M ; Get the next character
CPI ' ' ; Any file extent?
RZ ; If not, finished
MVI A,'.'
CALL TYPE
MVI B,3 ; Maximum size of file extent
;
SHONM1: MOV A,M ; Get FCB FILENAME/EXT character
CPI ' '+1 ; Skip any blanks
JC $+6
CALL TYPE ; Display on CRT
INX H ; Next FCB position
DCR B ; One less to go
JNZ SHONM1 ; If not done, get next one
RET
;
; Transfer is done - close destination file
;
TDONE: LHLD OUTPTR
MOV A,L
ANI RLEN-1
JNZ TDONE1
SHLD OUTSIZ
;
TDONE1: MVI A,EOF ; Fill remainder of record with ^Z's
PUSH PSW
CALL OUTCHR
POP PSW
JNZ TDONE
LXI D,FILE
CALL CLOSEF ; Close FOR file
LXI D,DEST
CALL CLOSEF ; Close FOR.$$$ file
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
LXI D,FILE
CALL DELFIL ; Delete original FOR file
MVI C,RENAME
LXI D,DEST ; Rename FOR.$$$ to FOR text file
CALL BDOS
JMP RESET ; Reset the drive/user, finished
ENDIF ; MSGDSC OR DESCRIB
;
; Send a bell just to the modem
;
SENBEL: CALL MDOUTST ; Is modem ready for another character?
JZ SENBEL ; If not, wait
MVI A,7
JMP MDOUTP ; Send to the modem only
;
; Send character in 'A' register to console
;
TYPE: PUSH B
PUSH D
PUSH H
PUSH PSW
MVI C,WRCON ; Write to console
MOV E,A ; Character to 'E' for CP/M
CALL BDOS
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 MDOUTST ; Is transmit ready
JZ SEND2 ; No, check carrier
POP PSW ; Modem is ready
JMP MDOUTP ; 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 MDCARCK ; 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
; (^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 B,1 ; Timeout delay
STA CONONL ; Show future diplays to local CRT only
CALL RECV ; Wait up to 1 second for character
JC WAITN1 ; No character this time
CPI CRC ; 'CRC' request?
JZ WAITK
CPI KSND ; Requesting 1k?
JZ SETK ; Exit if yes, otherwise set CRC
CPI NAK ; 'NAK' for checksum?
JZ CHECKY ; Yes, so check for Ymodem batch request
CPI CANCEL ; Cancel (^X)?
JZ ABORT ; Yes, abort
;
WAITN1: DCR E ; Finished yet?
JZ ABORT ; Yes, abort
JMP WAITNAK ; No, loop
;
WAITK: 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 7FH
CPI 7BH
JZ WAITK ; Disregard noisy lines
CPI KSND ; 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 'CRC requested',CR,LF,0
RET
;
; Turn on the flag for 1k blocks and insure in CRC mode
;
SETK: LDA MSPEED
CPI 5 ; 1k request for 1200 bps or more
JC SETC1 ; Don't allow 1k if less than 1200 bps
SETK1: STA KFLG ; Set the flag for 1k blocks
STA CRCFLG ; Insures in 'CRC' mode
CALL ILPRTL
DB 'Ymodem requested',CR,LF,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 'Checksum requested',CR,LF,0
RET ; From WAITNAK
;
SETNAK1:CALL ILPRTL
DB CR,LF,'Checksum not used in batch mode',CR,LF,0
JMP WAITNAK ; Ignore checksum request
;
CHECKY: LDA YMODEM ; Get Ymodem batch status
ORA A ; Requested?
JZ SETNAK ; No, put checksum into effect
MVI A,1
JMP SETK1 ; Yes, set CRC and 1k flag
;
; 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,FCB+1
CALL MOVENAM ; Move name to FCB
XRA A
STA FCBRNO ; Zero record number
STA FCBEXT ; Zero extent
LDA OPTSAV
CPI 'A' ; .ARK/.ARC?
JZ MOVEFA
CPI 'L' ; .LBR?
RNZ ; If neither, finished
;
; Handles library entries, first checks for proper .LBR extent. If no
; extent was included, it adds one itself.
;
SHLD SAVEHL
LXI H,FCBTYP ; 1st extent character
MOV A,M
CPI ' '
JZ NOLEXT ; No extent, make one
CPI 'L' ; Check 1st character in extent
JNZ LBRERR
INX H
MOV A,M
CPI 'B' ; Check 2nd character in extent
JNZ LBRERR
INX H
MOV A,M
CPI 'R' ; Check 3rd character in extent
JNZ LBRERR
JMP MOVEF1
;
; Handles .ARK/.ARChive entries - first checks for proper type. If none
; specified, .ARK is forced.
;
MOVEFA: SHLD SAVEHL
LXI H,FCBTYP ; 1st extent character
MOV A,M
CPI ' '
JZ NOAEXT ; No extent, make one
CPI 'A' ; Check 1st character in extent
JNZ LBRERR
INX H
MOV A,M
CPI 'R' ; Check 2nd character in extent
JNZ LBRERR
INX H
MOV A,M
CPI 'K' ; Check 3rd character in extent
;
IF ARCEXT
JZ MOVEF1 ; Was .ARK
CPI 'C' ; May be .ARC?
ENDIF
;
JNZ LBRERR ; Neither, abort
;
; 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 ' '+1
RNC
INX H
JMP CHKMSP
;
; Gets the count of characters remaining on the command line
;
GETB: MOV A,L
SUI TBUF+2 ; Start location of 1st command
MOV B,A ; Store for now
LDA TBUF ; Find length of command line
SUB B ; Subtract those already used
MOV B,A ; Now have number of bytes remaining
RET
;
LBRERR: CALL ERXIT
DB CR,LF
DB '++ No .LBR/.ARK/.ARC file with that name ++','$'
;
MEMERR: CALL ILPRT
DB CR,LF,LF
DB '++ Must request an internal file member ++'
DB CR,LF,0
JMP OPTERR
;
; Add .LBR extent to the library file name
;
NOLEXT: LXI H,FCB+9 ; Location of extent
MVI M,'L'
INX H
MVI M,'B'
INX H
MVI M,'R'
JMP MOVEF1 ; Now get the library member name
;
; Add .ARK extent to the file name
;
NOAEXT: LXI H,FCB+9 ; Location of extent
MVI M,'A'
INX H
MVI M,'R'
INX H
MVI M,'K'
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 ' '+1 ; Name ends with space or return
JC FILLSP ; Fill with spaces if needed
CPI '.'
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,' ' ; 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
ORA A ; Bypass NUBYE output to modem?
JNZ CTYPEL ; Yes, go directly to CRT, then
MVI C,WRCON ; BDOS console output, to CRT and modem,
CALL BDOS ; since NUBYE intercepts the char.
POP H ; Restore all registers
POP D
POP B
RET
;
CTYPEL: MOV C,E ; BIOS needs it in 'C'
CALL CONOUT ; 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 ; Switch on remote display
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
;
IF RESUSR
DENIED: CALL ERXIT
DB '++ Restricted Function ++','$'
ENDIF
;
; Inline print of message - terminates with '$'
;
IF CLRSCR
PRINTL: 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?
RZ ; If '$' is end of message
CALL CTYPE ; Else print character on console
JMP PRINTL ; And repeat until abort/end
ENDIF
;
; Print error message, then exit NUKMD
;
ERXIT: CALL ILPRT
DB CR,LF,0
;
ERXIT1: 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 EXITXL ; If '$' is end of message
CALL CTYPE ; Else print character on console
JMP ERXIT1 ; And repeat until abort/end
;
EXITXL: CALL ILPRT
DB CR,LF,0
;
ERXITX: POP H ; Restore stack
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
;
; Pause for user input, after displaying text
;
MORE: CALL ILPRT
DB CR,LF,LF,'Hit any key to continue...',0
MORE1: MVI C,6 ; Check keyboard status
MVI E,0FFH
CALL BDOS
ORA A ; Anything?
JZ MORE1 ; No, continue to wait
RET
;
; 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 XPRUSR ; Yes, set to private area
JNZ RECARE
LDA XUSR ; Ok now set the user area
;
RECARE: MOV E,A ; Stuff it in E
MVI C,SETUSR ; Tell BDOS what we want to do
JMP BDOS ; Now do it
;
RECDRV: LDA PRVTFL
ORA A
LDA XPRDRV ; Get private upload drive
JNZ RECDR1
LDA XDRV ; Or forced upload drive
;
RECDR1: SUI 'A' ; Adjust it
;
RECDRX: MOV E,A ; Stuff it in E
MVI C,SELDSK ; Tell BDOS
JMP BDOS ; 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 flag set if ok
MVI A,0FFH ; 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 10H
MOV H,A
MOV A,L
XRI 21H
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: IF LOGCAL OR MSGFIL OR MSGDSC
MVI C,CURDRV ; Get current drive
CALL BDOS ; (where down/upload occurred)
STA DSKSAV
MVI E,0FFH ; (where down/upload occurred)
CALL USRSET ; Get current user area
STA USRSAV ; Save it
ENDIF
;
IF (NOT LOGCAL) AND (MSGFIL OR MSGDSC)
RET
ENDIF
;
IF LOGCAL
XRA A
STA FCBCALLER+12
STA FCBCALLER+32
MVI A,LASTDRV-'A'
STA DEFAULT$DISK
MVI A,LASTUSR
STA DEFAULT$USER
LXI D,FCBCALLER
CALL OPENF ; Open LASTCALR file
JNZ LOGC1
CALL ILPRTL
DB '++ No LASTCALR??? file found ++',0
RET ; Show error local and transmit EOT
;
LOGC1: MVI C,SETRRD ; Get random record #
LXI D,FCBCALLER ; (for first record in file)
CALL BDOS
LXI D,DBUF
CALL DMASET ; Set DMA to DBUF
LXI D,FCBCALLER ; Read first (and only) record
CALL RRANDM ; Read it
LXI H,DBUF ; Set pointer to beginning of record
ENDIF ; LOGCAL
;
IF LOGCAL AND CLOCK
LXI D,0 ; Zero DE
MVI A,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
ENDIF
;
IF LOGCAL
SHLD CALLERPTR
LXI D,LOGBUF
CALL DMASET ; Set DMA to LOGBUF
XRA A
STA FCBLOG+12
STA FCBLOG+32
MVI A,LOGDRV-'A'
STA DEFAULT$DISK
MVI A,LOGUSR
STA DEFAULT$USER
LXI D,FCBLOG
CALL OPENF ; Open log file
JNZ LOGC4 ; If file exists, skip create
LXI D,FCBLOG
MVI C,MAKE ; Create a new file if needed
CALL BDOS
INR A
JNZ LOGC2 ; No error, continue
CALL ILPRTL ; File create error
DB '++ No DIR space: LOG ++',0
RET ; Go back and send EOT
;
LOGC2: MVI C,SETRRD ; Set random record #
LXI D,FCBLOG ; (for first record in file)
CALL BDOS
;
LOGC3: MVI A,EOF
STA LOGBUF
JMP LOGC4B
;
LOGC4: LXI D,LOGBUF
CALL DMASET ; Set DMA to LOGBUF
MVI C,FILSIZ ; Get file length
LXI D,FCBLOG
CALL BDOS
LHLD FCBLOG+33 ; Back up to last record
MOV A,L
ORA H
JZ LOGC3 ; Unless zero length file
DCX H
SHLD FCBLOG+33
LXI D,FCBLOG
CALL RRANDM ; Read it
;
LOGC4B: CALL RSTLP ; Initialize LOGPTR and LOGCNT
;
LOGC6: CALL GETLOG ; Get characters out of last record
CPI EOF
JNZ LOGC6 ; Until EOF
LDA LOGCNT ; Then backup one character
DCR A
STA LOGCNT
LHLD LOGPTR
DCX H
SHLD LOGPTR
ENDIF ; LOGCAL
;
IF LOGCAL AND RESUSR AND PUPOPT
LDA PUPFLG
ORA A ; Privileged upload option request?
JZ LOGC7 ; No
MVI A,'P' ; ...else,
JMP LOGC8 ; Show as private upload for log file
ENDIF
;
IF LOGCAL
LOGC7: LDA LOGOPT ; Get option back and put in file
CPI 'A'
JNZ LOGC8
MVI A,'L'
;
LOGC8: CALL PUTLOG
LDA MSPEED ; Get speed factor
ADI 30H
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 FCB
ORA A
JNZ WDRV
LDA DSKSAV
INR A
;
WDRV: ADI 'A'-1
CALL PUTLOG
LDA USRSAV
CALL PNDEC
MVI A,'>' ; Make it look like a prompt
CALL PUTLOG
LDA LBRARC
ORA A ; Member extraction?
JZ WDRV1 ; No, won't be member name
LXI H,MEMFCB ; Name of file in library
MVI B,11
CALL PUTSTR
CALL PUTSP ; ' '
;
WDRV1: LXI H,FCB+1 ; Now the name of the file
MVI B,11
CALL PUTSTR
LDA LBRARC
ORA A ; Member extraction?
JZ WDRV2 ; No, won't be member name
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
CALL DIVREC ; Divide record count by 8
;
EXKB2: CALL PNDEC3 ; Print to log file (right just xxxk)
LXI H,LOGK ; 'k '
MVI B,2
CALL PUTSTR
ENDIF ; LOGCAL
;
IF LOGCAL AND CLOCK
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,'/' ; '/'
CALL PUTLOG
POP PSW ; Get DD/YY
PUSH PSW ; Save YY
CALL PNDEC ; Print DD
MVI A,'/' ; '/'
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,':' ; With ':'
CALL PUTLOG ; Between HH:MM
LDA MNSAV ; Get min
CALL PNDEC ; And print min
CALL PUTSP ; Print a space
ENDIF ; LOGCAL AND CLOCK
;
IF LOGCAL
CLOOP: CALL GETCALLER ; And the caller
CPI EOF
JZ QUIT
CPI CR ; Do not print 2nd line of 'LASTCALR'
JNZ CLOP1
;
CEND: CALL PUTLOG
MVI A,LF
CALL PUTLOG ; And add a LF
JMP QUIT
ENDIF
;
CLOP1: IF LOGCAL AND CLOCK
CPI ' ' ; Space?
JNZ CLOP1A ; No, check for comma
MVI A,',' ; Convert space to comma to check field
ENDIF
;
IF LOGCAL
CLOP1A: CPI ',' ; Comma?
JNZ CLOP2
ENDIF
;
IF LOGCAL AND CLOCK
LDA COMMA
CPI 1 ; Is this the second comma or space?
JNZ CLOP1B ; No, bump the counter
MVI A,CR
JMP CEND ; Yes, stop taking data from LASTCALR
;
CLOP1B: INR A ; Bump it one
STA COMMA
ENDIF
;
IF LOGCAL
MVI A,' ' ; Instead send a ' '
;
CLOP2: CALL PUTLOG
JMP CLOOP
;
QUIT: MVI A,EOF ; Put in EOF
CALL PUTLOG
LDA LOGCNT ; Check count of chars in buffer
CPI 1
JNZ QUIT ; Fill last buffer & write it
LXI D,FCBCALLER ; Close lastcaller file
CALL CLOSEF
INR A
JZ QUIT1
LHLD FCBLOG+33 ; Move pointer back to show
DCX H ; Actual file size
SHLD FCBLOG+33
LXI D,FCBLOG ; Close log file
CALL CLOSEF
INR A
RNZ ; If OK, return
;
QUIT1: CALL ILPRTL ; If error, oops
DB '++ Can''t 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,EOF
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
ENDIF ; LOGCAL
;
IF CPM3 AND LOGCAL
PUSH D ; Save FCB address
CALL RSTDMA ; Reset to default DMA
POP D ; Get back pointer to FCB
PUSH D ; Save FCB pointer again
MVI C,SRCHF ; Search for first match
CALL BDOS
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,TBUF ; 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
ENDIF
;
IF LOGCAL
CALL OPENIT ; Open file
CPI 0FFH ; Not present?
RET ; Return to caller
ENDIF
;
; Write character to log file
;
PUTLOG: IF LOGCAL
LHLD LOGPTR ; Get pointer
ANI 7FH ; Mask off any high bits
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
LXI D,FCBLOG ; Else, write this sector
MVI C,WRDM
CALL BDOS
ORA A
JZ ADVRCP ; If ok, cont.
CALL ILPRTL
DB '++ Disk full - can''t 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,' '
CALL PUTLOG
POP H
POP PSW
RET
ENDIF ; LOGCAL
;
; Store decimal format number into log file and/or upload descriptions file.
;
PNDEC: IF LOGCAL
CPI 10 ; Two column decimal format routine
JC ONE ; One or two digits to area number?
JMP TWO
;
ONE: PUSH PSW
MVI A,'0'
CALL PUTLOG
POP PSW
ENDIF
;
IF LOGCAL OR MSGDSC
TWO: MVI H,0
MOV L,A
;
DECOT: PUSH B
PUSH D
PUSH H
LXI B,-10
LXI D,-1
;
DECOT2: DAD B
INX D
JC DECOT2
LXI B,10
DAD B
XCHG
MOV A,H
ORA L
CNZ DECOT
ENDIF
;
IF MSGDSC
LDA DSCFLG
ORA A ; Description entry?
ENDIF
;
IF LOGCAL OR MSGDSC
MOV A,E
ENDIF
;
IF MSGDSC
JZ DECOT3 ; No, so skip next 3 lines
ADI '0'
CALL OUTCHR ; ...else store it
JMP DECOT4 ; And skip next 2 lines
ENDIF
;
IF LOGCAL OR MSGDSC
DECOT3: ADI '0'
CALL PUTLOG
;
DECOT4: POP H
POP D
POP B
RET
ENDIF
;
; Put string to log file
;
IF LOGCAL
PUTSTR: MOV A,M
PUSH H
PUSH B
CALL PUTLOG
POP B
POP H
INX H
DCR B
JNZ PUTSTR
RET
ENDIF ; LOGCAL
;
; end of LOGCAL routine
; ---------------------
;
; Start of TIMEON routine
;
; Calculate time on system and inform user. NUBYE will handle logoff if
; MXTIME is exceeded.
;
TIME: IF TIMEON
MVI E,0FFH
MVI C,81 ; Ask for MXTIME
CALL BDOS
STA TLIMIT ; And save it
MVI E,0
MVI C,81 ; Stop NUBYE from checking time just now
CALL BDOS
MVI C,79 ; Ask for TON and RTC address
CALL BDOS
STA TON ; Save TON
ENDIF
;
IF CLOCK AND TIMEON AND DTOS
MVI C,83
CALL BDOS ; Have NUBYE print time-on system
ENDIF
;
IF TIMEON
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
ENDIF
;
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,'0'-1
;
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 '1' ; 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 '0' ; 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)
;
IF TIMEON OR CLOCK
GETTIME:MVI C,79 ; Ask for TON and RTC address
CALL BDOS
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
ENDIF
;
IF LOGCAL AND CLOCK
GETDATE:MVI C,79 ; Get RTC address
CALL BDOS
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 MMSAV ; Get MM in A
ENDIF
;
IF EDATE
MOV D,B
MOV B,A
MOV A,D ; Return with dd/mm/yy vice mm/dd/yy
ENDIF
;
IF LOGCAL AND CLOCK
RET ; And return
ENDIF
;
; Convert BCD value in A to binary in A
;
IF CLOCK OR TIMEON
BCDBIN: PUSH PSW ; Save A
ANI 0F0H ; 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 0FH ; Mask low nibble
ADD B ; +B gives binary value of BCD digit A
RET ; Return
ENDIF
;
; -----
;
; The following allocations are used by the LOGCALL routines
;
IF LOGCAL
PGSIZE: DB 0,0,0 ; Program length in mins and secs
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???' ; Last caller file FCB
DB 0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0
;
CALLERPTR: DW LOGBUF
ENDIF
;
FCBLOG: IF LOGCAL AND (NOT MBBS)
DB 0,'NUKMD LOG' ; Log file FCB
ENDIF
;
IF LOGCAL AND MBBS
DB 0,'LOG SYS' ; Log file FCB
ENDIF
;
IF LOGCAL
DB 0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0 ; Rest of FCB
LOGPTR: DW DBUF
LOGCNT: DB 0
ENDIF
;
IF LOGCAL OR RESUSR OR MSGFIL OR MSGDSC
DSKSAV: DB 0 ; Up/download disk saved here
USRSAV: DB 0 ; Up/download user saved here
ENDIF
;
IF LOGCAL
LOGK: DB 'k '
ENDIF
;
IF LOGCAL AND (TIMEON OR CLOCK)
YYSAV: DB 0
MMSAV: DB 0
DDSAV: DB 0
MNSAV: DB 0
ENDIF
;
; -----
;
; 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 ; Batch filename from command line
DB 0,0,0,0,0
DB 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
DB 0,0,0,0,0,0
MFNAM6: DB 0,0,0,0,0,0
DB 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
;
; Temporary storage area
;
FILE: IF MSGDSC AND (NOT DESCRIB)
DB 0,'UPLOADS '
ENDIF
;
IF DESCRIB AND (NOT MSGDSC)
DB 0,'FOR '
ENDIF
;
DB 0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0
;
DEST: DB 0,' $$$'
DB 0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0
;
DUSAVE: DB 0,0,0,0 ; Buffer for drive/user
MEMFCB: DB ' ' ; Library name (16 bytes required)
;
AFBYTE: DB 0 ; Access flags byte storage
ANYET: DB 0 ; Any description typed yet?
ARCEOF: DB 0 ; EOF flag for .ARK/.ARC
ARCFST: DB 0 ; First record flag
ARCLST: DB 0 ; Last record byte count -1
ARCVER: DB 0 ; Flag for compression type (1-8)
BLKSHF: DB 0
CHKASK: DB 0 ; First time wrap mode prompt
CHKEOT: DB 0 ; Prevents locking up after an EOT
CHOICE: DB 0 ; User choice flag
CHRCNT: DB 0,0,0 ; 24-bit counter
CRCFLG: DB 0 ; For sending checksum rather than CRC
CONONL: DB 0 ; CTYPE console-only flag
COMMA: DB 0 ; Field counter for logcal
DRUSER: DB 0 ; Original drive/user, for return
DSCFLG: DB 0 ; Special description file flag
DUD: DB 0 ; Specified disk
DUU: DB 0 ; Specified user
EOFLG: DB 0 ; 'EOF' flag (1=yes)
EOTFLG: DB 0 ; EOT (End Of Transmission) status flag
ERRCT: DB 0 ; Error count
FRSTIM: DB 0 ; Turned on after first 'SOH' received
GOTONE: DB 0 ; Prevents asking for a description
INBTCH: DB 0 ; For batch uploads and MSGDSC
KIND: DB 0 ; Asks what kind of file this is
KFLG: DB 0 ; For sending 1k blocks
LBRARC: DB 0 ; .LBR/.ARK/.ARK request flag
MSGFLG: DB 0 ; Special flag for message file uploads
NOISY: DB 0 ; 1 indicates noisy line switch to Xmodem
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
PUPFLG: DB 0 ; Special flag for privileged option uploads
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
SPLFL1: DB 0 ; Special flag for alternate section downloads
SYSABT: DB 0 ; Local sysop xfr abort with ^X
YMODEM: DB 0 ; Special flag for Ymodem batch xfr (CRC-1k)
;
ACCERR: DW 0 ; No 'ACK' error count for 1k ratio
ARCCNT: DW 0 ; .ARK/.ARC record count
ARCPTR: DW 0 ; Record pointer
ARCREC: DW 0 ; Record number
BLKMAX: DW 0
CRCVAL: DW 0 ; Current CRC value
DIRSIZ: DW 0 ; Directory size
HDRADR: DW 0 ; Header address
INDEX: DW 0 ; Index into directory
MINUTE: DW 0 ; Transfer time in mins for MAXTIM
OUTADR: DW DBUF
OUTPTR: DW 0
OUTSIZ: DW BSIZE
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: IF DESCRIB AND (NOT MSGDSC)
DB '----',CR,LF
ENDIF
;
IF MSGDSC AND (NOT DESCRIB)
DB 'MSG#: ????',CR,LF
DB 'FROM: ',0
HLINE1: DB ' (PRIVATE)',CR,LF
DB ' TO: SYSOP',CR,LF
DB ' RE: '
MBDSH: DB 'NEW UPLOAD: ',0
HLINE2: DB ' ',CR,LF
ENDIF
;
IF MSGDSC OR DESCRIB
HLINE3: DB CR,LF,0 ; End of description
ENDIF
;
OLINE: DS 80 ; Temporary storage buffer
DS 60 ; Area for stack
;
; BDOS equates
;
WRCON EQU 2 ; Output to console
DIRCON EQU 6 ; Direct console output
PRINT EQU 9 ; Print string function
SELDSK EQU 14 ; Select drive
OPEN EQU 15 ; 0FFH = not found
CLOSE EQU 16 ; " "
SRCHF EQU 17 ; " "
SRCHN EQU 18 ; " "
DELET EQU 19 ; Delete file
READ EQU 20 ; 0=OK, 1=EOF
WRITE EQU 21 ; 0=OK, 1=ERR, 2=?, 0FFH=no dir. space
MAKE EQU 22 ; 0FFH=bad
RENAME EQU 23 ; Rename a file
CURDRV EQU 25 ; Get current drive
STDMA EQU 26 ; Set DMA
SETUSR EQU 32 ; Set user area to receive file
RRDM EQU 33 ; Read random
WRDM EQU 34 ; Write random
FILSIZ EQU 35 ; Compute file size
SETRRD EQU 36 ; Set random record
BDOS EQU 5 ; Address for BDOS jump vectors
TBUF EQU 80H ; Default DMA address
FCB EQU 5CH ; System FCB
FCB1 EQU 6CH ; Secondary FCB area
FCBEXT EQU FCB+12 ; File extent
FCBRNO EQU FCB+32 ; Record number
FCBTYP EQU FCB+9 ; File type
RANDOM EQU FCB+33 ; Random record field
;
MAIN EQU 1
VERS EQU 11
MONTH EQU 2
DAY EQU 4
YEAR EQU 87
;
BELL EQU 7 ; Bell
BS EQU 8 ; Backspace character
ACK EQU 6 ; Acknowledge
CANCEL EQU 18H ; ^X for cancel
CR EQU 0DH ; Carriage return
CRC EQU 'C' ; CRC request character
KSND EQU 'K' ; 1k block request character
EOF EQU 1AH ; End of file - ^Z
EOT EQU 4 ; End of transmission
LF EQU 0AH ; Line feed
NAK EQU 15H ; Negative acknowledge
RLEN EQU 128 ; Record length
TAB EQU 9 ; Horizontal tab
SOH EQU 1 ; Start of header
STX EQU 2 ; Start of 1k header
;
MSPEED EQU 3CH ; Location of NUBYE's modem speed indicator
DRIVMAX EQU 3DH ; Location of MAXDRIV byte
USRMAX EQU 3FH ; Location of MAXUSER byte
;
ARCMRK EQU 26 ; Header mark
HDRSIZ EQU 28 ; Header size (version 1 = HDRSIZ-4)
;
; 16k disk buffer
;
ORG ($+127)/128*128
;
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
BSIZE EQU 24*1024 ; Set for 24k for the DESCRIB/MSGDSC buffer
;
END
;
; 1) Irvin M. Hoff et al, "KMD22.ASM" (1986)