home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
modem2
/
ukm7.aqm
/
UKM7.ASM
Wrap
Assembly Source File
|
1985-02-09
|
61KB
|
3,112 lines
;
; UKM7
;This program was originally written in 1977 by Ward Christensen.
;Ward's comments were removed, Terminal File and Batch Mode were
;added in 1980 by Mark Zeiger and James Mills. The original CRC
;checks were added by Paul Hansknecht in June 1981.
;Improved terminal file facilities, menus and a general tidy
;up of a very untidy program, removal of all modem dependent
;features, many bugs and adaptation for the UK CP/M Users
;Group Library by David Back. 4 May 1983.
;Version 1.5 includes many additional facilities, including a
;printer buffer, active toggling of terminal and secondary
;options, faster CRC's etc. David Back. 21 February 1984
;
;The file exchange protocols are compatible with the MODEMX
;series of programs in the US CP/M UG Library.
;UKM7 is compatible with CP/M 1.4 and CP/M 2.2.
;
FALSE EQU 0
TRUE EQU 0FFH
ORG 100H
;********* SYSTEM DEPENDENT OVERLAY **************
;**** This is a general purpose overlay applicable to many systems ****
JMP 300H ;absolute start of program
MODCTLP EQU 0ECH ;MODEM CONTROL PORT
MODDATP EQU 0EDH ;MODEM DATA PORT FOR SEND
MODDRCV EQU 0EDH ;MODEM DATA PORT FOR RECEIVE
MSNDB EQU 1 ;MODEM SEND BIT (XMIT BUFF EMPTY)
MSNDR EQU 1 ;MODEM SEND READY
MRCVB EQU 2 ;MODEM RECEIVE BIT (DAV)
MRCVR EQU 2 ;MODEM RECEIVE READY
BAUD EQU 2 ;enter 1 for 1X, or 2 for 16X, or 3 for 64X
;It is important not to alter the addresses of labels below
OLID: DB 1 ;overlay identifier
TWIDTH: DB 70 ;max. terminal columns
FASTCLK: DB TRUE ;4 MHz or greater processor speed
BAKUPBYTE: DB TRUE ;true=make .BAK file
XPRFLG: DB TRUE ;true=menu initially off
SAVCCP: DB TRUE ;true=do not overwrite CCP
SAVEFLG: DB TRUE ;true=terminal filesave initially on
ECHOFLG: DB FALSE ;true=terminal echo initially on
INITFLG: DB FALSE ;true=modem port already initialised
ANSBAK: DB TRUE ;true=answerback on ^E
INMODCTLP: IN MODCTLP ;get port status
RET
DB 0 ;space for memory mapped I/O's
OUTCTLP: OUT MODCTLP ;control
RET
DB 0
OUTMODDATP: OUT MODDATP ;send data
RET
DB 0
ANISND: ANI MSNDB ;bit to test for send ready
RET
CPISND: CPI MSNDR ;value of send bit when ready
RET
INMODDATP: IN MODDRCV ;get data
RET
DB 0
ANIRCV: ANI MRCVB ;bit to test for receive ready
RET
CPIRCV: CPI MRCVR ;value of receive bit when ready
RET
LOGMSSG: DB 'David;Back;Shepperton Middx',CR,LF,0
ORG 150H ;do not alter this org
;The routine below should work in most systems which use an 8251 USART
;Modem port must be initialised for 8 data bits and no parity
INITMOD:LXI D,CPMS ;finish signon message
MVI C,9
CALL BDOS
LDA INMODCTLP+1
CALL HEXPRT
LXI D,DPMS
MVI C,9
CALL BDOS
LDA OUTMODDATP+1
CALL HEXPRT
LXI D,HMS
MVI C,9
CALL BDOS
LDA INITFLG
ORA A ;return if already initialsed
RNZ
MVI A,0EH
CALL OUTCTLP ;force command instruction
PUSH PSW
POP PSW
MVI A,40H
CALL OUTCTLP ;internal reset
PUSH PSW
POP PSW
MVI A,6CH OR (BAUD AND 3)
CALL OUTCTLP ;8 bits no parity
PUSH PSW
POP PSW
MVI A,37H ;modem connected
CALL OUTCTLP
CALL INMODDATP
CALL INMODDATP ;clear buffers
RET
;necessary because location of HEXO is not fixed
HEXPRT: PUSH PSW
RAR
RAR
RAR
RAR
CALL NIB
POP PSW
NIB: ANI 0FH
CPI 10
JC NU
ADI 7
NU: ADI '0'
MOV E,A
MVI C,2
CALL BDOS
RET
CPMS: DB 'Control port=0$'
DPMS: DB 'H Data port=0$'
HMS: DB 'H',CR,LF,'$'
;********** END OF OVERLAY AREA ************
DBUFSIZ EQU 2 ;S & R BUFFER SIZE IN KBYTES
;2K is the normally accepted optimum
RING EQU 8 ;print ring buffer size, K bytes
ERRLIM EQU 10 ;NUMBER OF TIMES TO RETRY
;SEND/RECEIVE ERRORS BEFORE QUIT
COMPUT EQU 'C'-40H ; ^C = Computer mode
DMENU EQU 'D'-40H ; ^D = display terminal menu
EXITCHR EQU 'E'-40H ; ^E = exit terminal mode
TRANCHR EQU 'T'-40H ; ^T = TRANSFER FILE
SAVECHR EQU 'Y'-40H ; ^Y = memory save toggle
PRNCHR EQU 'P'-40H ; ^P = printer toggle
EXTCHR EQU '^'-40H ;SEND NEXT CHAR LITERALLY
SAVON EQU 12H ;^R Terminal filesave on
SAVOFF EQU 14H ;^T Terminal filesave off
;
;PROGRAM FOLLOWING IS NOT SYSTEM DEPENDENT,
;PLEASE DO NOT INTRODUCE ANY SYSTEM DEPENDENT FEATURES BELOW
;
XOFF EQU 'S'-40H ; ^S = XOFF CHARACTER
XON EQU 'Q'-40H ; ^Q = XON CHARACTER
CAN EQU 'X'-40H ; ^X = CANCEL SEND/RECEIVE
EOFCHAR EQU 'Z'-40H ; ^Z = END OF FILE
ENQ EQU 5 ; ^E Auto ID
SOH EQU 1 ; START OF HEADER
EOT EQU 4 ; END OF TEXT
ACK EQU 6 ; ACKNOWLEDGE
NAK EQU 15H ; NOT ACKNOWLEDGE
CRC EQU 'C' ;USED TO RQST CRC INSTEAD OF CKSUM
BDNMCH EQU 75H ; BAD NAME MATCH
LF EQU 10 ; LINEFEED
CR EQU 13 ; CARRIAGE RETURN
BELL EQU 7 ; BELL CHARACTER
WRCON EQU 2
LIST EQU 5 ;printer o/p
PRINT EQU 9 ;string o/p to console
OPEN EQU 15
CLOSE EQU 16
SRCHF EQU 17
SRCHN EQU 18
ERASE EQU 19
READ EQU 20
WRITE EQU 21
MAKE EQU 22
REN EQU 23
STDMA EQU 26
FILSIZ EQU 35
BDOS EQU 5
FCB EQU 5CH
FCBEXT EQU FCB+12
FCBRNO EQU FCB+32
ORG 300H ;do not alter this org
LXI H,0
DAD SP ;GET CP/M'S STACK
SHLD STAK ;SAVE IT
LXI SP,STAK ;LOCAL STACK
CALL INITADR ;INITIALIZE BIOS ADDRESSES
CALL INITCRC ;initialise crc table
CALL ILPRT
DB 'UK MODEM7 D.R. Back Version 1.5',CR,LF,0
CALL INITMOD ;INITIALISE MODEM PORTS
LXI H,80H
LXI D,CMDBUF+1
MVI B,80H
CALL MOVE ;default buffer to cmdbuf
MVI A,TRUE
STA NFILFLG
CALL PROCOPT ;PROCESS CONTROL OPTIONS
RESTART:LDA OPTION ;GET MAIN OPTION
CPI ' ' ;NO OPTION SPEC'D?
JZ MENU
CPI 'M' ;MENU
JZ MENU2
CALL MOVEFCB ;MOVE 2ND HALF FCB TO FIRST HALF
CALL INMODDATP ;GOBBLE UP GARBAGE..
CALL INMODDATP ;..CHARACTERS ON LINE
LDA OPTION ;PROCESS MAIN OPTION
CPI 'T' ;TERMINAL MODE?
JZ DSKSAVE
CPI 'S' ;SEND A FILE?
JZ SENDFIL
CPI 'R' ;RECEIVE A FILE?
JZ RCVFIL
MENU: LXI SP,STAK ;RESTORE STACK
LXI H,RESTSN ;RESTORE SECTOR NUMBERS..
LXI D,SECNOB ;..FOR NEW FILE TRANSFER.
MVI B,SECNOE-SECNOB
CALL MOVE
LXI H,RESTROPT ;RESTORE OPTION TABLE
LXI D,OPTBL
MVI B,OPTBE-OPTBL
CALL MOVE
LDA LSTFLG
STA LSTRET ;save print toggle
MVI A,0
STA LSTFLG ;printer off
STA MFFLG1 ;RESET MFACCESS ROUTINE..
CMA ;..AND MULTI TRANS IN CASE..
STA FSTFLG ;..OF ABORT.
MENU1: LDA XPRFLG ;TEST IF MENU SHOULD BE SHOWN
ORA A
JNZ XPRT
MENU2: CALL ILPRT
DB CR,LF
DB 'SYNTAX: primaryoption[secondaryoptions] [d:][filename] [ afn]'
DB CR,LF,CR,LF
DB ' PRIMARY OPTIONS:',CR,LF
DB ' S Send binary files, afn list',CR,LF
DB ' R Receive binary files, drive:',CR,LF
DB ' T Terminal mode. Terminal filename optional',CR,LF
DB ' DEL Delete Terminal file',CR,LF
DB ' DIR Directory list, afn optional',CR,LF
DB ' CPM Exit to CP/M.',CR,LF
DB ' X Expert, toggle menus on/off',CR,LF
DB ' M Menu display',CR,LF,CR,LF
DB ' SECONDARY OPTIONS: (for primary options S and R)'
DB CR,LF
DB ' N Non batch mode, send or receive file'
DB CR,LF
DB ' Q Quiet mode, remote system Send/Receive',CR,LF
DB 'S,R,V Monitor data Sent, Received or View file',CR,LF
DB ' T Go to Terminal mode after file transfers',CR,LF,0
XPRT: CALL ILPRT
DB CR,LF,0
MVI C,25 ;CURRENT DISK FUNCTION
CALL BDOS
ADI 41H ;MAKE ASCII
CALL CTYPE
CALL ILPRT
DB ' ==>>',0
LXI D,CMDBUF ;ENTER COMMAND
CALL INBUFF
CALL CRLF
LXI D,CMDBUF+2 ;POINT TO COMMAND
CALL ILCOMP
DB 'CPM',0
JNC EXIT
CALL ILCOMP
DB 'DIR',0
JNC DIR
CALL ILCOMP
DB 'DEL',0
JNC NEWFILE
LXI D,CMDBUF
LXI H,FCB
CALL CPMLINE ;LOAD FCB
CALL PROCOPT
JMP RESTART
DIR: CALL DIRLST
JMP XPRT
EXIT: LDA NFILFLG
CPI TRUE
CNZ TFILWR ;write and close terminal file
LXI D,80H
MVI C,STDMA
CALL BDOS
LHLD STAK
SPHL
LDA SAVCCP
ORA A
JZ 0 ;WARM BOOT
RET ;to CCP
NEWFILE:LDA NFILFLG
CPI TRUE
JZ MENU1 ;IF NO FILE, DON'T ERASE
LXI D,FCB3
MVI C,ERASE
CALL BDOSRT
MVI A,TRUE ;DO NOT ALLOW TERMINAL..
STA NFILFLG ;..SAVE SINCE NO FILE..
JMP MENU1
;======================================
;TERMINAL ROUTINE ALLOWING MEMORY SAVE
DSKSAVE:LDA FCB+1 ;FIRST CHAR OF FILENAME
CPI ' ' ;FILE SPEC'D
LHLD HLSAVE
JZ TERM1
LDA NFILFLG
CPI TRUE
CNZ TFILWR ;write & close existing file
CALL TFLERAS
CALL MOVE2 ;move FCB to FCB3
LXI D,FCB3
MVI C,MAKE
CALL BDOS
INR A
JNZ TERM0
CALL ILPRT
DB 'Can''t make file',CR,LF,0
JMP TERM1
TERM0: LXI H,BOTTRAM
SHLD HLSAVE
MVI A,FALSE
STA NFILFLG
STA CTRLR ;cancel any previous ^R
TERM1: LDA LSTRET
STA LSTFLG ;restore printer toggle
LDA XPRFLG
ORA A
JNZ TERM3
TERM2: LDA NFILFLG
CPI TRUE
JZ NOTFIL
PUSH H
LXI H,FCB3
LXI D,TFILE
MOV A,M
ORA A
JNZ PUTDRV
MVI A,' '
STAX D
INX D
JMP NAME1
PUTDRV: ADI 40H
STAX D
INX D
MVI A,':'
NAME1: STAX D
INX D
INX H
MVI B,8
CALL MOVE
INX D
MVI B,3
CALL MOVE
POP H
CALL ILPRT
DB CR,LF,' ^Y Terminal file '
TFILE: DB ' toggle save on/off '':'' =on',0
NOTFIL: CALL ILPRT
DB CR,LF
DB ' ^P Printer, toggle on/off',CR,LF
DB ' ^T Transfer (Send) ASCII file without checks',CR,LF
DB ' ^X Abort transfer initiated above',CR,LF
DB ' ^C Computer mode, toggle echo on/off',CR,LF
DB ' ^^ Send following character literally',CR,LF
DB ' ^E Exit to command menu',CR,LF
DB ' ^D Display terminal menu',CR,LF,0
TERM3: LDA NFILFLG
ORA A
JNZ TERM
LDA SAVEFLG
ORA A
JZ TERM
MVI A,':' ;indicate filesave is on
CALL TYPE ;dont print or inc col count
TERM: CALL STAT ;o/p to print & check keypress
JZ TERML ;NO, CHECK LINE
CALL KEYIN ;GET CHAR FROM KBD
MOV B,A
LDA EXACFL
ORA A
MVI A,FALSE
STA EXACFL
MOV A,B
JNZ NOTOG
CPI EXITCHR ;^E?
JZ MENU ;YES, RETURN TO MENU
CPI COMPUT ;^C Computer mode with echo
JNZ NOECH
LDA ECHOFLG
CMA
STA ECHOFLG
JMP TERML
NOECH: CPI DMENU ;^D display terminal menu
JZ TERM2
CPI EXTCHR ;literal
JZ EXTFLG
CPI TRANCHR ;TEST FOR TRANSFER REQUEST (^T)
CZ TRANSFER ;SEND-A-FILE
JZ TERM3 ;LOOP
CPI PRNCHR
JNZ NOTLST
LDA LSTFLG
CMA
STA LSTFLG
JMP TERML
NOTLST: CPI SAVECHR
JNZ NOTOG
LDA NFILFLG ;DO NOT ALLOW SAVE IF..
CPI TRUE ;..THIS FLAG IS SET.
JZ TERML
LDA SAVEFLG
CMA
STA SAVEFLG
JMP TERM3
EXTFLG: MVI A,TRUE
STA EXACFL
JMP TERML
NOTOG: MOV B,A
LDA ECHOFLG
ORA A
MOV A,B
JZ TSEND
CPI LF
JZ TERML ;ignore LF
CALL OUTMODDATP
CALL CTYPE ;local echo
PUSH PSW
CALL MSAVE ;local save in terminal file
POP PSW
CPI CR
JNZ TERML
MVI A,LF
CALL CHRSND ;send to remote
CALL CTYPE ;append LF
CALL MSAVE
JMP TERML
TSEND: CALL OUTMODDATP
TERML: CALL INMODCTLP
CALL ANIRCV
CALL CPIRCV
JNZ TERM
CALL INMODDATP
ANI 7FH ;strip parity
JZ TERM ;ignore null
CPI ENQ ;auto logon
JNZ NOLOG
LDA ANSBAK
ORA A
JZ TERM
LXI D,LOGMSSG
NXCHAR: LDAX D
INX D
ORA A
JZ TERM
CALL CHRSND ;send to remote
MOV B,A
LDA ECHOFLG
ORA A
MOV A,B
JNZ CHRSAV
PUSH D
MVI D,0 ;init count
CALL INMODEM ;wait 100ms for echo
DCR D
POP D
JNZ NXCHAR
CHRSAV: CALL CTYPE
CALL MSAVE
JMP NXCHAR
NOLOG: CPI SAVON ;auto filesave on
JNZ TRYT
LDA SAVEFLG
ORA A
JNZ TERM ;save already on
MVI A,TRUE
STA SAVEFLG ;turn on save
STA CTRLR ;remember
JMP TERM3
TRYT: CPI SAVOFF ;auto save off
JNZ ONWRD
LDA CTRLR
ORA A
JZ TERM ;turn save off only
MVI A,FALSE ;if it was turned on
STA SAVEFLG ;by ^R from remote
STA CTRLR
JMP TERM
ONWRD: MOV B,A
LDA ECHOFLG
ORA A
MOV A,B
JZ TERM5
CPI LF
JZ TERM ;ignore LF
CALL OUTMODDATP ;echo to distant terminal
CPI CR
JNZ TERM5
CALL CTYPE
CALL MSAVE ;save in terminal file
MVI A,LF
CALL CHRSND ;send LF
TERM5: CALL CTYPE
CALL MSAVE ;save in terminal file
JMP TERM
CTRLR: DB FALSE
LASTB1: DB 0
LASTB2: DB 0
;=========================================
;SEND A CP/M FILE
SENDFIL:LDA BATCHFLG ;CHECK IF MULTIPLE FILE..
ORA A ;..MODE IS SET.
JZ SENDC1
MVI A,TRUE ;INDICATE BATCH SEND
STA SENDFLG
LDA FSTFLG ;IF FIRST TIME THRU..
ORA A ;..SCAN THE COMMAND LINE..
CNZ TNMBUF ;..FOR MULTIPLE NAMES.
CALL SENDFN ;SENDS FILE NAME TO RECEIVER
JNC SENDC2 ;CARRY SET MEANS NO MORE FILES.
MVI A,0 ;STOP BATCH..
STA BATCHFLG ;..MODE OPTION.
MVI A,EOT ;FINAL XFER END
CALL SEND
JMP DONE
SENDC1: LDA FCB+1
CPI ' '
JZ BLKFILE
CALL AMBGTS ;test for ambiguous filename
SENDC2: CALL CNREC ;GET NUMBER OF RECORDS
CALL OPENFIL
MVI E,80
CALL WAITNAK ;if a 'C' is received instead of NAK
SENDLP: CALL RDSECT ;then CRC mode is enabled
JC SENDEOF
CALL INCRSNO
XRA A
STA ERRCT
SENDRPT:CALL SENDHDR
CALL SENDSEC
LDA CRCFLG
ORA A
CZ SENDCRC
CNZ SENDCKS
CALL GETACK
JC SENDRPT
JMP SENDLP
SENDEOF:MVI A,EOT
CALL SEND
CALL GETACK
JC SENDEOF
JMP DONE
;===============================
;RECEIVE A FILE
RCVFIL: XRA A ;default to CRC mode
STA CRCFLG
RCV1FIL:LDA BATCHFLG ;CHECK IF MULT..
ORA A ;..FILE MODE.
JZ RCVC1
MVI A,FALSE ;FLAG WHERE TO RETURN..
STA SENDFLG ;..FOR NEXT FILE TRANS.
CALL GETFN ;GET THE FILE NAME.
JNC RCVC2 ;CARRY SET MEANS NO MORE FILES.
MVI A,0 ;STOP BATCH..
STA BATCHFLG ;..MODE OPTION.
JMP DONE
RCVC1: LDA FCB+1 ;MAKE SURE FILE IS NAMED
CPI ' '
JZ BLKFILE
JMP RCVC3
RCVC2: CALL CKCPM2
CALL CKBAKUP
RCVC3: CALL ERASFIL
CALL MAKEFIL
LDA QFLG
ORA A
JZ RCVFST
RCVC4: CALL ILPRT ;first comment
DB 'File open, ready to receive',CR,LF,0
RCVFST: LDA CRCFLG
ORA A
MVI A,NAK
JNZ RCV2FIL
MVI A,CRC ;indicate to Tx that CRC is wanted
RCV2FIL:CALL SEND ;by sending a 'C' instead of NAK
LDA QFLG
ORA A
JZ RCVLP
LDA CRCFLG
ORA A
JNZ RCVNAKM ;if in CRC mode
CALL ILPRT ;then say so
DB 'CRC in effect',cr,lf,0
JMP RCVLP
RCVNAKM:CALL ILPRT ;else say checksum mode
DB 'Checksum in effect',cr,lf,0
RCVLP: CALL RCVSECT
JC RCVEOT
CALL WRSECT ;sends CAN if error
CALL INCRSNO
CALL SENDACK
JMP RCVLP
RCVEOT: CALL WRBLOCK ;sends CAN if error
CALL SENDACK
CALL CLOSFIL
JMP DONE
;===================================
BLKFILE:CALL ILPRT ;fatal error
DB CR,LF,'No file specified',CR,LF,BELL,0
JMP MENU
;===============================
DONE: LDA BATCHFLG
ORA A
JZ DONETB
LDA QFLG
ORA A
JZ NMSTRNS
LXI H,FCB+1 ;PUT FILE NAME IN..
LXI D,FTRNMSG ;..SPACES IN MESSAGE..
MVI B,8 ;..BELOW.
CALL MOVE
INX D ;PUT FILE TYPE AFTER..
MVI B,3 ;..SKIPPING ONE SPACE..
CALL MOVE ;..BELOW.
CALL ILPRT ;final comment
DB CR,LF
FTRNMSG:DB ' transferred',CR,LF,CR,LF,0 ;13 SPACES
NMSTRNS:LDA FCB ;SAVE DRIVE NO.
STA DISKNO
LXI H,FCB ;BLANK OUT FILE CONTROL BLOCKS
CALL INITFCBS
LDA DISKNO ;PUT DRIVE NUMBER BACK
STA FCB
LXI H,RESTSN ;RESTORE SECTOR NUMBERS..
LXI D,SECNOB ;..FOR NEW FILE TRANSFER.
MVI B,SECNOE-SECNOB ;ROUTINE ALSO DONE IN MENU.
CALL MOVE
LDA SENDFLG ;GOES TO EITHER SEND OR..
ORA A ;..RECEIVE FILE, DEPENDING..
JNZ SENDFIL ;..UPON WHICH ROUTINE SET..
JMP RCV1FIL ;..THE FLAG IN MULTI-FILE MODE.
DONETB: MVI A,TRUE ;INDICATE NO FILES BEING..
STA FSTFLG ;RESET MULTIFILE TRANS
LDA QFLG
ORA A
JZ DONETA
CALL ILPRT ;final comment
DB CR,LF,'All transfers completed'
DB CR,LF,BELL,0
DONETA: LXI SP,STAK ;restore stack
MVI A,CRC
STA CRCFLG ;turn off CRC option
MVI A,0FFH
STA FIRSTME ;set first-time flag
LDA TERMFLG ;SEE IF RETURN TO..
ORA A ;..TERMINAL MODE..
JNZ MENU ;..AFTER X'FER.
CALL CRLF
MVI A,'T'
STA OPTION
MVI A,' '
STA FCB+1 ;too late to specify filename
CALL INMODDATP
CALL INMODDATP;clear usart
JMP DSKSAVE
;============================= SUBROUTINES ===============
MSAVE: PUSH PSW
LDA NFILFLG
CPI TRUE
JZ NOSAVE ;CANT SAVE IF NO FILE
LDA SAVEFLG
CPI FALSE
JZ NOSAVE
POP PSW
CPI EOFCHAR ;dont save EOF's in file
RZ ;CP/M doesn't like them
LHLD HLSAVE
MOV M,A
INX H
SHLD HLSAVE ;MENU COMMAND DESTROYS HL-REG..
CPI LF
JNZ NOCOLON ;TYPE ":" AFTER EACH LINE FEED..
MVI A,':' ;..WHEN MEMORY SAVE ACTIVE.
CALL TYPE ;dont increment col count
NOCOLON:LDA SAVCCP
ORA A
JZ SUB1
LDA 7
SBI 8 ;..PAGE BELOW CCP ..
JMP SUB1A
SUB1: LDA 7
SUB1A: DCR A ;..OR BDOS HAS BEEN..
CMP H ;..REACHED AND DISKSAVE IS NEEDED.
CZ INTDSKSV
RET
NOSAVE: POP PSW
RET
;==================================
PROCOPT:LXI D,FCB+1
LDAX D
STA OPTION ;primary option
OPTLP: INX D
LDAX D
CPI ' '
JZ CKPRI
LXI H,OPTBL
MVI B,OPTBE-OPTBL
OPTCK: CMP M
JNZ OPTNO
MVI M,0 ;INSERT SECONDARY OPTION
JMP OPTLP
OPTNO: INX H
DCR B
JNZ OPTCK
JMP BDOPT
CKPRI: LDA FCB+1 ;CHECK ON THE PRIMARY OPTION
CPI 'X'
JZ EXPRT
CPI ' '
RZ
CPI 'M'
RZ
CPI 'T'
RZ
CPI 'S'
JZ CKFILE
CPI 'R'
JNZ BDOPT
LDA BATCHFLG ;IF MULT FILE MODE, THEN..
ORA A ;..RECV OPT MUST NOT BE NAMED
JZ CKFILE
LDA FCB+17
CPI ' '
RZ
BDOPT: CALL ILPRT ;fatal error
DB '++Bad Syntax++',CR,LF,0
JMP MENU
EXPRT: LDA XPRFLG
CMA
STA XPRFLG
JMP MENU
CKFILE: LDA FCB+17 ;IF OPTION THAT NEEDS FILE NAME,..
CPI ' ' ;..THEN CHECK TO SEE IF NAME..
RNZ ;..EXISTS.
JMP BDOPT
;==================================
TFILWR: LHLD HLSAVE
CALL NUMRECS ;DISK WRITE ROUTINE AS USED IN..
CALL WRTDSK ;..IN THE INTDSKSV ROUTINE.
LXI D,FCB3
MVI C,CLOSE
CALL BDOS
MVI A,TRUE
STA NFILFLG
RET
;================================
INTDSKSV:
MVI A,XOFF ;SEND A CTRL-S TO STOP..
CALL OUTMODDATP ;..REMOTE COMPUTER OUTPUT.
MVI D,0 ;D IS THE BUFFER COUNT
CALL INMODEM ;GET LAST BYTES SENT..
STA LASTB1 ;..AFTER CTRL-S.
CALL INMODEM ;ADD MORE CALLS TO INMODEM..
STA LASTB2 ;..AND STA LASTBYT# IF YOU ARE..
;..LOSING BYTES WHEN MEMORY IS FULL.
PUSH D
CALL NUM1REC
CALL WRTDSK ;WRITE THE RECORDS
POP D
LXI H,BOTTRAM
INR D
DCR D ;TEST BUFFER COUNT FOR ZERO
JZ CTRLQ
LDA LASTB1 ;GET THE LAST BYTES THAT WERE..
MOV M,A ;..SAVED AND PUT THEM IN..
INX H ;..BOTTRAM.
CALL CTYPE
DCR D
JZ CTRLQ
LDA LASTB2
MOV M,A
INX H
CALL CTYPE
CTRLQ: SHLD HLSAVE
MVI A,XON ;SEND START CHARACTER..
CALL OUTMODDATP ;..TO REMOTE COMPUTER.
RET
;======================================
;SUBROUTINE LOOPS UNTIL THE MODEM RECEIVES A CHARACTER OR 100ms
;RETURNS BYTE COUNT IN D OR ZERO FOR TIMEOUT
;if ^S is received it waits until ^Q is received
INMODEM:LDA FASTCLK
ORA A
LXI B,1250
JZ CHKMOD
LXI B,2500
CHKMOD: CALL INMODCTLP
CALL ANIRCV
CALL CPIRCV
JZ GETBYTE
DCX B
MOV A,B
ORA C
JNZ CHKMOD
RET
GETBYTE:CALL INMODDATP
ANI 7FH ;clear parity
CPI XOFF
JNZ TWAIT4
PUSH H
CALL ILPRT
DB CR,LF,'XOFF received, type ^Q to force continuation',CR,LF,0
TWAIT1: CALL INMODCTLP
CALL ANIRCV
CALL CPIRCV
JZ TWAIT2
CALL STAT ;local key?
JZ TWAIT1
CALL KEYIN ;get char
CPI XON
JZ TWAIT3
JMP TWAIT1
TWAIT2: CALL INMODDATP
ANI 7FH
CPI XON ;remote XON?
JNZ TWAIT1
CALL ILPRT
DB 'XON received',CR,LF,0
TWAIT3: POP H
JMP INMODEM
TWAIT4: INR D
RET
;================================
NUMRECS:MVI M,EOFCHAR
INX H
LXI D,127
DAD D
NUM1REC:LXI D,-(BOTTRAM)
DAD D
MOV A,L ;DIVIDE HL BY 128..
ORA A
RAL ;..TO GET THE..
MOV L,H ;..NUMBER OF SECTORS
MVI H,0
PUSH PSW
DAD H
POP PSW
MVI A,0
ADC L
MOV L,A ;RETNS WITH NUMBER OF..
RET ;..128 BYTE RECORDS IN HL.
;======================================
WRTDSK: LXI D,BOTTRAM
NEXTWRT:MVI C,STDMA
CALL BDOSRT
PUSH D
LXI D,FCB3
MVI C,WRITE
CALL BDOSRT
POP D
INR A
JZ WRTERR
XCHG
PUSH D
LXI D,128
DAD D
POP D
XCHG
DCX H
MOV A,H
ORA L
JNZ NEXTWRT
CALL RSDMA ;for CP/M 1.4
RET
WRTERR: CALL ILPRT
DB 'Terminal file write error',CR,LF,0
RET
;==========================
BDOSRT: PUSH B
PUSH D
PUSH H
CALL BDOS
POP H
POP D
POP B
RET
;============================
MOVE2: LXI H,FCB3
CALL INITFCBS
LXI H,FCB
LXI D,FCB3
MVI B,12
CALL MOVE
RET
;=============================
;FILE TRANSFER ROUTINE - CALLED WITH
;CONTROL-T FROM TERMINAL ROUTINE.
;TRANSFER MAY BE CANCELLED WHILE SENDING BY USING CONTROL-X.
TRANSFER:
SHLD HLSAVE
PUSH D
PUSH B
PUSH PSW
LXI H,FCB4
CALL INITFCBS ;INITIALIZES FCBS POINTED..
LXI H,FCB+16 ;..TO BY HL REG.
CALL INITFCBS
GET: CALL ILPRT
DB CR,LF,'Enter file name to be transferred - C/R TO QUIT: ',0
LXI D,CMDBUF
CALL INBUFF
CALL CRLF
LDA CMDBUF+2 ;WAS FILE ENTERED
CPI 20H
JZ TRANCAN
LXI D,CMDBUF
LXI H,FCB4
CALL CPMLINE
LXI D,FCB4
MVI C,OPEN
CALL BDOS
CPI 0FFH ;RETURN WITH 0FFH MEANS
JNZ CONTIN ;FILE DOES NOT EXIST
CALL ILPRT
DB '++File does not exist++',CR,LF,0
TRANS2L:CALL ILPRT
DB 'Type "^X" to cancel transfer',CR,LF
DB 'Type "A" to re-enter name: ',BELL,0
CALL KEYIN
CALL UCASE
CALL CTYPE ;ECHO RESPONSE
CALL CRLF
CPI 'A'
JZ GET
CPI CAN
JZ TRANCAN
JMP TRANS2L
CONTIN: LXI D,80H
MVI C,STDMA
CALL BDOS
LDA ECHOFLG ;computer mode ?
ORA A
JZ READMR
MVI A,SAVON ;activate remote save
CALL CHRSND ;computer mode only
READMR: LXI D,FCB4
MVI C,READ
CALL BDOS
ORA A
JNZ RETNS
CALL SEND80C
CPI EOFCHAR ;END OF FILE
JZ RETNS
CPI CAN ;CANCELLATION?
JZ TRANC1
JMP READMR
RETNS: LDA ECHOFLG
ORA A
JZ RETN1
MVI A,SAVOFF
CALL CHRSND ;deactivate remote save
RETN1: CALL ILPRT
DB CR,LF,'++File transfer completed++',CR,LF,BELL,0
JMP RETURN
TRANC1: LDA ECHOFLG
ORA A
JZ TRANCAN
MVI A,SAVOFF
CALL CHRSND ;deactivate remote save
TRANCAN:CALL ILPRT
DB CR,LF,'++ Transfer cancelled ++',CR,LF,BELL,0
RETURN: POP PSW
POP B
POP D
LHLD HLSAVE
RET
;=============================
INITFCBS: ;ENTRY AT +2 WILL LEAVE..
MVI M,0 ;..DRIVE NO. INTACT.
INX H ;WILL INITIALIZE AN FCB..
MVI B,11 ;..POINTED TO BY HL-REG. FILLS 1ST POS
LOOP10: MVI M,' ' ;..WITH 0, NEXT 11 WITH..
INX H ;..WITH BLANKS, AND LAST..
DCR B ;..21 WITH NULLS.
JNZ LOOP10
MVI B,21
LOOP11: MVI M,0
INX H
DCR B
JNZ LOOP11
RET
;===========================
CHRSND: PUSH PSW
REDY: CALL INMODCTLP
CALL ANISND
CALL CPISND
JNZ REDY
POP PSW
CALL OUTMODDATP
RET
;===============================
;used by ^T transfer
SEND80C:MVI B,80H
LXI H,80H
SEND81: MOV A,M ;get next char
ANI 7FH ;remove top bit
CPI EOFCHAR ;end of file?
RZ
CALL FILTER ;ignore non printing ASCII
JC SEND85
MOV C,A
LDA ECHOFLG
ORA A
MOV A,C
JNZ SEND83
CPI LF ;ignore lf's in terminal mode
JZ SEND85
MOV A,C
CALL CHRSND ;send to remote
CPI CR
JZ SEND82
PUSH B
MVI D,0
CALL INMODEM ;wait 100ms for echo
POP B
JZ SEND85 ;zero indicates no echo
JMP SEND84
SEND83: CALL CHRSND
PUSH PSW
PUSH B
LXI B,1 ;zero delay
CALL CHKMOD ;check for XOFF from remote
POP B
POP PSW
SEND84: CALL CTYPE ;local console
PUSH B
PUSH H
CALL MSAVE ;save to memory
POP H
POP B
JMP SEND85
SEND82: PUSH B
MVI D,0
CALL INMODEM ;wait 100ms for echo
POP B
JZ SEND85 ;loop until timeout
PUSH B
CALL CTYPE
PUSH PSW ;save inmodem flag
PUSH H
CALL MSAVE
POP H
POP PSW
POP B
JMP SEND82
SEND85: CALL STAT ;TEST TO SEE IF
ORA A ;CANCELLATION REQUESTED
JZ SKIP12
CALL KEYIN
CPI CAN ;cancel?
RZ
CPI SAVECHR ;memory save toggle
JNZ SEND86
LDA NFILFLG
CPI TRUE ;cant save if no file
JZ SEND86
LDA SAVEFLG
CMA
STA SAVEFLG
SEND86: CPI PRNCHR
JNZ SKIP12
LDA LSTFLG
CMA
STA LSTFLG ;print toggle
SKIP12: INX H
DCR B
JNZ SEND81
RET
;=================================-
;send filename
SENDFN: LDA QFLG
ORA A
JZ SWNAK
CALL ILPRT ;first comment
DB 'Awaiting name NAK',CR,LF,0
SWNAK: MVI E,80 ;80 second timeout
CALL WAITNLP
MVI A,ACK ;GOT NAK, SEND ACK
CALL SEND
LXI H,FILECT
DCR M
JM NOMRNM
LHLD NBSAVE ;GET FILE NAME..
LXI D,FCB ;..IN FCB
MVI B,12
CALL MOVE
SHLD NBSAVE
CALL SENDNM ;SEND IT
ORA A ;CLEAR CARRY
RET
NOMRNM: MVI A,EOT
CALL SEND
STC
RET
;======================================
SENDNM: PUSH H
SEND1NM:MVI D,11 ;COUNT CHARS IN NAME
MVI C,0 ;INIT CHECKSUM
LXI H,FCB+1 ;ADDRESS NAME
NAMLPS: MOV A,M ;SEND NAME
ANI 7FH ;STRIP HIGH ORDER BIT SO CP/M 2..
CALL SEND ;..WON'T SEND R/O FILE DESIGNATION.
LDA SSEEFLG
ORA A
JZ ACKLP ;already typed by SEND
LDA QFLG ;SHOW NAME IF..
ORA A ;..QFLG NOT SET.
MOV A,M
CNZ TYPE ;first comment
ACKLP: PUSH B ;SAVE CKSUM
MVI B,1 ;WAIT FOR RECEIVER..
CALL RECV ;..TO ACKNOWLEDGE..
POP B ;..GETTING LETTER.
JC SCKSER
CPI ACK
JNZ ACKLP
INX H ;NEXT CHAR
DCR D
JNZ NAMLPS
MVI A,EOFCHAR ;TELL RECEIVER END OF NAME
CALL SEND
LDA QFLG
ORA A
CNZ CRLF ;first comment
MOV D,C ;SAVE CHECKSUM
MVI B,1
CALL RECV ;GET CHECKSUM..
CMP D ;..FROM RECEIVER.
JZ NAMEOK
SCKSER: MVI A,BDNMCH ;BAD NAME-TELL RECEIVER
CALL SEND
LDA QFLG
ORA A
JZ SKCSER1
CALL ILPRT ;error message
DB 'Checksum error',CR,LF,0
SKCSER1:MVI E,80 ;DO HANDSHAKING OVER
CALL WAITNLP ;DON'T PRINT "AWAITING NAK" MSG
MVI A,ACK
CALL SEND
JMP SEND1NM
NAMEOK: MVI A,ACK ;GOOD NAME-TELL RECEIVER
CALL SEND
POP H
RET
;============================
;get filename
GETFN: LXI H,FCB
CALL INITFCBS+2 ;DOES NOT INITIALIZE DRIVE
LDA QFLG
ORA A
JZ GNAMELP
CALL ILPRT ;first comment
DB 'Awaiting file name',CR,LF,0
GNAMELP:CALL HSNAK
JC GNAMELP
CALL GETNM ;GET THE NAME
CPI EOT ;IF EOT, THEN NO MORE FILES
JZ NOMRNG
ORA A ;CLEAR CARRY
RET
NOMRNG: STC
RET
;================================
GETNM: PUSH H
GETNM1: MVI C,0 ;INIT CHECKSUM
LXI H,FCB+1
NAMELPG:MVI B,5
CALL RECV ;GET CHAR
JNC GETNM3
LDA QFLG
ORA A
JZ GETNM2
CALL ILPRT ;error message
DB 'Time out receiving filename',CR,LF,0
GETNM2: JMP GCKSER
GETNM3: CPI EOT ;IF EOT, THEN NO MORE FILES
JZ GNRET
CPI EOFCHAR ;GOT END OF NAME
JZ ENDNAME
MOV M,A ;PUT NAME IN FCB
LDA RSEEFLG
ORA A
JZ GETNM4 ;already typed by RECV
LDA QFLG ;TYPE IT IF NO QFLG
ORA A
MOV A,M
CNZ CTYPE ;first comment
GETNM4: PUSH B ;SAVE CKSUM
MVI A,ACK ;ACK GETTING LETTER
CALL SEND
POP B
INX H ;GET NEXT CHAR
MOV A,L ;DON'T LET NOISE...
CPI 7FH ;..CAUSE OVERFLOW..
JZ GCKSER ;..INTO PROGRAM AREA.
JMP NAMELPG
ENDNAME:LDA QFLG
ORA A
CNZ CRLF ;first comment
MOV A,C ;SEND CHECKSUM
CALL SEND
MVI B,1
CALL RECV ;CHECKSUM GOOD?
CPI ACK ;YES IF ACK SENT..
JZ GNRET ;..ELSE DO OVER.
GCKSER: LXI H,FCB ;CLEAR FCB (EXCEPT DRIVE)..
CALL INITFCBS+2 ;..SINCE IT MIGHT BE DAMAGED..
LDA QFLG ;..BY TOO MANY CHARS.
ORA A
JZ GCK1SER
CALL ILPRT ;error message
DB 'Checksum error',CR,LF,0
GCK1SER:CALL HSNAK ;DO HANDSHAKING OVER
JC GCK1SER
JMP GETNM1
GNRET: POP H
RET
;==============================
HSNAK: MVI A,NAK ;SEND NAK UNTIL..
CALL SEND ;..RECEIVING ACK.
CALL CKABORT ;DON'T GET HUNG UP HERE
MVI B,2 ;WAIT 2 SECONDS..
CALL RECV ;..IN RECEIVE.
CPI ACK ;IF ACK,RETURN WITH..
RZ ;..CARRY CLEAR.
STC
RET
;============================
TNMBUF: MVI A,FALSE ;CALL FROM SENDFIL ONLY ONCE.
STA FSTFLG
STA FILECT
CALL SCAN
LXI H,NAMEBUF
SHLD NBSAVE ;SAVE ADDR OF 1ST NAME
TNLP1: CALL TRTOBUF
LXI H,FCB
LXI D,FCBBUF
CALL CPMLINE ;PARSE NAME TO CP/M FORMAT
TNLP2: CALL MFNAME ;SEARCH FOR NAMES (* FORMAT)
JC NEXTNM
LDA FCB+10 ;IF CP/M 2 $SYS FILE..
ANI 80H ;..DON'T SEND
JNZ TNLP2
LHLD NBSAVE ;GET NAME
LXI D,FCB ;MOVE IT TO FCB
XCHG
MVI B,12
CALL MOVE
XCHG
SHLD NBSAVE ;ADDR OF NEXT NAME
LXI H,FILECT ;COUNT FILES FOUND
INR M
JMP TNLP2
;
NEXTNM: LXI H,NAMECT ;COUNT NAMES FOUND
DCR M
JNZ TNLP1
LXI H,NAMEBUF ;SAVE START OF BUFFER
SHLD NBSAVE
LDA FILECT
ORA A
JZ NOBFILE
CPI 65 ;NO MORE THAN 64 TRANSFERS
RC
MVI A,64 ;ONLY X'FER FIRST 64
STA FILECT
RET
NOBFILE:CALL ILPRT ;fatal error
DB 'No file',CR,LF,0
JMP MENU
;==============================================-
;SCANS CMDBUF COUNTING NAMES AND PUTTING DELIMITER (SPACE)
;AFTER LAST NAME
SCAN: PUSH H
LXI H,NAMECT
MVI M,0
LXI H,CMDBUF+1 ;FIND END OF CMD LINE..
MOV C,M ;..AND PUT SPACE THERE.
MVI B,0
LXI H,CMDBUF+2
DAD B
MVI M,20H
LXI H,CMDBUF+1
MOV B,M
INR B
INR B
CALL EAT ;eat spaces
JZ DNSCAN
SCAN1LP:INX H
DCR B
JZ DNSCAN
MOV A,M
CPI 20H
JNZ SCAN1LP
CALL EAT
JZ DNSCAN
SHLD BGNMS ;SAVE START OF NAMES IN CMDBUF
INR B
DCX H
SCAN3LP:INX H
DCR B
JZ DNSCAN
MOV A,M
CPI 20H
JNZ SCAN3LP
LDA NAMECT ;COUNTS NAMES
INR A
STA NAMECT
CALL EAT
JZ DNSCAN
JMP SCAN3LP
;
DNSCAN: MVI M,20H ;SPACE AFTER LAST CHAR
POP H
RET
;==================================
;Space eater
EAT: INX H
DCR B
RZ
MOV A,M
CPI ' '
JZ EAT
RET
;==========================================
;PLACES NEXT NAME IN BUFFER SO CPMLINE MAY PARSE IT
TRTOBUF:LHLD BGNMS
MVI B,0
LXI D,FCBBUF+2
TBLP: MOV A,M
CPI 20H
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 20H
JZ TRBFEND
SHLD BGNMS
LXI H,FCBBUF+1 ;PUT # CHARS BEFORE NAME
MOV M,B
RET
;====================================
;IN CP/M V.2, IF FILE IS R/O OR SYS, IT IS CHANGED TO 'BAK'.
CKCPM2: MVI C,12
CALL BDOS
ORA A ;RETURN 0 MEANS CP/M 1
RZ
MVI C,STDMA
LXI D,80H
CALL BDOS
MVI C,SRCHF ;SEARCH FOR FILE
LXI D,FCB
CALL BDOS
CPI 0FFH
RZ
ADD A
ADD A ;MULT A-REG BY..
ADD A
ADD A ;..32 TO FIND..
ADD A ;..NAME IN DMA.
LXI H,80H
ADD L
MOV L,A ;HL POINTS TO DIR NAME
LXI D,9
DAD D ;POINT TO R/O ATTRIB BYTE
MOV A,M
ANI 80H ;TEST MSB
JNZ MKCHG ;IF SET, MAKE CHANGE
INX H ;CHECK SYSTEM ATTRIB BYTE
MOV A,M
ANI 80H
RZ ;NOT $SYS OR $R/O
DCX H
MKCHG: LXI D,-8
DAD D ;POINT HL TO FILENAME + 1
LXI D,FCB+1 ;MOVE DIR NAME TO FCB..
MVI B,11 ;..WITHOUT CHANGING DRIVE.
CALL MOVE
LXI H,FCB+9 ;R/O ATTRIB
MOV A,M
ANI 7FH ;STRIP R/O ATTRIB
MOV M,A
INX H ;SYS ATTRIB
MOV A,M
ANI 7FH
MOV M,A
LXI D,FCB
MVI C,30 ;SET NEW ATTRIBS IN DIR
CALL BDOS
;MAY BE CALLED BY CKBAKUP BELOW. ITS RETURN DONE HERE
PLANCHG:LXI H,FCB ;CHANGE NAME TO TYPE "BAK"
LXI D,6CH
MVI B,9 ;MOVE DRIVE AND NAME (NOT TYPE)
CALL MOVE
LXI H,75H ;START OF TYPE IN FCB2
MVI M,'B'
INX H
MVI M,'A'
INX H
MVI M,'K'
LXI D,6CH
MVI C,ERASE ;ERASE ANY PREV BACKUPS
CALL BDOS
LXI H,6CH ;FCB2 DR FIELD SHOULD..
MVI M,0 ;..0 FOR RENAME.
LXI D,FCB
MVI C,REN
CALL BDOS
RET
CKBAKUP:LDA BAKUPBYTE
ORA A
RZ
MVI C,SRCHF
LXI D,FCB
CALL BDOS
INR A
RZ ;FILE NOT FOUND
JMP PLANCHG ;IN "CKCPM2" - RET DONE THERE
;====================================
;receive a sector
RCVSECT:XRA A
STA ERRCT
RCVRPT: CALL VORQ
JZ RCVSQ
CALL CRLF ;clear col count
CALL ILPRT ;comment message
DB 'Awaiting # ',0
PUSH H ;SAVE IT
LHLD SECNO ;GET SECTOR NUMBER
INX H ;BUMP IT
CALL DECOUT ;PRINT SECTOR NUMBER IN DECIMAL
CALL ILPRT
DB ' (', 0
CALL DHXOUT ;16 BIT HEX CONVERSION & OUTPUT
CALL ILPRT
DB 'H)',0
POP H ;RESTORE IT
; If CRC is in effect, there is only a 7 second wait
; for the first SOH. If the SOH is not received within
; this time, then a NAK is sent which tells the sender
; to use checksum checking instead of CRC. This allows
; automatic compatability with versions of MODEM that
; do not implement Cyclic Redundancy Checking(CRC).
RCVSQ: LDA FIRSTME ;first SOH...
ORA A ;...been received?
JZ RCVSQ2 ;yes, go get next SOH
XRA A ;turn off...
STA FIRSTME ;...first soh recvd switch
LDA CRCFLG ;CRC in...
ORA A ;...effect?
JNZ RCVSQ2 ;no, do long wait for first SOH
MVI B,7 ;wait for upto 7 seconds
CALL RECV ;get a character from modem
JNC RCVSQ3 ;got a char, go see if SOH
LDA QFLG
ORA A
JZ CRCM
CALL ILPRT ;first comment
DB CR,LF,'++Switching to CHECKSUM MODE++',CR,LF,0
CRCM: MVI A,'C' ;turn off...
STA CRCFLG ;...CRC mode.
MVI A,NAK ;send NAK to tell sender checksum
CALL SEND ;...is in effect & to start sending.
JMP RCVSECT ;go start receiving sector
;
RCVSQ2: MVI B,7 ;10 IN ORIG PROG
CALL RECV
JC RCVSTOT
RCVSQ3: CPI SOH
JZ RCVSOH
ORA A ;IGNORE NULLS
JZ RCVSQ
CPI EOT
STC
RZ
MOV B,A
LDA QFLG
ORA A
JZ RCVSERR
MOV A,B
CALL CRLF
CALL HEXO
CALL ILPRT ;error message
DB 'H recv''d, not SOH',CR,LF,0
RCVSERR: MVI B,1
CALL RECV
JNC RCVSERR
MVI A,NAK
CALL SEND
LDA ERRCT
INR A
STA ERRCT
CPI ERRLIM
JC RCVRPT
LDA QFLG
ORA A
JZ RCVSABT
CALL CKQUIT
JZ RCVSECT
RCVSABT:CALL CLOSFIL
CALL ERXIT ;fatal error
DB CR,LF,'++Unable to receive block - Aborting++$'
RCVSTOT:LDA QFLG
ORA A
JZ RCVSERR
CALL ILPRT ;error message
DB CR,LF,'++ Timeout ++ ',0
RCVPRN: LDA ERRCT
CALL HEXO
CALL CRLF
JMP RCVSERR
RCVSOH: MVI B,1
CALL RECV
JC RCVSTOT
MOV D,A
MVI B,1
CALL RECV
JC RCVSTOT
CMA
CMP D
JZ RCVDATA
LDA QFLG
ORA A
JZ RCVSERR
CALL ILPRT ;error message
DB CR,LF,'++ Bad sector # in Hdr',CR,LF,0
JMP RCVSERR
RCVDATA:MOV A,D
STA RCVSNO
MVI A,1
STA DATAFLG
MVI C,0 ;clear checksum
CALL CLRCRC ;clear crc counter
LXI H,80H
RCVCHR: MVI B,1
CALL RECV
JC RCVSTOT
MOV M,A
INR L
JNZ RCVCHR
XRA A
STA DATAFLG
LDA CRCFLG
ORA A
JZ RCVCRC
MOV D,C
MVI B,1
CALL RECV
JC RCVSTOT
CMP D
JNZ RCVCERR
CHKSNUM:LDA RCVSNO
MOV B,A
LDA SECNO
CMP B
JZ RECVACK
INR A
CMP B
JNZ ABORT
RET
RCVCRC: MVI E,2 ;nr of crc bytes
RCV2CRC:MVI B,1
CALL RECV
JC RCVSTOT
DCR E
JNZ RCV2CRC
CALL CHKCRC
ORA A
JZ CHKSNUM
LDA QFLG
ORA A
JZ RCVSERR
CALL ILPRT ;error message
DB CR,LF,'++CRC error++',0
JMP RCVPRN
RCVCERR:LDA QFLG
ORA A
JZ RCVSERR
CALL ILPRT ;error message
DB '++Cksum error++ ',0
JMP RCVPRN
RECVACK:CALL SENDACK
JMP RCVSECT
;===============================
SENDACK:MVI A,ACK
CALL SEND
RET
;===========================
;send header
SENDHDR:CALL VORQ
JZ SENDHNM
CALL CRLF ;clear col count
CALL ILPRT ;comment message
DB 'Send # ',0
PUSH H
LHLD SECNO ;GET SECTOR NUMBER
CALL DECOUT ;PRINT IT IN DECIMAL
CALL ILPRT
DB ' (',0
CALL DHXOUT ;16 BIT HEX CONVERSION & OUTPUT
CALL ILPRT
DB 'H)',0
POP H
SENDHNM:MVI A,SOH
CALL SEND
LDA SECNO
CALL SEND
LDA SECNO
CMA
CALL SEND
RET
;========================
SENDSEC:MVI A,1
STA DATAFLG
MVI C,0
CALL CLRCRC
LXI H,80H
SENDC: MOV A,M
CALL SEND
INR L
JNZ SENDC
XRA A
STA DATAFLG
RET
;===============================
SENDCKS:MOV A,C
CALL SEND
RET
;==============================
SENDCRC:PUSH H
LHLD CRCVAL
MOV A,H
CALL SEND
MOV A,L
CALL SEND
POP H
XRA A
RET
;===============================
GETACK: MVI B,7 ;10 IN ORIG PROG
CALL RECVDG
JC GETATOT
CPI ACK
RZ
MOV B,A
LDA QFLG
ORA A
JZ ACKERR
MOV A,B
CALL CRLF
CALL HEXO
CALL ILPRT ;error message
DB 'H Recv''d, not ACK',CR,LF,0
ACKERR: LDA ERRCT
INR A
STA ERRCT
CPI ERRLIM
RC
LDA QFLG
ORA A
JZ CSABORT
CALL CKQUIT
STC
RZ
CSABORT:CALL ERXIT ;fatal error
DB CR,LF,'Can''t send sector -- Aborting$'
GETATOT:LDA QFLG
ORA A
JZ ACKERR
CALL ILPRT ;error message
DB CR,LF,'Timeout on ACK',CR,LF,0
JMP ACKERR
;================================
CKABORT:LDA QFLG
ORA A
RZ
CALL STAT
RZ
CALL KEYIN
CPI CAN
JZ ABORT ;local abort
RET
ERXIT: POP D ;print message and abort
MVI C,PRINT
CALL BDOS ;does not update col count
CALL CRLF ;reset col count
ABORT: LXI SP,STAK
ABORTL: MVI B,1
CALL RECV ;wait until sender finishes
JNC ABORTL
MVI A,CAN
CALL SEND
ABORTW: MVI B,1
CALL RECV ;wait until sender finishes
JNC ABORTW
MVI A,' ' ;send a space to clear out ^X
CALL SEND
CALL ILPRT
DB CR,LF,'Routine cancelled',CR,LF,BELL,0
LXI H,RESTROPT
LXI D,OPTBL
MVI B,OPTBE-OPTBL-2
CALL MOVE ;clear options except T
XRA A
STA BATCHFLG;clear N option
JMP DONETA
;===================================
INCRSNO:PUSH H
LHLD SECNO ;GET SECTOR NUMBER
INX H ;BUMP IT
SHLD SECNO ;STORE IT
POP H
RET
;===============================
AMBGTS: LXI H,FCB+1
MVI C,11
AMBIG: MOV A,M
INX H
CPI '?' ;test for ambiguous name
JNZ NOTAMB
CALL ILPRT ;fatal error
DB 'Ambiguous filename not allowed',CR,LF,0
JMP MENU
NOTAMB: DCR C
JNZ AMBIG
RET
;=============================
ERASFIL:LDA BATCHFLG ;DON'T ASK FOR ERASE..
ORA A ;..IN MULTI-FILE MODE,..
JNZ NOASK
LDA QFLG
ORA A
JZ NOASK ;dont hang up in Q mode
TFLERAS:CALL AMBGTS ;ambiguous name ?
LXI D,FCB
MVI C,SRCHF ;see if file exists already
CALL BDOS
INR A
RZ
CALL ILPRT
DB 'File exists -- Type ''Y'' to erase: ',BELL,0
CALL KEYIN
PUSH PSW
CALL CTYPE
POP PSW
CALL UCASE
CPI 'Y'
CALL CRLF
JNZ MENU
NOASK: LXI D,FCB
MVI C,ERASE
CALL BDOS
RET
;===========================
MAKEFIL:LXI D,FCB
MVI C,MAKE
CALL BDOS
INR A
RNZ
CALL ERXIT ;fatal error
DB 'Error - Can''t make file',CR,LF
DB 'Directory must be full$'
;==================================
CNREC: MVI C,12
CALL BDOS
ORA A ;0 means CP/M 1
JZ CNREC14
MVI C,FILSIZ ;COMPUTE FILE SIZE FUNCTION IN CP/M 2.x
LXI D,FCB ;POINT TO FILE CONTROL BLOCK
CALL BDOS
LHLD FCB+33 ;GET RECORD COUNT
SHLD RCNT ;STORE IT
LXI H,0 ;ZERO HL
SHLD FCB+33 ;RESET RANDOM RECORD IN FCB
RET
CNREC14:MVI A,'?' ;MATCH ALL EXTENTS
STA FCBEXT
MVI A,0FFH
STA MAXEXT ;INIT MAX EXT NO.
MVI C,SRCHF ;GET 'SEARCH FIRST' FNC
LXI D,FCB
CALL BDOS ;READ FIRST
INR A ;WERE THERE ANY?
JNZ SOME ;GOT SOME
CALL ERXIT ;fatal error
DB '++File not found++$'
;POINT TO DIRECTORY ENTRY
SOME: DCR A ;UNDO PREV 'INR A'
ANI 3 ;MAKE MODULUS 4
ADD A ;MULTIPLY...
ADD A ;..BY 32 BECAUSE
ADD A ;..EACH DIRECTORY
ADD A ;..ENTRY IS 32
ADD A ;..BYTES LONG
LXI H,80H ;POINT TO BUFFER
ADD L ;POINT TO ENTRY
ADI 15 ;OFFSET TO RECORD COUNT
MOV L,A ;HL NOW POINTS TO REC COUNT
MOV B,M ;GET RECORD COUNT
DCX H
DCX H ;BACK DOWN TO EXTENT NUMBER
DCX H
LDA MAXEXT ;COMPARE WITH CURRENT MAX.
ORA A ;IF NO MAX YET
JM BIGGER ;THEN SAVE RECORD COUNT ANYWAY
CMP M
JNC MOREDIR
BIGGER: MOV A,B ;SAVE NEW RECORD COUNT
STA RCNT
MOV A,M ;SAVE NEW MAX. EXTENT NO.
STA MAXEXT
MOREDIR:MVI C,SRCHN ;SEARCH NEXT
LXI D,FCB
CALL BDOS ;READ DIR ENTRY
INR A ;CHECK FOR END (0FFH)
JNZ SOME ;NOT END OF DIR...PROCESS EXTENT
LDA MAXEXT ;HIT END...GET HIGHEST EXTENT NO. SEEN
MOV L,A ;WHICH GIVES EXTENT COUNT -1
MVI H,0
MOV D,H
LDA RCNT ;GET RECORD COUNT OF MAX EXTENT SEEN
MOV E,A ;SAVE IT IN DE
DAD H
DAD H ;MULTIPLY # OF EXTENTS -1
DAD H ; TIMES 128
DAD H
DAD H
DAD H
DAD H
DAD D ;ADD IN SIZE OF LAST EXTENT
SHLD RCNT ;SAVE TOTAL RECORD COUNT
RET
;==================================
OPENFIL:XRA A
STA FCBEXT
LXI D,FCB
MVI C,OPEN
CALL BDOS
INR A
JNZ OPENOK
CALL ERXIT ;fatal error
DB 'Can''t open file$'
OPENOK: CALL VORQ
RZ
CALL ILPRT ;comment message
DB 'File open, size: ',0
LHLD RCNT ;GET RECORD COUNT
CALL DECOUT ;PRINT NUMBER OF SECTORS IN DECIMAL
CALL ILPRT ;PRINT
DB ' (',0
CALL DHXOUT
CALL ILPRT
DB 'H) sectors',CR,LF,0
RET
;=============================
CLOSFIL:LXI D,FCB
MVI C,CLOSE
CALL BDOS
INR A
RNZ
CALL ERXIT ;fatal error
DB 'Can''t close file$'
;================================
RDSECT: LDA SECINBF
DCR A
STA SECINBF
JM RDBLOCK
LHLD SECPTR
LXI D,80H
CALL MOVE128
SHLD SECPTR
RET
RDBLOCK:LDA EOFLG
CPI 1
STC
RZ
MVI C,0
LXI D,DBUF
RDSECLP:PUSH B
PUSH D
MVI C,STDMA
CALL BDOS
LXI D,FCB
MVI C,READ
CALL BDOS
POP D
POP B
ORA A
JNZ REOF
LXI H,80H
DAD D
XCHG
INR C
MOV A,C
CPI DBUFSIZ*8 ;BUFFER SIZE IN 128 BYTE SECTORS
JZ RDBFULL
JMP RDSECLP
REOF: MVI A,1
STA EOFLG
MOV A,C
RDBFULL:STA SECINBF
LXI H,DBUF
SHLD SECPTR
LXI D,80H
MVI C,STDMA
CALL BDOS
JMP RDSECT
;==================================
WRSECT: LHLD SECPTR
XCHG
LXI H,80H
CALL MOVE128
XCHG
SHLD SECPTR
LDA SECINBF
INR A
STA SECINBF
CPI DBUFSIZ*8 ;BUFFER SIZE IN 128 BYTE SECTORS
RNZ
WRBLOCK:LDA SECINBF
ORA A
RZ
MOV C,A
LXI D,DBUF
DKWRLP: PUSH H
PUSH D
PUSH B
MVI C,STDMA
CALL BDOS
LXI D,FCB
MVI C,WRITE
CALL BDOS
POP B
POP D
POP H
ORA A
JNZ WRERR
LXI H,80H
DAD D
XCHG
DCR C
JNZ DKWRLP
XRA A
STA SECINBF
LXI H,DBUF
SHLD SECPTR
RSDMA: LXI D,80H ;required by CP/M 1.4
MVI C,STDMA
CALL BDOS
RET
WRERR: CALL RSDMA
MVI A,CAN
CALL SEND
CALL ERXIT ;fatal error
DB 'Error writing file$'
;===========================================
;RECV: Receive a character
;Timeout time is in B, in seconds. Entry via 'RECVDG' deletes garbage
;characters on the line. For example, having just sent a sector, calling
;RECVDG will delete any line noise induced characters LONG before the
;ACK/NAK would be received.
RECVDG: CALL INMODDATP
CALL INMODDATP
RECV: PUSH D
LDA FASTCLK
ORA A
JZ MSEC
MOV A,B
ADD A
MOV B,A
MSEC: LXI D,15000 ;60% OF ORIG 50000
CALL CKABORT
MWTI: CALL STAT ;check keyboard
ORA A
JZ MWTJ
CALL KEYIN ;get char
CALL UCASE
CALL FLGTGL ;toggle appropriate flag
MWTJ: CALL INMODCTLP
CALL ANIRCV
CALL CPIRCV ;wait for modem char
JZ MCHAR
DCR E
JNZ MWTI
DCR D
JNZ MWTI
DCR B
JNZ MSEC
POP D
STC
RET
MCHAR: CALL INMODDATP
POP D
PUSH PSW
CALL UPDCRC ;calc crc
ADD C
MOV C,A
LDA RSEEFLG
ORA A
JZ MONIN
LDA VSEEFLG
ORA A
JNZ NOMONIN
LDA DATAFLG
ORA A
JZ NOMONIN
MONIN: POP PSW
PUSH PSW
CALL SHOW
NOMONIN:POP PSW
ORA A
RET
;==============================
;set/reset secondary options
FLGTGL: PUSH B
PUSH D
PUSH H
LXI H,OPTBL ;flag
LXI D,RESTROPT
MVI C,5 ;do Q,R,S,V,T but NOT N
MOV B,A
DOCMP: LDAX D ;get reference
CMP B
JNZ DOCMP1
MOV A,B
CMP M ;current setting
MOV M,A ;clear flag
JNZ DOCMP1
XRA A
MOV M,A ;set flag
DOCMP1: INX D
INX H
DCR C
JNZ DOCMP
POP H
POP D
POP B
RET
;=================================
SEND: PUSH PSW
LDA SSEEFLG
ORA A
JZ MONOUT
LDA VSEEFLG
ORA A
JNZ NOMONOT
LDA DATAFLG
ORA A
JZ NOMONOT
MONOUT: POP PSW
PUSH PSW
CALL SHOW
NOMONOT:POP PSW
PUSH PSW
CALL UPDCRC ;calc crc
ADD C
MOV C,A
POP PSW
CALL CHRSND ;send to remote
CALL STAT ;check for keypress
ORA A
RZ
CALL KEYIN ;get char
CALL UCASE
CALL FLGTGL ;toggle appropriate flag
RET
;==================================
WAITNAK:LDA QFLG
ORA A
JZ WAITNLP
CALL ILPRT ;first comment
DB 'Awaiting initial NAK',CR,LF,0
WAITNLP:CALL CKABORT
MVI B,1
CALL RECV
CPI NAK
RZ
CPI CRC ;crc request?
JZ WAITCRC ;yes, go set crc flag
DCR E
JZ ABORT
JMP WAITNLP
WAITCRC:XRA A
STA CRCFLG
LDA QFLG
ORA A
RZ
CALL ILPRT ;first comment
DB 'CRC request received',CR,LF,0
RET
;===============================
;return zero = no message display
;use ahead of comment messages except first and final
VORQ: LDA VSEEFLG
ORA A
RZ
LDA QFLG
ORA A
RET
;==================================
INITADR:LHLD 1
LXI D,3
DAD D
SHLD VSTAT+1
DAD D
SHLD VKEYIN+1
DAD D
SHLD VTYPE+1
LXI D,33
DAD D
SHLD LISTST+1
RET
;=================================
MOVEFCB:LXI H,FCB+16
LXI D,FCB
MVI B,16
CALL MOVE
XRA A
STA FCBRNO
STA FCBEXT
RET
;=========================
CMPDEHL:MOV A,E
CMP L
RNZ
MOV A,D
CMP H
RET
;=============================
;carry set for non printing ASCII chars + VT & FF
FILTER: CPI 8 ;BS
RC ;Byte is less than 08H
CPI LF+1 ;
CMC ;invert carry flag
RNC ;get rid of VT & HT, invert BS & LF carry flag
CPI CR ;
RC ;Byte is less than 0D
RZ ;byte = LF,CR or BS, no carry
CPI ' ' ;CPI space
RC ;Byte is between 0E & 0F, reject
CPI 7FH ;Del char
CMC ;printable chars to no carry, DEL to carry
RET
;==============================
;put C into print buffer, if buffer full byte is lost
LISTOUT:MOV A,C
ANI 7FH ;remove top bit
CALL FILTER ;ignore non printing chars
RC
LSTBUF: LHLD BUFEND
XCHG
LHLD BUFRIN
INX H
CALL CMPDEHL
JNZ LBUFF1 ;test for end of buffer
LHLD BUFBEG ;reset to start
LBUFF1: XCHG
LHLD BUFROUT
CALL CMPDEHL
RZ ;buffer full
XCHG
SHLD BUFRIN
MOV M,C ;save byte in buffer
RET
;==========================
;Check printer status, if busy return, else print one char
LISTST: CALL $-$
ORA A
RZ ;see if printer is busy
LHLD BUFRIN
XCHG
LHLD BUFROUT
CALL CMPDEHL
RZ ;return if buffer empty
INX H
PUSH H
LHLD BUFEND
XCHG
POP H
CALL CMPDEHL
JNZ PRBUF2
LHLD BUFBEG ;reset to start
PRBUF2: SHLD BUFROUT
MOV E,M
MVI C,LIST ;CP/M print function
CALL BDOS
RET
;=============================
CONO: MOV C,A
CPI 9
JNZ CONSOP
TAB: MVI A,' '
CALL CONSOP ;expand tabs
LDA COLCNT
ANI 7
JNZ TAB
RET
CONSOP: MOV C,A
LDA LSTFLG
ORA A
CNZ LISTOUT ;no tabs left by now
MOV A,C
CALL TYPE
MOV C,A
LXI H,COLCNT
CPI 7FH
RZ
INR M
CPI ' '
RNC
DCR M
MOV A,M
ORA A
RZ
MOV A,C
CPI 8
JNZ TRYCR
DCR M
RET
TRYCR: CPI CR
RNZ
MVI M,0
RET
;=============================
SHOW: CPI LF
JZ CTYPE
CPI CR
JZ CTYPE
PUSH PSW
PUSH H
LXI H,TWIDTH
LDA COLCNT ;check terminal width
CMP M
CNC CRLF ;force CR,LF
POP H
POP PSW
CPI 9
JZ CTYPE
CPI ' '
JC SHOWHEX
CPI 7FH
JC CTYPE
SHOWHEX:PUSH PSW
MVI A,'('
CALL CTYPE
POP PSW
CALL HEXO
MVI A,')'
CTYPE: PUSH PSW
PUSH B
PUSH D
PUSH H
ANI 7FH ;ensure parity bits are suppressed
CALL CONO ;type with tabs expanded
POP H
POP D
POP B
POP PSW
RET
;=============================
CRLF: PUSH PSW
MVI A,CR
CALL CTYPE
MVI A,LF
CALL CTYPE
POP PSW
RET
;==========================
TYPE: PUSH PSW
PUSH B
PUSH D
PUSH H
ANI 7FH ;ensure parity bits are suppressed
MOV C,A
VTYPE: CALL $-$
POP H
POP D
POP B
POP PSW
RET
;=========================
STAT: PUSH B
PUSH D
PUSH H
CALL LISTST ;o/p next char to printer
VSTAT: CALL $-$ ;get keyboard status
POP H
POP D
POP B
ORA A
RET
;===============================
KEYIN: PUSH B
PUSH D
PUSH H
VKEYIN: CALL $-$
POP H
POP D
POP B
RET
;===========================
UCASE: CPI 61H ;CHANGES LOWER CASE CHARACTER..
RC ;..IN A-REG TO UPPER CASE.
CPI 7BH
RNC
ANI 5FH
RET
;============================
DECOUT: PUSH PSW
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
POP PSW
RET
;======================================
; - double precision hex output routine.
DHXOUT: PUSH H
PUSH PSW
MOV A,H ;GET MS BYTE
ORA A
CNZ HEXO ;suppress high order if zero
MOV A,L ;GET LS BYTE
CALL HEXO ;OUTPUT LOW ORDER BYTE
POP PSW
POP H
RET
;===============================
HEXO: PUSH PSW
RAR
RAR
RAR
RAR
CALL NIBBL
POP PSW
NIBBL: ANI 0FH
CPI 10
JC ISNUM
ADI 7
ISNUM: ADI '0'
JMP CTYPE
;=====================================
;RETNS W/ ZERO SET IF RETRY ASKED. IF MULTI-FILE MODE, THEN
;NO QUESTIONS ASKED, JUST QUIT
CKQUIT: LDA BATCHFLG
ORA A
JZ CKQTASK ;ASK FOR RETRY
INR A ;RESET ZERO FLG
RET
CKQTASK:XRA A
STA ERRCT
CALL ILPRT
DB 'Multiple errors encountered.',CR,LF
DB 'Type Q to quit, R to retry: ',BELL,0
CALL KEYIN
PUSH PSW
CALL CTYPE
CALL CRLF
POP PSW
CALL UCASE
CPI 'R'
RZ
CPI 'Q'
JNZ CKQUIT
ORA A
RET
;==============================
ILPRT: XTHL
ILPLP: MOV A,M
ORA A
JZ ILPRET
CALL TYPE
INX H
JMP ILPLP
ILPRET: XTHL
RET
;=========================
MOVE128:MVI B,128
MOVE: MOV A,M
STAX D
INX H
INX D
DCR B
JNZ MOVE
RET
;==================================
;Cyclic Redundancy Check Subroutines.
;The generator is X^16 + X^12 + X^5 +1 as
;recommended by CCITT
;Generate table for fast CRC calculation
INITCRC:LXI H,CRCTBL
MVI C,0
GLOOP: XCHG
LXI H,0
MOV A,C
PUSH B
MVI B,8
XRA H
MOV H,A
LLOOP: DAD H
JNC LSKIP
MVI A,10H
XRA H
MOV H,A
MVI A,21H
XRA L
MOV L,A
LSKIP: DCR B
JNZ LLOOP
POP B
XCHG
MOV M,D
INR H
MOV M,E
DCR H
INX H
INR C
JNZ GLOOP
RET
;=================================
;Reset CRC Accumulator for a new message.
CLRCRC: PUSH H
LXI H,0
SHLD CRCVAL
POP H
RET
;====================================
;Update CRC Accumulator using byte in (A).
UPDCRC: PUSH PSW
PUSH B
PUSH D
PUSH H
LHLD CRCVAL
XCHG
MVI B,0
XRA D
MOV C,A
LXI H,CRCTBL
DAD B
MOV A,M
XRA E
MOV D,A
INR H
MOV E,M
XCHG
SHLD CRCVAL
POP H
POP D
POP B
POP PSW
RET
;==================================
; Check CRC bytes of received message.
CHKCRC: PUSH H
LHLD CRCVAL
MOV A,H
ORA L
POP H
RZ
MVI A,0FFH
RET
;
CRCVAL: DW 0
;======================================
;INBUFF - DUPLICATES READ BUFFER ROUTINE
;SAME AS CP/M FUNCTION 10, BUT DOES
;NOT USE CTRL-C (REASON FOR ROUTINE).
;DOES ALLOW CONTROLS U, R, E, AND H (BACKSPACE).
INBUFF: PUSH PSW
PUSH H
PUSH B
PUSH D ;DE REGISTERS MUST BE PUSHED LAST
ISTART: CALL ICLEAR ;CLEAR THE BUFFER AREA
POP D ;GET ADDRESS OF BUFFER ON RETRIES
PUSH D ;RESTORE STACK
XRA A
INX D ;ADDRESS COUNT FIELD
STAX D ;INITIALIZE WITH A ZERO IN COUNT BYTE
INX D
XCHG ;ADDRESS FIRST BUFFER BYTE WITH HL
INBUFA: CALL CONIN
CPI 0DH ;IS IT A RETURN?
JZ INBUFR ;IF SO, THEN RETURN
CPI 7FH ;IS IT A DELETE?
JZ DELETE
CPI 8 ;CTRL-H WILL BACKSPACE..
JZ DELETE ;..OVER DELETED CHAR.
CPI 'U'-40H ;IS IT A CTRL-U
JZ INBUFO ;OUTPUT # CR LF AND START OVER
CPI 'R'-40H ;CTRL-R RETYPES LINE
JZ RETYPE
CPI 'E'-40H
JZ PCRLF
CPI 20H ;NO CONTROL CHARACTERS OTHER..
JC INBUFA ;..THAN ABOVE ALLOWED.
MOV B,A ;SAVE INPUTTED CHARACTER
XCHG ;SAVE HL IN DE
POP H ;GET ADDRESS OF BUFFER IN HL
PUSH H ;RESTORE STACK
INX H ;ADDRESS COUNT BYTE
INR M ;INCREASE COUNT BYTE
DCX H ;ADDRESS MAXIMUM
MOV A,M ;PUT MAXIMUM IN A
INX H ;ADDRESS COUNT
CMP M ;COMPARE COUNT TO MAXIMUM
JC ALERT ;IF MAXIMUM, RING BELL AND WAIT FOR CR
XCHG ;RESTORE BUFFER POINTER TO HL
MOV M,B ;PUT INPUTTED CHARACTER IN BUFFER
MOV A,B ;OUTPUT IT
CALL TYPE
INX H ;BUMP POINTER
JMP INBUFA ;GET NEXT CHARACTER
DELETE: XCHG ;SAVE BUFFER POINTER IN DE
POP H ;ADDRESS BEGINNING OF BUFFER
PUSH H ;RESTORE STACK
INX H ;ADDRESS COUNT FIELD
MOV B,A ;SAVE DELETE CHAR - 7FH OR 08H
MOV A,M
SUI 1 ;DECREASE COUNT
MOV M,A
JC NODEL ;DON'T DELETE PAST BEGINING OF BUFFER.
XCHG ;RESTORE BUFFER POINTER TO HL
DCX H ;POINT TO LAST BYTE INPUTTED
MOV A,B ;GET BACK EITHER 7FH OR 08H
MOV B,M ;GET CHARACTER BEING DELETED
MVI M,20H ;RESTORE BLANK
CPI 8
JZ BKSPC
CPI 7FH
JZ BKSPC0
JMP INBUFA ;GET NEXT CHARACTER
NODEL: INR M ;DON'T LEAVE COUNT NEGATIVE
XCHG ;RESTORE POINTER TO HL
JMP INBUFA
BKSPC0: MVI A,08H
BKSPC: CALL TYPE ;TRUE ERASE IF 08H
MVI A,20H
CALL TYPE
MVI A,8
CALL TYPE
JMP INBUFA
INBUFO: MVI A,'#'
CALL TYPE
CALL CRLF
JMP ISTART
RETYPE: POP D
PUSH D
INX D ;POINT TO CURRENT NUMBER..
LDAX D ;..OF CHARACTERS.
MOV B,A
MVI A,'#'
CALL TYPE
CALL CRLF
MOV A,B ;TEST IF ZERO INPUT
ORA A
JZ INBUFA
CTLRLP: INX D
LDAX D
CALL TYPE
DCR B
JNZ CTLRLP
JMP INBUFA
ALERT: MVI A,7
CALL TYPE
DCR M
XCHG
JMP INBUFA
PCRLF: CALL CRLF
JMP INBUFA
INBUFR: CALL CRLF
POP D
POP B
POP H
POP PSW
RET
ICLEAR: POP D ;ACCOUNTS FOR CALL
POP H ;ADDRESS BUFFER IN HL
PUSH H ;RESTORE..
PUSH D ;..STACK
MOV B,M ;SAVE MAXIMUM IN B
INX H ;POINT TO FIRST..
INX H ;..BUFFER BYTE.
MVI A,20H
CLEARL: MOV M,A
INX H
DCR B
JNZ CLEARL
RET
CONIN: PUSH H
PUSH D
PUSH B
CONINLP:CALL STAT
JZ CONINLP
CALL KEYIN
CALL UCASE
POP B
POP D
POP H
RET
;==========================================
;LOADS A COMMAND LINE ADDRESSED BY DE REGISTERS (MAX # CHARACTERS IN LINE
;IN DE, NUMBER OF CHARS 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 MAXIMUM LENGTH OF AT LEAST ONE MORE
;THAN THE GREATEST NUMBER OF CHARACTERS THAT WILL BE NEEDED.
CPMLINE:PUSH PSW
PUSH B
PUSH D
PUSH H
CALL INIT ;FILLS FCBS WITH BLANKS AND NULLS
XCHG ;GET START OF COMMAND LINE IN HL.
INX H ;ADDRESS # BYTES IN CMD LINE.
MOV E,M ;LOAD DE PAIR WITH # BYTES.
MVI D,0
INX H
DAD D ;POINT TO BYTE AFTER LAST CHAR..
MVI M,0DH ;..IN CMD 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 CDRIVE
MVI C,8 ;TRANSFER FIRST FILENAME TO FCB.
CALL TRANS
CPI 0DH
JZ CDONE
CPI 20H ;IF SPACE, THEN START OF..
JZ NAME2 ;..SECOND FILENAME.
POP H ;FILETYPE MUST BE AFTER..
PUSH H ;..EIGHTH BYTE OF NAME.
LXI B,9
DAD B
MVI C,3 ;TRANSFER TYPE OF FIRST FILE
CALL TRANS
CPI 0DH
JZ CDONE
NAME2: LDAX D ;EAT MULTIPLE SPACES..
CPI 20H ;..BETWEEN NAMES.
JNZ NAME2C
INX D
JMP NAME2
LDAX D
CPI 0DH ;TEST IF FIRST NAME..
JZ CDONE ;..ONLY AND THEN SPACE.
NAME2C: POP H ;SECOND NAME STARTS IN 16TH BYTE.
PUSH H ;POINT HL TO THIS BYTE.
LXI B,16
DAD B
CALL CDRIVE
MVI C,8
CALL TRANS
CPI 0DH
JZ CDONE
POP H ;SECOND TYPE STARTS IN 25TH BYTE.
PUSH H
LXI B,25
DAD B
MVI C,3
CALL TRANS
CDONE: POP H
PUSH H
INX H ;POINT TO FIRST CHAR OF FIRST NAME IN FCB.
CALL CSCAN ;CHECK FOR * (AMBIGUOUS NAMES).
POP H
PUSH H
LXI B,17 ;POINT TO FIRST CHAR OF SECOND NAME IN FCB.
DAD B
CALL CSCAN
POP H
POP D
POP B
POP PSW
RET
INIT: PUSH H ;INITIALIZES FCB WITH 1 NULL (FOR FIRST DRIVE),..
PUSH B ;..11 BLANKS, 4 NULLS, 1 NULL (FOR 2ND DRIVE),..
MVI M,0 ;..11 BLANKS, AND 4 NULLS.
INX H
MVI B,11
MVI A,20H
CALL INITFILL
MVI B,5
MVI A,0
CALL INITFILL
MVI B,11
MVI A,20H
CALL INITFILL
MVI B,4
MVI A,0
CALL INITFILL
POP B
POP H
RET
INITFILL:
MOV M,A
INX H
DCR B
JNZ INITFILL
RET
CDRIVE: 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 ('INIT' PUT ZERO)
LDAX D
ANI 5FH
SUI 40H ;CALCULATE DRIVE (A=1, B=2,...)..
MOV M,A ;..AND PLACE IT IN FCB.
INX D ;ADDRESS FIRST BYTE OF..
INX D ;..IN CMD LINE,..
DEFDR: INX H ;..AND NAME FIELD IN FCB.
RET
TRANS: LDAX D ;TRANSFER FROM CMD LINE TO FCB..
INX D ;..UP TO NUMBER OF CHARS SPECIFIED..
CPI 0DH ;..BY C-REG. KEEP SCANNING FIELD..
RZ ;..WITHOUT TRANSFER UNTIL A DELIMITING..
CPI '.' ;..FIELD CHAR SUCH AS '.', BLANK, OR..
RZ ;..C/R (FOR END OF CMD LINE).
CPI 20H
RZ
DCR C
JM TRANS ;ONCE C-REG IS LESS THAN ZERO, KEEP READING..
MOV M,A ;..CMD LINE BUT DO NOT TRANSFER TO FCB.
INX H
JMP TRANS
CSCAN: 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 TSTTYP
FILL1: CALL FILL
TSTTYP: MVI B,3 ;SCAN AND FILL TYPE FIELD FOR NAME..
TSTLTYP:MOV A,M ;..SPECIFIED ABOVE.
CPI '*'
JZ FILL2
INX H
DCR B
RZ
JMP TSTLTYP
FILL2: CALL FILL
RET
FILL: MVI M,'?' ;ROUTINE TRANSFERS '?'.
INX H
DCR B
JNZ FILL
RET
;======================================
;IN-LINE COMPARE. COMPARES STRING ADDRESSED BY DE-REG TO STRING
;AFTER CALL (ENDS WITH ZERO). RETURN WITH CARRY SET MEANS STRINGS
;NOT THE SAME. ALL REGISTERS EXCEPT A-REG ARE UNAFFECTED.
ILCOMP: XTHL ;POINT HL TO 1ST CHAR.
PUSH D
ILCMPL: MOV A,M ;HL POINTS TO IN-LINE STRING.
ORA A ;END OF STRING IF ZERO.
JZ SAME
LDAX D
CMP M
JNZ NOTSAME
INX H
INX D
JMP ILCMPL
NOTSAME:MVI A,0 ;IF NOT SAME, FINISH THRU..
NSLP: INX H ;..STRING SO RETURN WILL..
CMP M ;..GO TO INSTRUCTION AFTER..
JNZ NSLP ;..STRING AND NOT REMAINDER OF STRING.
STC
SAME: POP D
INX H ;AVOIDS A NOP INSTRUCTION..
XTHL ;..WHEN RETURNING.
RET
;==================================
;MULTI-FILE ACCESS SUBROUTINE. ALLOWS PROCESSING
;OF MULTIPLE FILES (E.G. *.ASM) FROM DISK. THIS
;ROUTINE BUILDS THE PROPER NAME IN THE FCB EACH
;TIME IT IS CALLED.
;THE FCB WILL BE 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 CAN BE FOUND
MFNAME: PUSH B
PUSH D
PUSH H
MVI C,STDMA
LXI D,80H
CALL BDOS
POP H
POP D
POP B
XRA A
STA FCBEXT
LDA MFFLG1
ORA A
JNZ MFN01
MVI A,1
STA MFFLG1
LXI H,FCB
LXI D,MFREQ
LXI B,12
CALL MOVER
LDA FCB
STA MFCUR ;SAVE DISK IN CURR FCB
LXI H,MFREQ
LXI D,FCB
LXI B,12
CALL MOVER
PUSH B
PUSH D
PUSH H
MVI C,SRCHF
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
JMP MFN02
MFN01: LXI H,MFCUR
LXI D,FCB
LXI B,12
CALL MOVER
PUSH B
PUSH D
PUSH H
MVI C,SRCHF
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
LXI H,MFREQ
LXI D,FCB
LXI B,12
CALL MOVER
PUSH B
PUSH D
PUSH H
MVI C,SRCHN
LXI D,FCB
CALL BDOS
POP H
POP D
POP B
MFN02: INR A
STC
JNZ MFFIX1
STA MFFLG1
RET
MFFIX1: DCR A
ANI 3
ADD A
ADD A
ADD A
ADD A
ADD A
ADI 81H
MOV L,A
MVI H,0
PUSH H ;SAVE NAME POINTER
LXI D,MFCUR+1
LXI B,11
CALL MOVER
POP H
LXI D,FCB+1
LXI B,11
CALL MOVER
XRA A
STA FCBEXT
STA FCBRNO
RET
;
;MULTI-FILE ACCESS WORK AREA
;
MFFLG1: DB 0 ;1ST TIME SW
MFREQ: DS 12 ;REQ NAME
MFCUR: DS 12 ;CURR NAME
;=====================================
;MOVE SUBROUTINE
MOVER: MOV A,M
STAX D
INX H
INX D
DCX B
MOV A,B
ORA C
JNZ MOVER
RET
;================================
DIRLST: LXI D,CMDBUF ;PUT COMMAND LINE IN FCB
LXI H,5CH
CALL CPMLINE
LXI H,SRCHFCB
CALL INITFCBS
LDA 6CH ;GET DRIVE #
STA SRCHFCB
LDA 6DH
CPI 20H ;IF BLANK GET ALL NAMES
PUSH PSW
CZ QSTMARK
POP PSW
CNZ MOVENAME ;ELSE MOVE NAME INTO FCB
CALL DRIVE
LXI D,80H
MVI C,STDMA
CALL BDOS
XRA A
STA DNAMECT ;CR AFTER 4 NAMES
LXI D,SRCHFCB
MVI C,SRCHF ;DO FIRST SEARCH
CALL BDOS
CPI 0FFH
JZ NOFILE
DIRLP: CALL GETADD
LXI D,15 ;OFFSET FOR RECORD COUNT
DAD D
MOV A,M
ORA A
JZ NEXTSR ;NO LIST IF FILE IS ZERO LENGTH
LXI D,-5
DAD D ;POINT TO $SYS ATTRIB BYTE
MOV A,M
ANI 80H
JNZ NEXTSR ;NO LIST IF $SYS FILE
LXI D,-10
DAD D ;POINT TO BEGINNING OF NAME
INX H ;POINT TO FIRST LETTER
LXI D,PRNTNAME
MVI B,8
CALL MOVE
INX D
MVI B,3
CALL MOVE
CALL ILPRT
PRNTNAME:
DB ' ',' : ',0 ;12 SPACES
LDA DNAMECT
INR A
STA DNAMECT
ANI 03H
ORA A
CZ CRLF
NEXTSR: LXI D,SRCHFCB
MVI C,SRCHN ;DO NEXT SEARCH
CALL BDOS
CPI 0FFH
JZ DIRDONE
JMP DIRLP
NOFILE: CALL ILPRT
DB 'NOT FOUND',0
DIRDONE:CALL CRLF
RET
QSTMARK:MVI A,'?' ;IF BLANK IN FCB, PUT IN 11 ?'s
MVI B,11
LXI H,SRCHFCB+1
QSTLP: MOV M,A
INX H
DCR B
JNZ QSTLP
RET
MOVENAME:
LXI H,6DH
LXI D,SRCHFCB+1
MVI B,11
CALL MOVE ;MOVE IN CP/M PROGRAM
RET
GETADD: ANI 03H ;GET MOD4 FOR CP/M 1.4
ADD A
ADD A
ADD A ;ADD 32
ADD A
ADD A
MOV E,A
MVI D,0
LXI H,80H ;ADD DMA OFFSET
DAD D
RET
DRIVE: LDA SRCHFCB ;IF NO DRIVE, CAL
ORA A ;LOGGED IN DRIVE
JZ CALCDR
ADI 40H
JMP PRNTHD
CALCDR: MVI C,25
CALL BDOS
ADI 41H
PRNTHD: STA DRNAME
CALL ILPRT
DB CR,LF,'DRIVE '
DRNAME: DB ' ',CR,LF,0
RET
SRCHFCB:DS 33
DNAMECT:DS 1
;================================
NFILFLG:DB FALSE ;Terminal file open flag
OPTION: DB 0 ;primary option
OPTBL EQU $
QFLG: DB 'Q' ;secondary option flags
RSEEFLG:DB 'R'
SSEEFLG:DB 'S'
VSEEFLG:DB 'V'
TERMFLG:DB 'T'
BATCHFLG:DB 'N' ;0=Non-batch
OPTBE EQU $
RESTROPT: ;MUST BE IN SAME ORDER AS TABLE ABOVE
DB 'Q','R','S','V','T','N'
CRCFLG: DB 'C' ;use CRC instead of cksum
RESTSN: DB 0,0,0,0,0
DW DBUF
DB 0,0,0,0,0,0,0
SECNOB EQU $
RCVSNO: DB 0
SECNO: DW 0 ;sector no.
ERRCT: DB 0
EOFLG: DB 0 ;end of file flag
SECPTR: DW DBUF
SECINBF: DB 0
MAXEXT: DB 0
RCNT: DW 0 ;no. of records to send
DATAFLG: DB 0
EXACFL: DB 0 ;literalise next char flag
COLCNT: DB 0 ;column counter
SECNOE EQU $
FSTFLG: DB TRUE ;multiple file transmit flag
FIRSTME:DB 0FFH ;first SOH received switch(it is zero after 1st SOH)
LSTFLG: DB FALSE ;true=print echo on
LSTRET: DB FALSE ;print toggle save
BUFBEG: DW PRTBUF ;start of prtbuf
BUFRIN: DW PRTBUF ;i/p pointer
BUFROUT:DW PRTBUF ;o/p pointer
BUFEND: DW PRTBUF+(RING*1024) ;end of prtbuf
CMDBUF: DB 80H,0
DS 80H
HLSAVE: DS 2 ;memory pointer, terminal mode
DISKNO: DS 1
SENDFLG:DS 1
NBSAVE: DS 2
BGNMS: DS 2
FILECT: DS 1
NAMECT: DS 1
DS 64
STAK: DS 2
FCB3: DS 33 ;terminal file fcb
FCB4: DS 33 ;file transfer fcb
FCBBUF: DS 15
DBUF: DS DBUFSIZ*1024 ;FILE BUFFER FOR SEND & RECIEVE MODES
NAMEBUF:DS 64*12 ;BUFFER FOR FILE NAMES IN BATCH MODE.
ORG $ + 100H AND 0FF00H ;must be on page boundary
CRCTBL: DS 512 ;crc lookup table
PRTBUF: DS RING*1024 ;print buffer
BOTTRAM:DS 1 ;MEMORY BUFFER FOR TERMINAL FILE
END 100H