TITLE 'PACKET TRANSMISSION / RECEPTION PROGRAM'
; 11-8-80
; LAST MODIFIED 4-12-81
;
;WRITTEN BY DALE A. HEATHERINGTON, WA4DSY
; 3126 FLAMINGO DR.
; DECATUR, GA 30033
; tel: 404-292-1499
;
;***************************************************************
;This program has not been perfected and probably has a few bugs
;in it. It is for the 2.2 version of CP/M only. At this time
;(4-19-81) it has been used by only 2 stations in the Atlanta Ga.
;area so I have no idea what will happen when several stations
;get into a "pile up" . The program could become confused. There
;are several fields in the packet which have no function now and
;may be changed later. There are some packet types which have not
;been implemented ie: FS (file seperator). Also beware of comments
;;which don't match the code.....
;Consider this version (1.8) to be in PUBLIC DOMAIN for non-commercial
;use and distribution. This might not be true for future versions.
;********************************************************************
;THIS PROGRAM MUST BE ASSEMBLED WITH THE
;CP/M RELOACATING MACRO ASSEMBLER (RMAC) AND THE
;MACRO LIBRARY "SEQIO.LIB" MUST BE AVALABLE.
;NOTE: SEQIO.LIB WILL NOT WORK WITH RMAC UNLESS
;MODIFICATIONS ARE MADE.
MACLIB SEQIO2 ;MODIFIED SEQIO.LIB
VERSION EQU '1'
PTVER EQU '8' ;VERSION 1.8
ON EQU 1
OFF EQU 0
;
;--------------------------------------------------------;
;
; PACKET LAYOUT
;
; BYTE # BYTES DESCRIPTION
; 1 4 LEADER FF,7FH,1FH,7
; 5 1 SOH START OF HEADER
; 6 2 TOTAL LENGTH OF MESSAGE (STARTING AT CRC)
; 8 2 CRC BYTES (LOW BYTE FIRST)
; 10 6 DESTINATION CALL SIGN
; 16 1 DESTINATION PHYSICAL LOCATION CODE
; 17 1 DESTINATION REPEATER # (0 = DIRECT)
; 18 6 SOURCE CALL SIGN
; 24 1 SOURCE PHYSICAL LOCATION CODE
; 25 1 SOURCE REPEATER # (0= DIRECT)
; 26 1 PACKET TYPE (ENQ,ACK,NAK,FS,DTA,FRQ,EOT)
; 27 12 PACKET NAME (CP/M FILE NAME)
; 39 2 PACKET NUMBER ( 0..65535 )
; 41 0..1024 DATA BYTES
;
;NOTES: CRC IS CRC-16 REVERSE X^16+X^14+X+1 (SAME AS CLINK 3.0)
;
; THE PACKET NAME CONFORMS TO CP/M FILE CONTROL BLOCK FORMAT
; THE FIRST CHARACTER IS THE DRIVE INDICATOR (0=DEFAULT DRIVE)
; THE NEXT 8 ARE THE FILE NAME PADDED WITH TRAILING BLANKS.
; THE LAST 3 ARE THE FILE TYPE
;
; ALL PACKETS TYPES EXCEPT DATA HAVE A PACKET NUMBER OF ZERO.
; DATA PACKETS START WITH PACKET NUMBER 1 AND ARE TRANSMITTED
; IN SEQUENCE.
;
; FOUR RETRYS ARE ALLOWED IF PACKETS ARE "NAK'ED"
;
; PACKET TYPES
;
; ENQ REQUEST TO ESTABLISH COMMUNICATION AND SEND A FILE
; FRQ REQUEST A FILE
; FNF FILE NOT FOUND
; DTA PACKET CONTAINS DATA
; EOT END OF TRANSMISSION
; FS FILE SEPERATOR
; ACK ACKNOWLEDGE RECEIVING PACKET
; NAK DID NOT RECEIVE PACKET
; NAV RECEIVED ENQ BUT NOT AVALABLE TO RECV. DATA
;
;---------------------------------------------------------------------;
SYN EQU 255
DTA EQU 0
SOH EQU 1
FRQ EQU 3
EOT EQU 4
ENQ EQU 5
ACK EQU 6
FNF EQU 7
NAV EQU 8
NAK EQU 15H
FS EQU 1CH
NULL EQU 0
CR EQU 0DH
LF EQU 0AH
BDOS EQU 5
FCB EQU 5CH
CPMBOOT EQU 0
CRCPLY EQU 8005H
HDRLEN SET TDATA-TCRC ;HEADER LENGTH
; CRC TO DATA
;ENTRY LOCATIONS IN THIS MODULE
PUBLIC RECVIT
PUBLIC SENDIT
;EXTERNAL ENTRY LOCATIONS
EXTRN CPUCLK ;CPU CLOCK RATE IN MHZ
EXTRN INITX ;INITIALIZE FOR XMITTING
EXTRN INIRX ;INITIALIZE FOR RECEIVING
EXTRN SENDCHR ;SEND A BYTE (IN REG. C )
EXTRN RECVCHR ;GET A BYTE (IN REG. A)
EXTRN RXSTAT ;RETURN NON ZERO IF BYTE READY
EXTRN RFON ;KEY TRANSMITTER
EXTRN RFOFF ;UNKEY TRANSMITTER
EXTRN CKCD ;RETURN NON ZERO IF CARRIER
EXTRN TONEON ;AUDIO SUBCARRIER ON
EXTRN TONEOFF ;AUDIO SUBCARRIER OFF
EXTRN WAIT1 ;WAIT 1 MILLISECOND
EXTRN MYCALL ;USERS CALL SIGN
EXTRN MYLOC ;USERS LOCATION CODE
EXTRN QUIT ;PATH BACK TO CP/M
;--------------------------------------------------------
;HERE ARE SOME STRING CONSTANTS
WHO$R$U: DB '????????????' ;12 QUESTION MARKS
NOFN: DB 0,' ' ;0+11 BLANKS (A BLANK FILE NAME)
DIRFILE:DB 0,'DIR ' ;SPECIAL CASE FOR DIRECTORY
SYNCS: DB 0FFH,07FH,01FH,7,SOH ;PREAMBLE FOR PACKETS
;----------------------------------------------------------
;CODE STARTS HERE
INIBUFFR: ;INITIALIZE TX BUFFER LEADER
LXI H,SYNCS
LXI D,TBUFFR
MVI B,5
CALL MOVE
RET
CLEARBUF: ;CLEARS RECEIVE BUFFER HEADER
MVI B,RDATA-RBUFFR ;LENGTH OF HEADER
LXI H,RBUFFR ;RECEIVE HEADER
LXI D,RBUFFR+1
JMP MOVE ;CLEAR RECEIVE HEADER
;THIS IS THE FILE TRANSMITTING ROUTINE
SENDIT: MVI A,0
STA ID$INH ;DONT INHIBIT NEXT CW ID
SENDIT2:CALL INIBUFFR
LXI H,MYCALL
MVI B,6
CALL PRNMEM ;PRINT OUR CALL SIGN
CALL PRINT
DB ' PACKET TRANSMISSION PROGRAM VER. ',VERSION,'.',PTVER,CR
CALL PRINT
DB 'CPU CLOCK(MHZ) = @'
LDA CPUCLK
ADI 30H
MOV C,A
CALL CONOUT ;PRINT CPU CLOCK CONSTANT
CALL CRLF
LXI H,HDRLEN ;GET LENGTH OF ENQ PACKET
SHLD TLEN ;PUT IT IN BUFFER
MVI A,ENQ
STA TPTYPE ;PUT PACKET TYPE IN BUFFER
LXI H,FCB+17 ;GET DESTINATION CALL SIGN
LXI D,TDSTCAL
MVI B,6
CALL MOVE
MVI A,4 ;GET LOCATION CODE
STA TDSTLOC
MVI A,0 ;NON REPEATER
STA TDSTRPT
LXI H,MYCALL ;GET OUR CALL SIGN
LXI D,TSRCCAL
MVI B,6
CALL MOVE ;PUT IT IN XMIT BUFFER
LDA MYLOC ;GET OUR LOCATION CODE
STA TSRCLOC ;PUT IT IN BUFFER
MVI A,0
STA TSRCRPT ;REPEATER # IS ZERO
LXI H,FCB ;GET FILE NAME
LXI D,TTITLE
MVI B,12
CALL MOVE ;PUT IT IN BUFFER
SUB A
STA TTITLE ;DESTINATE IT TO DEFAULT DRIVE
LXI H,0
SHLD TPACNUM ;ZERO THE PACKET NUMBER
LXI H,TTITLE+1 ;SEE IF DIRECTORY REQUEST "DIR"
LXI D,DIRFILE+1
MVI B,11
CALL COMPARE$
JNZ REALFILE ;JUMP IF NOT DIRECTORY REQUEST
MVI A,255
STA DIRFLAG ;SET DIRECTORY FLAG
CALL OPEN$DIR ;SET UP DIRECTORY ROUTINE
JMP FIRSTID ;START SENDING
REALFILE:
MVI A,0
STA DIRFLAG ;CLEAR DIRECTORY FLAG
FILE SETFILE,CHECK,,1,,
DIRECT CHECK ;SEE IF THE FILE IS THERE
JNZ GOTIT ;JUMP IF WE FOUND IT
CALL PRINT
DB 'FILE NOT FOUND',CR
MVI A,1 ;RETURN A=1 IF NOT FOUND
ORA A
RET
GOTIT: FILE INFILE,SOURCE,,1,,1024,INBUFFER
FIRSTID:
CALL CWID ;ID IN (YUK) MORSE CODE FOR FCC
CALL SENDPAC ;SEND THE BUFFER CONTENTS (ENQ PACKET)
JZ OK2SEND
CALL NORPLY ;SAY HE'S NOT HOME
RET ;QUIT
OK2SEND:LXI H,1 ;SET PACKET NUMBER TO 1
SHLD TPACNUM
TXLP:
LXI H,1024 ;MAX DATA LENGTH
SHLD DATACNT
LXI H,TDATA ;POINTER TO XMIT DATA BUFFER
SHLD DATAPTR
LXI H,HDRLEN ;RESET LENGTH FIELD
SHLD TLEN
FILBUF:
LDA DIRFLAG
ORA A ;DIRECTORY OR FILE??
JZ FLB1 ;JMP IF FILE
;ELSE...
CALL GETABYTE ;GET A BYTE FROM DIRECTORY
JMP FLB2
FLB1: GET SOURCE ;GET A BYTE FROM SOURCE FILE
FLB2: JZ DONE
LHLD DATAPTR
MOV M,A ;PUT IT IN BUFFER
INX H ;ADVANCE POINTER
SHLD DATAPTR ;SAVE IT
LHLD TLEN
INX H ;ADVANCE LENGTH FIELD
SHLD TLEN
LHLD DATACNT
DCX H
SHLD DATACNT
MOV A,L
ORA H ;SEE IF WE DID 1024 BYTES
JNZ FILBUF
MVI A,DTA
STA TPTYPE ;PACKET TYPE IS DATA
CALL SENDPAC ;NOW TRANSMIT THE PACKET
JNZ ERRR
LHLD TPACNUM
INX H
SHLD TPACNUM ;COUNT THE PACKET
MOV A,L
DCR A
ANI 7 ;ID IN CW EVERY 8TH PACKET
CZ CWID
JMP TXLP ;GO BACK TO DO ANOTHER PACKET
DONE:
LHLD TLEN ;SEE IF LENGTH IS ZERO
LXI D,-HDRLEN
DAD D
MOV A,L
ORA H
JZ SNDEOT ;IF ZERO LENGTH JUST DO EOT
MVI A,DTA ;ELSE SEND SHORT DATA PACKET
STA TPTYPE
CALL SENDPAC
JNZ ERRR ;QUIT IF BAD OR NO RESPONSE
SNDEOT: MVI A,EOT
SND2: STA TPTYPE
LXI H,HDRLEN ;
SHLD TLEN ;SET LENGTH
LXI H,0 ;CONTROL PACKETS HAVE NUMBER 0
SHLD TPACNUM
CALL SENDPAC ;SEND LAST PACKET
PUSH PSW
CALL CWID ;LET FCC KNOW WHO WE ARE
POP PSW
JNZ ERRR
CALL PRINT
DB 'ALL PACKETS SENT',CR
XRA A ;RETURN ZERO IF OK
RET
ERRR: CALL PRINT
DB 'TRANSMISSION ABORTED',7,CR
MVI A,255
ORA A
RET ;RETURN NON ZERO IF ERROR
;THIS IS THE FILE REQUEST ROUTINE
;COME HERE IF PACKET IS REQUESTING A FILE FROM US.
;
FLREQ: CALL CWID ;ID IN CRUDE MORSE CODE
MVI A,ACK
STA TPTYPE
CALL SENDAPAC ;ACK HIS FILE REQUEST
LXI H,RSRCCAL ;GET HIS CALL SIGN
LXI D,FCB+17
MVI B,6
CALL MOVE ;MOVE IT TO FCB
MVI A,1
STA ID$INH ;INHIBIT NEXT CW ID
CALL SENDIT2 ;SEND THE FILE IF WE HAVE IT
RZ ;QUIT NOW IF FILE WAS SENT
CPI 1 ;SEE IF THE FILE WAS NOT IN DIR
RNZ ;RETURN IF JUST BAD SIGNALS
MVI A,FNF ;ELSE SEND FILE NOT FOUND PACKET
STA TPTYPE
LXI H,HDRLEN
SHLD TLEN ;INIT HEADER LENGTH
LXI H,0
SHLD TPACNUM ;SET PACKET NUMBER TO ZERO
CALL SENDAPAC ;SEND THE FNF PACKET
CALL CWID
XRA A
RET ;ALL DONE
;THIS SENDS A FILE REQUEST PACKET TO THE OTHER GUY IF
;WE WANTED TO GET A FILE FROM HIM.
;
SENDFRQ:
LXI H,HDRLEN
SHLD TLEN
MVI A,FRQ
STA TPTYPE
LXI H,MYCALL
LXI D,TSRCCAL
MVI B,6 ;SET UP SOURCE CALL
CALL MOVE
LXI H,FCB+17 ;GET DESTINATION CALL
LXI D,TDSTCAL
MVI B,6
CALL MOVE
MVI A,0
STA TSRCRPT
MVI A,14H ;DEST. LOCATION CODE
STA TDSTLOC
LDA MYLOC
STA TSRCLOC
LXI H,FCB
LXI D,TTITLE ;GET FILE NAME
MVI B,12
CALL MOVE
LXI H,0
SHLD TPACNUM ;CLEAR PACKET NUMBER
CALL CWID
CALL SENDPAC ;SEND FRQ PACKET AND GET REPLY
RET ;RETURN ZERO WITH PACKET TYPE IN ACC
;-------------------------------------------------------------------
; TITLE 'DIRECTORY LISTER'
;THIS IS A SELF CONTAINED SUBROUTINE WHICH WILL
;RETURN THE CP/M DIRECTORY A BYTE AT A TIME BY
;CALLING "GETABYTE".
;THE ZERO FLAG WILL BE SET AFTER THE LAST BYTE
;IS RETURNED.
;THE SUBROUTINE MUST BE INITIALIZED BY
;CALLING "OPEN$DIR" FIRST.
SETDMA EQU 26
SEARCH EQU 17
SEARCH$NEXT EQU 18
GET$ALLOC EQU 27
GET$DPBLK EQU 31
DIRFCB: DB 0 ;DR
DB '???????????',0
DS 20
PWR10: DW -1000
DW -100
DW -10
DW -1
DSEG ;DATA SEGMENT
N1: DS 0
SWITCH: DS 1
KLEFT: DS 2 ;K BYTES LEFT ON DISK
ENCNT: DS 1
COUNTER:DS 1
DPBADR: DS 2 ;ADDRESS OF DISK PARAMETER BLOCK
POINTER:DS 2
ENTBUF: DS 16 ;BUFFER FOR 1 DIR. ENTRY
BUFFER: DS 128
;-------------------------------------------------------------
CSEG ;CODE SEGMENT
;RETURNS HL POINTING TO NEXT DIRECTORY ENTRY
;A=ZERO IF OK OR 255 IF NO MORE ENTRYS
GET$ENTRY:
LDA SWITCH
ORA A
JZ SNXT ;DO SEARCH NEXT IF ZERO
MVI C,SETDMA
LXI D,BUFFER
CALL BDOS ;SET UP DIRECTORY BUFFER
LXI D,DIRFCB
MVI C,SEARCH ;SEARCH FOR FIRST ENTRY
CALL BDOS
JMP GE2
SNXT: LXI D,DIRFCB
MVI C,SEARCH$NEXT
CALL BDOS ;SEARCH FOR NEXT ENTRY
GE2: CPI 255 ;DONE?
RZ
ADD A ;MPY BY 32
ADD A
ADD A
ADD A
ADD A
MOV E,A
MVI D,0
LXI H,BUFFER
DAD D
XRA A
STA SWITCH ;CLEAR THE SWITCH
RET ;A=0 HL->ENTRY
;-------------------------------------------------------------
OPEN$DIR: ;INITIALIZE DIRECTORY LISTER
MVI A,255
STA SWITCH
MVI A,4 ;INIT ENTRY COUNTER
STA ENCNT
MVI A,1
STA COUNTER
LXI H,ENTBUF
SHLD POINTER
;AT NO EXTRA CHARGE WE NOW GIVE YOU THE NUMBER OF K BYTES
;LEFT ON THE DISK... RETURNED IN HL
MVI C,GET$DPBLK ;GET DISK PRAM. BLOCK ADDRESS
CALL BDOS
SHLD DPBADR ;SAVE IT
LXI D,5
DAD D ;POINT TO DISK CAPACITY
MOV E,M ;GET IT
INX H
MOV D,M
PUSH D ;SAVE IT
MVI C,GET$ALLOC
CALL BDOS ;GET ADDRESS OF ALLOCATION MAP
POP B
PUSH B ;GET AND SAVE DISK CAPACITY
LXI D,0 ; BLOCK COUNTER
CNTK: MOV A,M ;GET 8 ALLOCATION UNIT BITS
PUSH H ;SAVE POINTER
MVI L,8
MOV H,A ;BITS OF ALLOCATION TO H
CBITS: MOV A,H
ADD A ;SHIFT OVER A BIT
MOV H,A
JNC ZEROB
INX D ;COUNT THE ONE'S
ZEROB: DCX B ;COUNT A BLOCK
MOV A,C
ORA B
JZ NOMORE
DCR L ;COUNT BITS
JNZ CBITS ;LOOP 8 TIMES
POP H ;GET POINTER BACK
INX H
JMP CNTK ;LOOP
NOMORE: POP H
MOV A,E ;TWOS COMPLIMENT BLOCKS USED
CMA
MOV E,A
MOV A,D
CMA
MOV D,A
INX D
POP H ;GET MAX DISK CAPACITY
DAD D ;ADD THE -KBYTES USED
INX H ;CORRECTION
PUSH H ;PUSH BLOCKS USED ON STACK
LHLD DPBADR ;GET ADDRESS OF DISK PARAMETER BLOCK
LXI D,2
DAD D ;POINT TO BLOCK SHIFT FACTOR
MOV A,M ;GET IT
SUI 3 ;REMOVE BIAS
POP H ;GET BLOCKS LEFT
KLOOP: ORA A ;TEST FOR ZERO
JZ KDONE
DAD H ;SHIFT HL
DCR A
JMP KLOOP
KDONE: SHLD KLEFT ;SAVE THE NUMBER
RET
;-------------------------------------------------------------
GETABYTE: ;GET 1 CHARACTER OF DIRECTORY
LXI H,COUNTER
DCR M
JZ ANOTHER
LHLD POINTER
MOV A,M
INX H
SHLD POINTER
ORA A ;RETURN NON ZERO IF NOT DONE
RET
ANOTHER:
LXI H,ENTBUF ;CLEAR ENTRY BUFFER POINTER
SHLD POINTER
CALL GET$ENTRY
CPI 255
JZ PUTK ;DONE, SAY HOW MANY BYTES LEFT NOW
MVI B,8
LXI D,ENTBUF
AN1: INX H
MOV A,M ;MOVE FILE NAME TO BUFFER
STAX D
INX D
DCR B
JNZ AN1
MVI A,' '
MVI B,4
AN2: STAX D
INX D
INX H
MOV A,M
DCR B
JNZ AN2
XCHG
LDA ENCNT ;GET ENTRY COUNT
DCR A
JNZ AN3 ;END OF LINE IF ZERO
CALL EOL
JMP GETABYTE
AN3: STA ENCNT
MVI M,' ' ;ELSE PUT UP FENCE
INX H
MVI M,':'
INX H
MVI M,' '
MVI A,16
STA COUNTER
JMP GETABYTE
EOL: MVI M,0DH
INX H
MVI M,0AH
INX H
MVI A,15
STA COUNTER
MVI A,4
STA ENCNT
RET
PUTK:
LXI H,ENTBUF ;PUTS "XXXX K LEFT" IN BUFF
CALL EOL
SHLD POINTER
LHLD KLEFT
CALL DECONV ;CONVERT K TO DECIMAL & PUT IN BUFFER
LHLD POINTER
LXI D,KMSG
MVI B,11
PKLP: LDAX D ;PUT IN " K LEFT "
MOV M,A
INX D
INX H
DCR B
JNZ PKLP
LXI H,ENTBUF
SHLD POINTER
MVI A,17
STA COUNTER
JMP GETABYTE
KMSG: DB ' K LEFT',0DH,0AH,0
;--------------------------------------------------------------
;CONVERT NUMBER IN HL TO DECIMAL STRING AND PUT IN BUFFER
;USING "POINTER"
DECONV:
SHLD N1
LXI H,PWR10
DECV0: MOV E,M
INX H
MOV D,M ;GET POWER OF 10
INX H
PUSH H
LHLD N1
MVI C,'0'
DECV1: DAD D ;SUB POWER OF 10 FROM N1
JNC DECV2
INR C
JMP DECV1 ;LOOP TIL IT GOES NEG.
DECV2: MOV A,E
CMA
MOV E,A ;TWOS COMPLIMENT
MOV A,D
CMA
MOV D,A
INX D
DAD D ;RESTORE TO +
SHLD N1
LHLD POINTER
MOV M,C ;OUTPUT DIGIT TO BUFFER
INX H
SHLD POINTER
POP H
MVI A,1
CMP E ;DONE YET?
JNZ DECV0
RET
;END OF DIRECTORY LISTER
;--------------------------------------------------------------------
;
;THIS IS THE FILE RECEIVE ROUTINE
;
;
RECVIT:
MVI A,0
STA ID$INH ;DONT INHIBIT NEXT CW ID
CALL INIBUFFR
LXI H,MYCALL
MVI B,6
CALL PRNMEM ;PRINT OUR CALL SIGN
CALL PRINT
DB ' PACKET RECEPTION PROGRAM VER. ',VERSION,'.',PTVER,CR
CALL PRINT
DB 'CPU CLOCK(MHZ)= @'
LDA CPUCLK
ADI 30H
MOV C,A
CALL CONOUT
CALL CRLF
LDA 80H ;SEE IF FILE NAME WAS ENTERED
ORA A
JZ RCV0 ;JUMP IF NORMAL RECEIVE
CALL SENDFRQ ;DO FILE REQUEST IF LENGTH NON ZERO
RNZ ;RETURN IF BAD
CPI ACK ;SEE IF HE HAD THE FILE
RNZ ;RETURN AND QUIT IF NOT
MVI A,1
STA ID$INH ;ALL OK, INHIBIT NEXT CW ID
RCV0: LXI H,HDRLEN ;INIT THE TRANSMIT HEADER
SHLD TLEN
MVI A,ACK
STA TPTYPE ;BE OPTIMISTIC
LXI H,MYCALL
LXI D,TSRCCAL ;PUT IN OUR CALL
MVI B,6
CALL MOVE
LDA MYLOC ;GET LOCATION CODE
STA TSRCLOC
MVI A,0
STA TSRCRPT ;WE ARE NOT A REPEATER (YET)
STA TDSTLOC ;CLEAR THE DEST. LOC. CODE
LXI H,WHO$R$U ;INIT. THE DEST. CALL WITH ??????
LXI D,TDSTCAL
MVI B,6
CALL MOVE
LXI H,NOFN ;CLEAR THE FILE NAME FIELD
LXI D,TTITLE
MVI B,12
CALL MOVE
CALL PRINT
DB 'WAITING FOR ENQ PACKET',CR
LXI H,0
SHLD 80H ;CLEAR CP/M DEFAULT BUFFER
SHLD TPACNUM ;CLEAR PACKET NUMBER
LXI D,0 ;ZERO TELLS IT TO WAIT FOREVER
CALL RECVPAC ;GET A PACKET
JNZ RECVIT ;JUMP IF BAD
CPI FRQ ;REQUEST FOR FILE?
JZ RCV1 ;YES, THEN JUMP
CPI ENQ
JNZ RECVIT ;JUMP IF NOT ENQ
RCV1: LXI H,RSRCCAL ;POINT TO SOURCE CALL SIGN
LXI D,TDSTCAL ;MOVE IT TO DESTINATION
MVI B,6
CALL MOVE
CALL CKCALL ;SEE IF PACKET IS FOR US
JNZ RECVIT ;NO, GO BACK
LXI H,RTITLE ;GET FILE NAME TO FCB
LXI D,FCB
MVI B,12
CALL MOVE
LDA RPTYPE ;CHECK PACKET TYPE AGAIN
CPI FRQ ;FILE REQUEST?
JNZ RXD3 ;YUP, PROCESS THAT INSTEAD
CALL FLREQ
LXI H,80H ;POINT TO CP/M DEFAULT BUFFER
MVI M,0 ;CLEAR LENGTH BYTE
JMP RECVIT ;AND GO BACK AND WAIT FOR ENQ
RXD3: FILE OUTFILE,TEMP,,1,$$$,1024,OUTBUFFER ;OPEN THE TEMP. FILE
FILE SETFILE,DEST,,1,, ;SET UP FOR REAL FILE
LXI H,1 ;INIT THE SEQUENCE NUMBER
SHLD SEQNUM ;FOR THE FIRST PACKET
RXDLP2: CALL CWID ;WASTE TIME WITH PRIMITIVE MORSE CODE
RXDLP1: MVI A,ACK ;NOW DO MODERN ASCII STUFF
STA TPTYPE
CALL SENDAPAC ;SEND ACK PACK
RECVDATA:
LXI D,15000 ;SET FOR 15 SEC TIMEOUT
CALL RECVPAC ;GO GET A PACKET
JNZ BADPAC
CPI FS ;FS NOT REALLY IMPLIMENTED YET
JZ CLOSEIT ;BUT CLOSE FILE ANYWAY
CPI EOT
JZ CLOSEIT
CPI ENQ
JZ RXDLP1 ;ACK THE EXTRA ENQ
CPI DTA ;DATA?
RNZ
LHLD RLEN ;CHECK FOR ZERO LENGTH
LXI D,-HDRLEN
DAD D
MOV A,L
ORA H
JZ RXDLP1 ;DISCARD ZERO LENGTH DATA PACKET
LHLD SEQNUM ;CHECK FOR DUPLICATE PACKET
LDA RPACNUM
CMP L
JNZ DISCARD ;DISCARD IF OUT OF SEQUENCE
LDA RPACNUM+1
CMP H
JNZ DISCARD
INX H ;COUNT THIS PACKET
SHLD SEQNUM ;UPDATE SEQUENCE NUMBER
LXI D,-HDRLEN
LHLD RLEN ;GET LENGTH
DAD D ;DATA LENGTH=TOTAL-HEADER
SHLD DATACNT ;SAVE DATA COUNT
LXI H,RDATA ;DATA BUFFER POINTER
SHLD DATAPTR ;SAVE IT
RXDLP: LHLD DATAPTR
MOV A,M ;PUT BUFFER CONTENTS ON DISK
INX H
SHLD DATAPTR
PUT TEMP ;WRITE TO TEMPORARY FILE
LHLD DATACNT
DCX H
SHLD DATACNT ;COUNT BYTES
MOV A,L
ORA H
JNZ RXDLP ;LOOP TILL ZERO
LDA SEQNUM ;GET LOW BYTE OF SEQUENCE NUMBER
ANI 7
JZ RXDLP2 ;DO CW ID EVERY 8TH ACK
JMP RXDLP1 ;SEND ACK AND DO IT AGAIN
DISCARD: CALL PRINT
DB 'EXTRA PACKET DISCARDED',CR
JMP RXDLP1
CLOSEIT:
FINIS TEMP
ERASE DEST
RENAME DEST,TEMP
MVI A,ACK
STA TPTYPE ;SEND FINAL ACK
CALL SENDAPAC
CALL CWID ;FINAL CW ID
CALL PRINT
DB 'FILE RECEIVED',CR
LXI H,80H
MVI M,0
JMP RECVIT ;GO WAIT FOR ENQ AGAIN
BADPAC: FINIS TEMP
ERASE TEMP
CALL PRINT
DB 'TOO MANY ERRORS, RECEPTION ABORTED',7,CR
LXI H,80H
MVI M,0
JMP RECVIT
;WAIT FOR A SYNC STREAM FOLOWED BY SOH
;"TIME" HAS TIMEOUT VALUE IN MS
;"TIME"= ZERO MEANS WAIT FOREVER
;
WAITSOH:
CALL RFOFF ;BE SURE WE DONT TRANSMIT
CALL INIRX ;INIT MODEM FOR RECEIVE
LHLD TIME
SHLD TEMPTIME ;GET TIMEOUT VALUE
WSOH3: CALL CKCRLC ;CHECK FOR CONTROL C
CALL COUNTDOWN ;DO TIMEOUT TEST
RZ ;RETURN ZERO IF TIMEOUT
CALL RXSTAT ;CHECK FOR A CHARACTER
ORA A
JZ WSOH3 ;LOOP BACK IF WE DONT HAVE ONE
LXI H,LEADBB+1 ;LEADIN BUFFER END POINTER
LXI D,LEADBB ;LEADIN START POINTER
MVI B,4 ;NUMBER OF BYTES TO SHIFT
CALL MOVE ;SHIFT BYTES OVER
LXI D,1 ;1 MS TIMEOUT (CHAR. THERE ALREADY)
CALL RECVCHR ;GET THE CHARACTER
STA LEADBE ;STORE AT END OF BUFFER
LXI H,LEADBB ;ADDRESS OF UNKNOWN STRING
LXI D,SYNCS ;THIS IS WHAT WERE LOOKING FOR
MVI B,5 ;WE WANT TO CHECK 5 CHARACTERS
CALL COMPARE$ ;NOW COMPARE SYNCS WITH UNKNOWN
JNZ WSOH3 ;NO MATCH, LOOP BACK
MVI A,SOH ;RETURN WITH SOH MEANS WE GOT IT
RET
COUNTDOWN:
LHLD TEMPTIME ;GET TIMEOUT VALUE
MOV A,L
ORA H ;PRETEST FOR ZERO
JZ NEVERMIND ;ZERO MEANS WAIT FOREVER
DCX H ;COUNT 1 MS
SHLD TEMPTIME ;SAVE NEW VALUE
CALL WAIT1 ;WAIT 1 MS
MOV A,L
ORA H
RET ;RET ZERO IF TIMED OUT
NEVERMIND:
MVI A,255
ORA A
RET ;PRETEND WE DIDN'T TIMEOUT
;CHECKS CONSOLE FOR CONTROL C TO ABORT
CKCRLC: CALL CONST ;CHECK CONSOLE
ORA A
RZ
CALL CONIN ;GET CONSOLE CHAR
CPI 3 ;CONTROL C?
RNZ
JMP QUIT
;---------------------------------------------------------
;
;RECEIVES 1 PACKET OF ANY TYPE TO ANY DESTINATION
;ENTER WITH DE CONTAINING THE TIMEOUT VALUE IN MILLISECONDS.
;IF DE=0 THE TIMEOUT IS FOREVER
;IT WILL TRY 4 TIMES TO HEAR THE PACKET BEFORE GIVING UP.
;RETURNS WITH PACKET TYPE IN REG. A WITH ZERO FLAG SET.
;RETURNS NON-ZERO IF ERROR.
;
RECVPAC:
XCHG
SHLD TIME ;SET TIMEOUT VALUE
MVI A,4 ;SET RETRY COUNTER
STA RETRYS
RP1: CALL RECVAPAC ;GET 1 PACKET
JNZ RP4 ;JUMP IF ERROR
XRA A
LDA RPTYPE ;A HAS PACKET TYPE
RET ;EXIT
RP4:
LDA RETRYS ;COUNT THE RETRYS
DCR A
STA RETRYS
JNZ RP3
INR A
RET ;RETURN NON ZERO IF ERROR
RP3: CALL CKCALL ;SEE IF IT WAS FOR US
JNZ RP1 ;DONT NAK IF NOT FOR US
MVI A,NAK
STA TPTYPE
CALL SENDAPAC ;SEND NAK PAC
JMP RP1 ;GO BACK AND TRY AGAIN
;GETS 1 PACKET (NO RETRYS, SEE RECVPAC ABOVE)
;RETURNS ZERO FLAG SET WITH PACKET TYPE IN REG. A
;IF NO ERRORS
;"TIME" HAS TIMEOUT VALUE... 0=FOREVER
RECVAPAC:
CALL CLEARBUF ;CLEAR RECEIVE HEADER
CALL WAITSOH ;WAIT FOR NEW LEADER
CPI SOH
JNZ TOOLONG ;MUST BE TIMEOUT IF NO SOH
LXI H,HDRLEN+2 ;TOTAL LENGTH OF HEADER
SHLD RLEN ;INITIAL LENGTH
LXI H,RCRC ;SET UP BUFFER POINTER
SHLD DATAPTR
LXI D,100 ;100 MS TIMEOUT
CALL RECVCHR ;TRY TO GET A CHAR
JC TOOLONG
STA RLEN ;GET LOW LENGTH BYTE
CALL RECVCHR
JC TOOLONG
STA RLEN+1 ;HIGH LENGTH BYTE
LHLD RLEN ;GET COUNT
SHLD DATACNT ;SAVE IT
CPI 5 ;SEE IF LENGTH TOO BIG
JNC TOOBIG
RCVLP: CALL RECVCHR ;START GETTING REST OF DATA NOW
JC TOOLONG
LHLD DATAPTR
MOV M,A ;PUT A BYTE IN PACKET BUFFER
INX H
SHLD DATAPTR
LHLD DATACNT ;COUNT THE BYTES
DCX H
SHLD DATACNT
MOV A,L
ORA H
JNZ RCVLP ;LOOP FOR MORE BYTES
CALL RXCRC ;CHECK RECEIVED CRC
JNZ CRCERR ;JUMP IF ERROR
CALL PRNRXHEADER ;PRINT WHAT WE RECEIVED
LDA RPTYPE ;GET PACKET TYPE
CMP A ;SET ZERO FLAG
RET
CRCERR: CALL PRINT
DB '** CRC ERROR **',7,CR
XRA A
INR A
RET ;RETURN NON ZERO DUE TO ERROR
;COME HERE AFTER TIMEOUT ERROR
;
TOOLONG:
CALL PRINT
DB '** TIMEOUT ERROR **',7,CR
XRA A
INR A ;RETURN NON ZERO
RET
TOOBIG:
CALL PRINT
DB '** PACKET TOO BIG FOR BUFFER **',7,CR
XRA A
INR A
RET
;
;
;
;
;----------------------------------------------------------
;THIS IS THE PACKET TRANSMISSION SECTION
;SEND 1 PACKET AND WAIT FOR REPLY
;AND DO IT UP TO 4 TIMES IF IT GETS A NAK BACK
;OR A TIMEOUT
SENDPAC:MVI A,4 ;RETRY COUNTER
STA RETRYS
AGAIN: CALL SENDAPAC ;SEND THE PACKET
CALL INIRX ;SET UP FOR RECEIVE
LXI H,23000 ;23 SEC TIMEOUT
SHLD TIME
CALL RECVAPAC ;GET REPLY PACKET
JNZ SNDP1 ;ERROR IF NON ZERO
CPI NAK
JZ SNDP1 ;TRY AGAIN IF NAK
CMP A ;SET ZERO FLAG
RET ;RETURN PACKET TYPE IN ACC
SNDP1: LDA RETRYS
DCR A
STA RETRYS
JZ SP2
CALL PRINT
DB 'RETRANSMITTING',CR
JMP AGAIN ;TRY AGAIN
SP2: MVI A,1 ;1=ERROR
ORA A
RET
;
;
;THIS SENDS 1 PACKET
;
SENDAPAC:
CALL INITX ;SET UP FOR TRANSMIT
LXI D,3000 ;3 SEC WAIT AFTER KEYDOWN
CALL RFON ;KEY TRANSMITTER
CALL TXCRC ;COMPUTE TRANSMIT CRC
CALL PRNTXHEADER ;TELL USER WHAT WERE SENDING
LHLD TLEN ;GET PACKET LENGTH
INX H
INX H
INX H
INX H
INX H ;ADD 7 FOR LEADER AND LENGTH BYTES
INX H
INX H
XCHG ;TRUE LENGTH IN DE
LXI H,TBUFFR ;POINT TO START OF BUFFER
SP1: MOV C,M ;GET A BYTE
CALL SENDCHR ;SEND IT
CALL CKCRLC ;ABORT IF CONTROL C TYPED
INX H
DCX D
MOV A,E
ORA D
JNZ SP1 ;LOOP TIL ALL BYTES SENT
LXI D,100
CALL DELAY ;100 MS TRAILER
CALL RFOFF ;KILL TRANSMITTER
RET
;
;
;
;---------------------------------------------------
;THIS MESS PRINTS THE HEADER INFORMATION TO THE CONSOLE
;FOR PACKETS TO BE SENT OR PACKETS WHICH HAVE BEEN RECEIVED.
PRNTXHEADER:
LXI H,TBUFFR
LXI D,BUFFR
MVI B,TDATA-TBUFFR
CALL MOVE ;GET TRANSMIT HEADER
CALL PRINT
DB 'TX-@'
CALL PRINFO
RET
PRNRXHEADER:
LXI H,RBUFFR
LXI D,BUFFR
MVI B,RDATA-RBUFFR
CALL MOVE ;GET RECEIVER HEADER
CALL PRINT
DB 'RX-@'
;
PRINFO:
LHLD LEN
CALL BXDEC ;PRINT LENGTH
CALL PRINT
DB ' BYTES #@'
LHLD PACNUM
CALL BXDEC ;PRINT PACKET NUMBER
CALL PRINT
DB ' @'
LXI H,SRCCAL
MVI B,6
CALL PRNMEM ;PRINT SOURCE CALL
CALL PRINT
DB ' -> @'
LXI H,DSTCAL
MVI B,6
CALL PRNMEM ;PRINT DESTINATION CALL
CALL PRINT
DB ', @'
LXI H,STITLE+1
MVI B,11
CALL PRNMEM ;PRINT FILE NAME
CALL PRINT
DB ' = @'
CALL PRTYPE
JMP CRLF
PRTYPE: LDA PTYPE
CPI DTA
JZ TPDTA
CPI ENQ
JZ TPENQ
CPI ACK
JZ TPACK
CPI NAK
JZ TPNAK
CPI FS
JZ TPFS
CPI EOT
JZ TPEOT
CPI FRQ
JZ TPFRQ
CPI FNF
JZ TPFNF
CALL PRINT
DB 'UNKNOWN @'
RET
TPDTA: CALL PRINT
DB 'DATA @'
RET
TPENQ: CALL PRINT
DB 'ENQ @'
RET
TPACK: CALL PRINT
DB 'ACK @'
RET
TPNAK: CALL PRINT
DB 'NAK @'
RET
TPFS: CALL PRINT
DB 'FS @'
RET
TPEOT: CALL PRINT
DB 'EOT @'
RET
TPFRQ: CALL PRINT
DB 'FRQ @'
RET
TPDRQ: CALL PRINT
DB 'DRQ @'
RET
TPFNF: CALL PRINT
DB 'FNF @'
RET
;-----------------------------------------------------
CRLF: CALL PRINT
DB CR
RET
;COMPARES 2 STRINGS
;POINTED TO BY HL & DE (B HAS LENGTH)
;RETURNS ZERO FLAG SET IF EQUAL
;
COMPARE$:
LDAX D
CMP M
RNZ
INX H
INX D
DCR B
JNZ COMPARE$
XRA A
RET
;CHECKS THE RECEIVED CALL SIGN AGAINST OUR OWN
;RETURNS ZERO IF MATCHED.
CKCALL: LXI H,MYCALL
LXI D,RDSTCAL
MVI B,6
JMP COMPARE$
;
;PRINTS A STRING IN MEMORY POINTED TO BY HL
;B HAS LENGTH.
PRNMEM: MOV C,M
CALL CONOUT
INX H
DCR B
JNZ PRNMEM
RET
;
;SENDS A CHARACTER IN REG C TO CONSOLE
;
CONOUT: PUSH PSW
PUSH B
PUSH D
PUSH H
MOV E,C
MVI C,2
CALL BDOS
POP H
POP D
POP B
POP PSW
RET
;CHECK CONSOLE STATUS
;RETURN A=ZERO IF NO KEY PRESSED
;
CONST: PUSH B
PUSH D
PUSH H
MVI C,11 ;CP/M CONSOLE STATUS CODE
CALL BDOS
POP H
POP D
POP B
RET ;A=FF IF KEY PRESSED
;
;
;GETS A CHARACTER FROM CONSOLE
;A=CHAR.
;
CONIN: PUSH B
PUSH D
PUSH H
MVI C,1 ;READ CONSOLE CPM COMMAND
CALL BDOS
POP H
POP D
POP B
RET ;A=CHAR
;
;
;PRINT A STRING POINTED TO BY THE CONTENTS OF THE
;TOP OF STACK. OR @ TERMINATES THE STRING.
PRINT: XTHL
PUSH PSW
PUSH B
PRN1: MOV A,M
INX H
CPI '@'
JZ PRN2
MOV C,A
CALL CONOUT
CPI CR
JNZ PRN1
CALL PRINT
DB LF,'@'
PRN2: POP B
POP PSW
XTHL
RET
;CONVERTS BINARY NUMBER IN HL TO DECIMAL ASCII
;AND SENDS IT TO CONSOLE
BXDEC: SHLD N ;SAVE NUMBER
LXI H,P10TAB ;POINT TO POWER OF 10 TABLE
BX0: MOV E,M
INX H
MOV D,M ;POWER OF 10
INX H
PUSH H
LHLD N
MVI C,'0'
BX1: DAD D ;SUBTRACT POWER OF TEN
JNC BX2
INR C
JMP BX1 ;LOOP TILL IT GOES NEG.
BX2: MOV A,E
CMA ;TWOS COMPLIMENT
MOV E,A
MOV A,D
CMA
MOV D,A
INX D
DAD D ;RESTORE TO +
SHLD N ;SAVE IT
CALL CONOUT
POP H
MVI A,1
CMP E ;SEE IF WE ARE DONE
JNZ BX0 ;NO, DO AGAIN
RET
;
;
P10TAB: DW -10000
DW -1000
DW -100
DW -10
DW -1
;
;
;THIS TELLS THE USER THAT THERE WAS NO RESPONSE
NORPLY: LXI D,NOTHOME+2
LXI H,TDSTCAL
MVI B,6
CALL MOVE
LXI D,NOTHOME
MVI C,9
CALL BDOS
RET
NOTHOME:
DB CR,LF,' DOES NOT RESPOND',CR,LF,'$'
;GENERAL PURPOSE MEMORY TO MEMORY MOVE SUBROUTINE
;ENTER WITH HL POINTING TO SOURCE, DE POINTING TO
;DESTINATION AND B HAS THE LENGTH (0..255, 0=256 BYTES)
MOVE: MOV A,M
STAX D
INX H
INX D
DCR B
JNZ MOVE
RET
;
;ENTER THIS DELAY ROUTINE WITH DE CONTAINING
;THE DELAY VALUE IN MILLISECONDS.
DELAY: CALL WAIT1 ;WAIT 1MS * DE
DCX D
MOV A,E
ORA D
JNZ DELAY
RET
;THIS COMPUTES THE CRC FOR TRANSMISSION
TXCRC: LXI H,0
SHLD CRCREG ;CLEAR CRC
LDA TLEN
MOV C,A
CALL CALCRC ;COMPUTE CRC FOR LENGTH FIELD
LDA TLEN+1
MOV B,A ;BC HAS LENGTH NOW
CALL CALCRC
LXI H,TDSTCAL ;SKIP OVER CRC FIELD
DCX B
DCX B ;ADJUST LENGTH
TXCRC1: MOV A,M ;GET A BYTE FROM BUFFER
CALL CALCRC ;COMPUTE CRC ON IT
INX H ;ADVANCE BUFFER POINTER
DCX B ;COUNT THE BYTES
MOV A,C
ORA B
JNZ TXCRC1 ;LOOP UNTIL COUNTER IS ZERO
LHLD CRCREG ;GET COMPLETE CRC
SHLD TCRC ;PUT IT IN THE BUFFER
RET
;COMPUTE CRC ON RECEIVE BUFFER CONTENTS
;RETURNS A=0 IF OK
RXCRC: LHLD RLEN ;GET LENGTH
MOV C,L
MOV B,H ;BC IS COUNTER
LXI H,0
SHLD CRCREG ;CLEAR CRC
MOV A,C ;COMPUTE CRC ON LENGTH WORD
CALL CALCRC
MOV A,B
CALL CALCRC
LXI H,RDSTCAL ;HL POINT TO BUFFER & SKIP CRC FIELD
DCX B
DCX B ;ADJUST COUNT
RXCRC1: MOV A,M ;GET A BYTE
CALL CALCRC ;DO CRC CALC.
INX H
DCX B
MOV A,C
ORA B
JNZ RXCRC1 ;LOOP UNTIL DONE
LHLD CRCREG ;GET COMPLETE CRC
XCHG ;TO DE
LHLD RCRC ;GET RECEIVED CRC
MOV A,E
SUB L
RNZ ;RETURN NON ZERO IF BAD CRC
MOV A,D
SUB H
RET
;
;THIS COMPUTES THE CRC FROM VALUES IN REGISTER A AND
;MEMORY LOCATION "CRCREG".
CALCRC: PUSH B
PUSH H
MOV B,A ;SAVE BYTE
MVI C,8 ;BIT COUNTER
CALC1: ANI 80H ;KEEP MSB
LHLD CRCREG ;GET CRC
XRA H
MOV H,A ;XOR CRC INTO DATA
DAD H ;SHIFT CRC LEFT
JNC CALC2
MOV A,H
XRI CRCPLY SHR 8 ; HL = HL XOR CRC POLYNOMIAL
MOV H,A
MOV A,L
XRI CRCPLY AND 255
MOV L,A
CALC2: SHLD CRCREG ;SAVE RESULT
DCR C
JZ CALC3
MOV A,B ;GET DATA
ADD A ;SHIFT IT LEFT 1 BIT
MOV B,A
JMP CALC1 ;DO IT AGAIN
CALC3: POP H
POP B
RET
;
;------------------------------------------------------------;
;
;THIS IS A CW ID SUBROUTINE PACKAGE
;HERE IS THE ASCII TO CW TRANSLATE TABLE
ASCII$CW:
DB 5+10010000B ; / -..-.
DB 5+11111000B ;ZERO
DB 5+01111000B ;1
DB 5+00111000B ;2
DB 5+00011000B ;3
DB 5+00001000B ;4
DB 5+00000000B ;5
DB 5+10000000B ;6
DB 5+11000000B ;7
DB 5+11100000B ;8
DB 5+11110000B ;9
DB 0 ;: NOT USED
DB 0 ; ; NOT USED
DB 0 ; < NOT USED
DB 0 ; = NOT USED
DB 0 ; > NOT USED
DB 0 ; ? NOT USED
DB 0 ; @ NOT USED
DB 2+01000000B ; A .-
DB 4+10000000B ;B -...
DB 4+10100000B ; C -.-.
DB 3+10000000B ; D -..
DB 1+00000000B ; E .
DB 4+00100000B ; F ..-.
DB 3+11000000B ;G --.
DB 4+00000000B ;H
DB 2+00000000B ; I ..
DB 4+01110000B ; J .---
DB 3+10100000B ; K -.-
DB 4+01000000B ; L .-..
DB 2+11000000B ; M --
DB 2+10000000B ; N -.
DB 3+11100000B ; O ---
DB 4+01100000B ; P .--.
DB 4+11010000B ; Q --.-
DB 3+01000000B ; R .-.
DB 3+00000000B ; S ...
DB 1+10000000B ; T -
DB 3+00100000B ; U ..-
DB 4+00010000B ; V ...-
DB 3+01100000B ; W .--
DB 4+10010000B ; X -..-
DB 4+10110000B ; Y -.--
DB 4+11000000B ; Z --..
;THIS IS THE END OF THE TABLE
ID$STRING: DS 20 ;RESERVE 20 BYTES FOR ID STRING
DE$STR: DB ' DE '
CWID:
LXI H,ID$INH ;ID INHIBIT FLAG
MOV A,M
MVI M,0 ;CLEAR IT
ORA A ;TEST IT
RNZ ;DONT ID THIS TIME IF TRUE
LXI H,TDSTCAL ;GET DESTINATION CALL
LXI D,ID$STRING
MVI B,6
CALL MOVE
LXI H,DE$STR ;POINT TO "DE" STRING
MVI B,4
CALL MOVE
LXI H,MYCALL ;PUT OUR CALL IN
MVI B,6
CALL MOVE
XCHG
MVI M,' ' ;TRAILING SPACE
INX H
MVI M,0 ;NULL TERMINATES STRING
CALL INITX ;INIT. FOR TRANSMITTING
CALL TONEOFF
LXI D,200 ;DELAY 200 MS AFTER KEY DOWN
CALL RFON ;KEY TRANSMITTER
LXI H,ID$STRING
CWID1: MOV A,M ;GET A ASCII CHAR.
ORA A ;NULL?
JZ RFOFF ;QUIT IF NULL
CPI '0'
JC CWID2 ;IGNORE INVALID CHAR
PUSH H
MOV C,A
CALL SEND$CW$CHAR ;CONVERT TO CW AND SEND
POP H
CWID2: INX H
JMP CWID1 ;LOOP TILL DONE
SEND$CW$CHAR:
PUSH B
CALL TONEOFF
MVI C,2
CALL DELAYX ;WAIT 2 DOT TIMES
POP B ;GET ASCII CHARACTER BACK
LXI H,ASCII$CW-2FH ;POINT TO CONVERSION TABLE
MOV A,C
CPI 20H ;TEST FOR SPACE
JNZ CW1
MVI C,7 ;7 DOT TIMES FOR SPACE
JMP DELAYX
CW1: MVI B,0
DAD B ;INDEX INTO TABLE
MOV A,M ;GET CW CODE
ANI 7 ;KEEP COUNT
RZ ;RETURN IF ZERO COUNT
MOV B,A ;PUT COUNT IN REG. B
MOV A,M ;GET CODE AGAIN
CW2: ADD A ;SHIFT MSB INTO CARRY
MVI E,1 ;ASSUME A DOT
JNC CW3 ;JUMP IF DOT
MVI E,3 ;ELSE SET FOR DASH
CW3: PUSH B ;SAVE COUNT
PUSH PSW ;SAVE CODE
CALL TONEON ;TURN ON TONE
MOV C,E ;GET TIME
CALL DELAYX
CALL TONEOFF ; TONE OFF
MVI C,1
CALL DELAYX ;WAIT 1 DOT TIME
POP PSW ;RESTORE CODE
POP B ;RESTORE COUNT
DCR B ;COUNT DOWN
JNZ CW2 ;LOOP TILL FINISHED
RET
;THIS TIMES 1 DIT.
DELAYX: MOV A,C
ORA A
RZ
PUSH B
PUSH D
DLY1: LXI D,50 ;50 MS DELAY
CALL DELAY
DCR C
JNZ DLY1
POP D
POP B
RET
;END OF CW ID ROUTINES
;--------------------------------------------------------;
;
;
DSEG ;BEGIN DATA SEGMENT
;
;THIS IS THE VARIABLE STORAGE AREA
CRCREG: DS 2 ;USED BY CRC ROUTINE
N: DS 2 ;USED BY BXDEC ROUTINE
DATAPTR:DS 2
DATACNT:DS 2
RETRYS: DS 1 ;RETRY COUNTER
SEQNUM: DS 2 ;LOCAL COUNT OF RECEIVED PACKETS
DIRFLAG:DS 1 ;1=DIRECTORY 0=NORMAL FILE
ID$INH: DS 1 ;1=INHIBIT NEXT ID
LEADBB: DS 4 ;LEADER BUFFER BEGINNING
LEADBE: DS 1 ;LEADER BUFFER END
TIME: DS 2 ;USED FOR TIMEOUTS
TEMPTIME: DS 2
;SCRATCH PAD PACKET HEADER
BUFFR: DS 5 ;LEADER
LEN: DS 2 ;TOTAL LENGTH OF MESSAGE
CRC: DS 2 ;CRC BYTES
DSTCAL: DS 6 ;DESTINATION CALL SIGN
DSTLOC: DS 1 ;DESTINATION LOCATION CODE
DSTRPT: DS 1 ;DESTINATION REPEATER (0=DIRECT)
SRCCAL: DS 6 ;SOURCE CALL SIGN
SRCLOC: DS 1 ;SOURCE LOCATION CODE
SRCRPT: DS 1 ;SOURCE REPEATER (0=DIRECT)
PTYPE: DS 1 ; PACKET TYPE
STITLE: DS 12 ;NAME OF PACKET GROUP (CP/M FILE NAME)
PACNUM: DS 2 ;PACKET NUMBER
;TRANSMIT PACKET BUFFER
TBUFFR: DS 5 ;LEADER
TLEN: DS 2 ;TOTAL LENGTH OF MESSAGE
TCRC: DS 2 ;CRC BYTES
TDSTCAL:DS 6 ;DESTINATION CALL SIGN
TDSTLOC:DS 1 ;DESTINATION LOCATION CODE
TDSTRPT:DS 1 ;DESTINATION REPEATER (0=DIRECT)
TSRCCAL:DS 6 ;SOURCE CALL SIGN
TSRCLOC:DS 1 ;SOURCE LOCATION CODE
TSRCRPT:DS 1 ;SOURCE REPEATER (0=DIRECT)
TPTYPE: DS 1 ; PACKET TYPE
TTITLE: DS 12 ;NAME OF PACKET GROUP (CP/M FILE NAME)
TPACNUM:DS 2 ;PACKET NUMBER
TDATA: DS 1024 ;THIS IS THE DATA
;RECEIVE PACKET BUFFER
;
RBUFFR: DS 5 ;LEADER
RLEN: DS 2 ;LENGTH OF RECEIVED PACKET
RCRC: DS 2 ;RECEIVED CRC BYTES
RDSTCAL:DS 6 ;DESTINATION CALL SIGN
RDSTLOC:DS 1 ;DESTINATION LOCATION CODE
RDSTRPT:DS 1 ;DESTINATION REPEATER (0=DIRECT)
RSRCCAL:DS 6 ;SOURCE CALL SIGN
RSCRLOC:DS 1 ;SOURCE LOCATION CODE
RSCRRPT:DS 1 ;SOURCE REPEATER (0=DIRECT)
RPTYPE: DS 1 ;PACKET TYPE
RTITLE: DS 12 ;NAME OF PACKET GROUP (FILE NAME)
RPACNUM:DS 2 ;PACKET NUMBER
RDATA: DS 1024 ;RECEIVED DATA
;
;
;
;
INBUFFER: DS 1024 ;FILE READ BUFFER
OUTBUFFER: DS 1024 ;FILE OUTPUT BUFFER
END