Pub - beta

Various works which are, to my knowledge, no longer copyrighted

User Tools

Site Tools


modem.asm
;MODEM ROUTINE - SEND, RECV, COMPUTER, TERMINAL
; * * * * * * * * * * * * * * * * * * * *
;SENSE SWITCH CONTROLS:			*
;					*
;	A12 UP TO DISPLAY INCOMING DATA *
;	A13 UP TO DISPLAY OUTGOING DATA *
; * * * * * * * * * * * * * * * * * * * *
;
;09/23/77 FIRST WRITTEN BY WARD CHRISTENSEN
;09/25/77 FIRST TESTING COMPLETE
;09/26/77 ADD ERROR$LIMIT EQU
;10/01/77 CHANGE EXIT$CHAR FROM CTL-C TO
;	  CTL-E FOR USE W/TIMESHARING COMPUTERS
;10/10/77 CORRECT TO SEND ANY LENGTH FILE
;
MODEM$CTL$PORT	EQU	4
MODEM$SEND$MASK	EQU	2
SEND$READY	EQU	2	;VALUE WHEN READY
MODEM$RECV$MASK	EQU	1
RECV$READY	EQU	1	;BIT ON WHEN READY
MODEM$DATA$PORT	EQU	5
KEY$CTL$PORT	EQU	0	;KEYBOARD STATUS
KEY$READY$MASK	EQU	2
KEY$READY	EQU	2	;VALUE WHEN KEYBOARD READY
KEY$DATA$PORT	EQU	1
INIT$REQD	EQU	1	;MODEM INIT. REQ'D?
INIT$CHAR$1	EQU	3	;FIRST INIT CHAR TO CTL PORT
INIT$CHAR$2	EQU	15H	;2ND INIT CHAR TO CTL PORT
ERROR$LIMIT	EQU	10	;MAX ALLOWABLE ERRORS
EXIT$CHAR	EQU	'E'-40H ;CHAR TO EXIT FROM T OR C
	ORG	100H
	CALL	START	;GO PRINT ID
	DB	'MODEM PROGRAM AS OF '
	DB	'10/10/77',13,10,'$'
;FLAG FOR GENERATING TEST CODE TO USE KEYBOARD
;TO ECHO ACK/NAK WHILE TESTING:
TEST	EQU	0	;GENERATE TEST CODE
;
;DEFINE ASCII CHARACTERS USED
SOH	EQU	1
EOT	EQU	4
ACK	EQU	6
NAK	EQU	15H
LF	EQU	10
CR	EQU	13
;
START	POP	D	;GET ID MESSAGE
	MVI	C,PRINT
	CALL	BDOS	;PRINT ID MESSAGE
;INIT PRIVATE STACK
	LXI	H,0	;HL=0
	DAD	SP	;HL=STACK FROM CP/M
	SHLD	STACK	;..SAVE IT
	LXI	SP,STACK	;SP=MY STACK
;
	CALL	INIT$PORT
;GOBBLE UP GARBAGE CHARS FROM THE LINE
	MVI	B,1	;TIMEOUT DELAY
	CALL	RECV
	MVI	B,1
	CALL	RECV
;
	LDA	FCB+1	;GET OPTION (S R C T)
	PUSH	PSW	;SAVE OPTION
	CALL	MOVE$FCB ;MOVE FROM 6C TO 5C
	POP	PSW	;GET OPTION
	CPI	'S'
	JZ	SEND$FILE
	CPI	'R'
	JZ	RECV$FILE
	CPI	'C'
	JZ	COMPUTER
	CPI	'T'
	JZ	TERMINAL
;INVALID OPTION
	CALL	ERXIT	;EXIT W/ERROR
	DB	'++INVALID OPTION ON MODEM COMMAND - ',CR,LF
	DB	'MUST BE ONE OF THE FOLLOWING:',CR,LF
	DB	'MODEM SEND FILENAME',CR,LF
	DB	'MODEM RECV FILENAME',CR,LF
	DB	'MODEM COMPUTER',CR,LF
	DB	'MODEM TERMINAL',CR,LF
	DB	'  ABBREV. ALLOWED: S R C T$'
;
;****************COMPUTER****************
;
;TERMINAL-TERMINAL W/ECHO SENT BY THIS PROGRAM
;'EXIT$CHAR' TYPED TO RE-BOOT
;IF ONE COMPUTER IS IN COMPUTER MODE,
;THE OTHER SHOULD BE IN TERMINAL MODE.
;AT NO TIME SHOULD BOTH BE IN COMPUTER
;MODE BECAUSE LINE ERRORS WILL BE PING-PONGED
;BACK AND FORTH AD INFINITUM.
COMPUTER:
	IN	MODEM$CTL$PORT
	ANI	MODEM$RECV$MASK
	CPI	RECV$READY
	JZ	LINE$CHAR
;NOTHING FROM LINE, CHECK KEYBOARD
	MVI	C,CONST	;CHECK STATUS
	CALL	BDOS
	ORA	A	;READY?
	JZ	COMPUTER ;..NO
	MVI	C,RDCON
	CALL	BDOS	;GET CHAR
	CPI	EXIT$CHAR ;END?
	JZ	EXIT	;YES, EXIT
	OUT	MODEM$DATA$PORT ;SEND CHAR
	JMP	COMPUTER
;GOT CHAR FROM LINE
LINE$CHAR:
	IN	MODEM$DATA$PORT
	OUT	MODEM$DATA$PORT ;ECHO
	CALL	TYPE	;TYPE IT
	JMP	COMPUTER
;
;**************TERMINAL****************
;
;SEE NOTES UNDER 'COMPUTER'
;
TERMINAL:
	IN	KEY$CTL$PORT
	ANI	KEY$READY$MASK
	CPI	KEY$READY
	JNZ	TRECV	;NOTHING FROM KEYBOARD
	IN	KEY$DATA$PORT
	ANI	7FH
	CPI	EXIT$CHAR ;TIME TO END?
	JZ	EXIT
	OUT	MODEM$DATA$PORT
TRECV	IN	MODEM$CTL$PORT
	ANI	MODEM$RECV$MASK
	CPI	RECV$READY
	JNZ	TERMINAL
	IN	MODEM$DATA$PORT
	CALL	TYPE
	JMP	TERMINAL
;INIT SERIAL PORT
INIT$PORT:
	LXI	D,MSG$BAUD
	CALL	PRINT$MESSAGE
	IF	INIT$REQD
	MVI	A,INIT$CHAR$1
	OUT	MODEM$CTL$PORT
	MVI	A,INIT$CHAR$2
	OUT	MODEM$CTL$PORT
	ENDIF
;GET THE SPEED
	XRA	A	;GET A ZERO
	CALL	SEND	;SEND A CHAR
	XRA	A	;GET ZERO
	CALL	SEND	;SEND AGAIN
;WAIT, TIMING TO DETERMINE BAUD RATE
	LXI	B,0	;INIT COUNT
INIT$WAIT:
	IN	MODEM$CTL$PORT
	ANI	MODEM$SEND$MASK
	CPI	SEND$READY
	JZ	INIT$WAIT$END
	DCR	C
	JNZ	INIT$WAIT
	DCR	B
	JNZ	INIT$WAIT
	CALL	ERXIT
	DB	'++TIME OUT DETERMINING BAUD RATE$'
INIT$WAIT$END:
	MOV	A,B	;GET COUNT
	CPI	0F5H	;110 BAUD = F0,
	JC	BAUD$110 ;300 BAUD = FA
;BAUD RATE 300
	LXI	D,MSG$300
INIT$PRINT:
	CALL	PRINT$MESSAGE
	RET		;FROM INIT$PORT
BAUD$110:
	LXI	D,MSG$110
	JMP	INIT$PRINT
MSG$BAUD DB 'BAUD RATE IS $'
MSG$110	DB	'110',CR,LF,'$'
MSG$300	DB	'300',CR,LF,'$'
;MOVE FCB (SECOND OPERAND ON COMMAND)
; TO NORMAL FCB LOCATION
MOVE$FCB:
	LXI	H,FCB
	LXI	D,FCB+16
	MVI	B,16
MOVE$LOOP:
	LDAX	D
	MOV	M,A
	INX	D
	INX	H
	DCR	B
	JNZ	MOVE$LOOP
	XRA	A	;GET 0
	STA	FCB+32	;ZERO RECORD #
	RET
;
;*****************SEND FILE***************
;
SEND$FILE:
	CALL	OPEN$FILE	;OPEN THE FILE
	LXI	D,OPENM
	CALL	PRINT$MESSAGE
SENDB	XRA	A	;GET A ZERO
	STA	ERRCT	;ZERO ERROR COUNT
;READ SECTOR, SEND IT
	CALL	READ$SECTOR
	LDA	SECTNO	;INCR SECT NO.
	INR	A
	STA	SECTNO
;SEND OR REPEAT SECTOR
REPTB	LXI	D,SECTMSG
	CALL	PRINT$MESSAGE
	LDA	SECTNO
	CALL	HEXO
	CALL	CRLF
	MVI	A,SOH
	CALL	SEND
	LDA	SECTNO
	CALL	SEND
	LDA	SECTNO
	CMA
	CALL	SEND
	MVI	C,0	;INIT CKSUM
	LXI	H,80H
SENDC	MOV	A,M
	CALL	SEND
	INX	H
	MOV	A,H
	CPI	1	;DONE WITH SECTOR?
	JNZ	SENDC
;SECTOR SENT, SEND CKSUM
	MOV	A,C	;GET CKSUM
	CALL	SEND
;GET ACK ON SECTOR
	MVI	B,4	;WAIT 4 SECONDS MAX
	CALL	RECV
	JNC	SNTO	;NO TIMEOUT
;TIMED OUT WAITING FOR ACK
	CALL	TOUT	;PRINT 'TIMEOUT', ERRCT
DATERR	LDA	ERRCT
	INR	A
	STA	ERRCT
	CPI	ERROR$LIMIT
	JC	REPTB	;REPEAT SECTOR
;SECTOR SEND NO GOOD AFTER 10 TRIES
	CALL	ERXIT
	DB	'CAN''T SEND SECTOR '
	DB	'- ABORTING',13,10,'$'
SECTMSG	DB	'SENDING SECTOR $'
;NO TIMEOUT SENDING SECTOR
SNTO	CPI	ACK	;ACK RECIEVED?
	JZ	SENDB	;..YES, SEND NEXT SECT
;ACK NOT RECIEVED
	CALL	HEXO	;TYPE CHR IN HEX
	LXI	D,ERR1
	CALL	PRINT$MESSAGE
	JMP	DATERR	;GO TO DATA ERROR
ERR1	DB	'H RECEIVED, NOT ACK',13,10,'$'
OPENM	DB	'FILE OPEN',13,10,'$'
;
;**************RECEIVE FILE****************
;
RECV$FILE:
	CALL	ERASE$OLD$FILE
	CALL	MAKE$NEW$FILE
RECV$LOOP:
	XRA	A	;GET 0
	STA	ERRCT	;INIT ERROR COUNT
RECV$HDR:
	LXI	D,RMSG
	CALL	PRINT$MESSAGE
	LDA	SECTNO
	INR	A
	CALL	HEXO
	CALL	CRLF
	MVI	B,5	;5 SEC TIMEOUT
	CALL	RECV
	JNC	RHNTO	;NO TIMEOUT
RECV$HDR$TIMEOUT:
	CALL	TOUT	;PRINT TIMEOUT
RECV$SECT$ERR:
;PURGE THE LINE OF INPUT CHARS
	MVI	B,1	;1 SEC W/NO CHARS
	CALL	RECV
	JNC	RECV$SECT$ERR ;LOOP UNTIL SENDER DONE
	MVI	A,NAK
	CALL	SEND	;SEND NAK
	LDA	ERRCT
	INR	A
	STA	ERRCT
	CPI	ERROR$LIMIT
	JC	RECV$HDR
	CALL	ERXIT
	DB	'++UNABLE TO GET VALID HEADER',0DH,0AH,'$'
RMSG	DB	'WAITING FOR SECTOR #$'
;GOT CHAR - MUST BE SOH
RHNTO	CPI	SOH
	JZ	GOT$SOH
	ORA	A	;00 FROM SPEED CHECK?
	JZ	RECV$HDR
	CPI	EOT
	JZ	GOT$EOT
;DIDN'T GET SOH - 
	CALL	HEXO
	LXI	D,ERRSOH
	CALL	PRINT$MESSAGE
	JMP	RECV$SECT$ERR
ERRSOH	DB	'H RECEIVED, NOT SOH',0DH,0AH,'$'
GOT$SOH:
	MVI	B,1
	CALL	RECV
	JC	RECV$HDR$TIMEOUT
	MOV	D,A	;D=BLK #
	MVI	B,1
	CALL	RECV	;GET CMA'D SECT #
	JC	RECV$HDR$TIMEOUT
	CMA
	CMP	D	;GOOD SECTOR #?
	IF	TEST
	JMP	RECV$SECTOR
	ENDIF
	JZ	RECV$SECTOR
;GOT BAD SECTOR #
	LXI	D,ERR2
	CALL	PRINT$MESSAGE
	JMP	RECV$SECT$ERR
ERR2	DB	'++BAD SECTOR # IN HDR',0DH,0AH,'$'
;
RECV$SECTOR:
	MOV	A,D	;GET SECTOR #
	STA	RECVD$SECT$NO
	MVI	C,0	;INIT CKSUM
	LXI	H,80H	;POINT TO BUFFER
RECV$CHAR:
	MVI	B,1	;1 SEC TIMEOUT
	CALL	RECV	;GET CHAR
	JC	RECV$HDR$TIMEOUT
	MOV	M,A	;STORE CHAR
	INR	L	;DONE?
	JNZ	RECV$CHAR
;VERIFY CHECKSUM
	MOV	D,C	;SAVE CHECKSUM
	MVI	B,1	;TIMEOUT
	CALL	RECV	;GET CHECKSUM
	JC	RECV$HDR$TIMEOUT
	CMP	D	;CHECK
	JNZ	RECV$CKSUM$ERR
;
;GOT A SECTOR, WRITE IF = 1+PREV SECTOR
;
	LDA	RECVD$SECT$NO
	MOV	B,A	;SAVE IT
	LDA	SECTNO	;GET PREV
	INR	A	;CALC NEXT SECTOR #
	CMP	B	;MATCH?
	JNZ	DO$ACK
;GOT NEW SECTOR - WRITE IT
	LXI	D,FCB
	MVI	C,WRITE
	CALL	BDOS
	ORA	A
	JNZ	WRITE$ERROR
	LDA	RECVD$SECT$NO
	STA	SECTNO	;UPDATE SECTOR #
DO$ACK	MVI	A,ACK
	CALL	SEND
	JMP	RECV$LOOP
;
WRITE$ERROR:
	CALL	ERXIT
	DB	'++ERROR WRITING FILE',0DH,0AH,'$'
;
RECV$CKSUM$ERR:
	LXI	D,ERR3
	CALL	PRINT$MESSAGE
	JMP	RECV$SECT$ERR
ERR3	DB	'++BAD CKSUM ON SECTOR'
	DB	0DH,0AH,'$'
;
GOT$EOT:
	MVI	A,ACK	;ACK THE EOT
	CALL	SEND
	LXI	D,FCB
	MVI	C,CLOSE
	CALL	BDOS
	INR	A
	JNZ	XFER$CPLT
	CALL	ERXIT
	DB	'++ERROR CLOSING FILE$'
;
ERASE$OLD$FILE:
	LXI	D,FCB
	MVI	C,SRCHF	;SEE IF IT EXISTS
	CALL	BDOS
	INR	A	;FOUND?
	RZ		;NO, RETURN
	LXI	D,EXIST
	CALL	PRINT$MESSAGE
	MVI	C,RDCON
	CALL	BDOS
	CPI	'Y'
	JNZ	0	;REBOOT IF NOT ERASE
	CALL	CRLF
;ERASE OLD FILE
	LXI	D,FCB
	MVI	C,ERASE
	CALL	BDOS
	RET
EXIST	DB	'++FILE EXISTS, TYPE Y TO ERASE:$'
;
MAKE$NEW$FILE:
	LXI	D,FCB
	MVI	C,MAKE
	CALL	BDOS
	INR	A	;FF=BAD
	RNZ		;OPEN OK
;DIRECTORY FULL - CAN'T MAKE FILE
	CALL	ERXIT
	DB	'++ERROR - CAN''T MAKE FILE',0DH,0AH
	DB	'++DIRECTORY MUST BE FULL',0DH,0AH,'$'
;
; S U B R O U T I N E S
;
;OPEN FILE
OPEN$FILE	LXI	D,FCB
	MVI	C,OPEN
	CALL	BDOS
	INR	A	;OPEN OK?
	RNZ		;GOOD OPEN
	CALL	ERXIT
	DB	'CAN''T OPEN FILE$'
; - - - - - - - - - - - - - - -
PRINT$MESSAGE:
	MVI	C,PRINT
	JMP	BDOS	;PRINT MESSAGE, RETURN
; - - - - - - - - - - - - - - -
;EXIT PRINTING MESSAGE FOLLOWING 'CALL ERXIT'
ERXIT	POP	D	;GET MESSAGE
	CALL	PRINT$MESSAGE	;PRINT IT
EXIT	LHLD	STACK	;GET ORIGINAL STACK
	SPHL		;RESTORE IT
	RET		;--EXIT-- TO CP/M
; - - - - - - - - - - - - - - -
;MODEM RECV
RECV	PUSH	D	;SAVE
MSEC	LXI	D,0BBBBH	;1 SEC DCR COUNT
	IF	NOT TEST
MWTI	IN	MODEM$CTL$PORT
	ANI	MODEM$RECV$MASK
	CPI	RECV$READY
	JZ	MCHAR	;GOT CHAR
	ENDIF
	IF	TEST
MWTI	IN	KEY$CTL$PORT	;READ KEYBOARD
	ANI	KEY$READY$MASK
	CPI	KEY$READY
	JZ	MCHAR
	ENDIF
	DCR	E	;COUNT DOWN
	JNZ	MWTI	;FOR TIMEOUT
	DCR	D
	JNZ	MWTI
	DCR	B	;DCR # OF SECONDS
	JNZ	MSEC
;MODEM TIMED OUT RECEIVING
	POP	D	;RESTORE D,E
	STC		;CARRY SHOWS TIMEOUT
	RET
;GOT MODEM CHAR
	IF	NOT TEST
MCHAR	IN	MODEM$DATA$PORT
	ENDIF
	IF	TEST
MCHAR	IN	KEY$DATA$PORT
	ANI	7FH	;DEL PARITY FROM KEYBOAREAD
	ENDIF
	POP	D	;RESTORE DE
;CALC CHECKSUM
	PUSH	PSW
	ADD	C
	MOV	C,A
;CHECK IF MONITORING INPUT
	IN	0FFH
	ANI	10H
	JZ	NO$MON$INPUT
	POP	PSW
	PUSH	PSW
	CALL	SHOW	;CHAR RECEIVED
NO$MON$INPUT:
	POP	PSW
;TURN OFF CARRY TO SHOW NO TIMEOUT
	ORA	A
	RET
; - - - - - - - - - - - - - - -
;MODEM SEND CHAR ROUTINE
SEND	PUSH	PSW
;CHECK IF MONITORING OUTPUT
	IN	0FFH
	ANI	20H
	JZ	NO$MON$OUTPUT
	POP	PSW
	PUSH	PSW
	CALL	SHOW
NO$MON$OUTPUT:
	POP	PSW
	PUSH	PSW
	ADD	C	;CALC CKSUM
	MOV	C,A
SENDW	IN	MODEM$CTL$PORT
	ANI	MODEM$SEND$MASK
	CPI	SEND$READY
	JNZ	SENDW
	POP	PSW	;GET CHAR
	OUT	MODEM$DATA$PORT
	RET
; - - - - - - - - - - - - - - -
;SHOW CHAR RECEIVED OR SENT
SHOW	CPI	0AH	;LF?
	JZ	TYPE
	CPI	0DH
	JZ	TYPE
	CPI	09	;TAB
	JZ	TYPE
	CPI	' '
	JC	SHOWHEX
	CPI	7FH
	JC	TYPE
SHOWHEX	PUSH	PSW
	MVI	A,'('
	CALL	TYPE
	POP	PSW
	CALL	HEXO
	MVI	A,')'
	JMP	TYPE
; - - - - - - - - - - - - - - -
;PRINT TIMEOUT MESSAGE
TOUTM	DB	'TIMEOUT $'
TOUT	LXI	D,TOUTM
	CALL	PRINT$MESSAGE
PRINT$ERRCT:
	LDA	ERRCT
	CALL	HEXO	;FALL INTO CR/LF
; - - - - - - - - - - - - - - -
CRLF	MVI	A,13
	CALL	TYPE
	MVI	A,10
; - - - - - - - - - - - - - - -
TYPE	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A
	MVI	C,WRCON
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET
; - - - - - - - - - - - - - - -
;HEX OUTPUT
HEXO	PUSH	PSW
	RAR
	RAR
	RAR
	RAR
	CALL	NIBBL
	POP	PSW
NIBBL	ANI	0FH
	CPI	10
	JC	ISNUM
	ADI	7
ISNUM	ADI	'0'
	JMP	TYPE
; - - - - - - - - - - - - - - -
;FILE READ ROUTINE
READ$SECTOR:
	LXI	D,FCB
	MVI	C,READ
	CALL	BDOS
	ORA	A
	RZ
	DCR	A	;EOF?
	JNZ	RDERR
;EOF
	XRA	A
	STA	ERRCT
	LXI	D,FSENTM ;FILE SENT MESSAGE
	CALL	PRINT$MESSAGE
SEOT	MVI	A,EOT
	CALL	SEND
	MVI	B,5	;WAIT 5 SEC FOR TIMEOUT
	CALL	RECV
	JC	EOTTOT	;EOT TIMEOUT
	CPI	ACK
	JZ	XFER$CPLT
;ACK NOT RECIEVED
	CALL	HEXO
	LXI	D,ERR1
	CALL	PRINT$MESSAGE
EOTERR	LDA	ERRCT
	INR	A
	STA	ERRCT
	CPI	ERROR$LIMIT
	JC	SEOT
	CALL	ERXIT
	DB	'NO ACK RECIEVED ON EOT$',10,13
FSENTM	DB	13,10,'FILE SENT, SENDING EOT''S',10,13,'$'
;TIMEOUT ON EOT
EOTTOT	CALL	TOUT
	JMP	EOTERR
;READ ERROR
RDERR	CALL	ERXIT
	DB	'++FILE READ ERROR$'
; - - - - - - - - - - - - - - -
;DONE - CLOSE UP SHOP
XFER$CPLT:
	CALL	ERXIT
	DB	13,10,'TRANSFER COMPLETE$'
	DS	40	;STACK AREA
STACK	DS	2	;STACK POINTER
RECVD$SECT$NO	DB	0
SECTNO	DB	0	;CURRENT SECTOR NUMBER 
ERRCT	DB	0	;ERROR COUNT
;
; BDOS EQUATES (VERSION 2)
;
RDCON	EQU	1
WRCON	EQU	2
PRINT	EQU	9
CONST	EQU	11	;CONSOLE STAT
OPEN	EQU	15	;0FFH=NOT FOUND
CLOSE	EQU	16	;   "	"
SRCHF	EQU	17	;   "	"
SRCHN	EQU	18	;   "	"
ERASE	EQU	19	;NO RET CODE
READ	EQU	20	;0=OK, 1=EOF
WRITE	EQU	21	;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC
MAKE	EQU	22	;0FFH=BAD
REN	EQU	23	;0FFH=BAD
STDMA	EQU	26
BDOS	EQU	5
REIPL	EQU	0
FCB	EQU	5CH	;SYSTEM FCB
modem.asm.txt · Last modified: 2025/03/04 08:03 by evert