IF NOT lasm
.printx * CPXAPP.ASM *
ENDIF	;NOT lasm
;       KERMIT - (Celtic for "FREE")
;
;       This is the CP/M-80 implementation of the Columbia University
;       KERMIT file transfer protocol.
;
;       Version 4.0
;
;       Copyright June 1981,1982,1983,1984,1985
;       Columbia University
;
; Originally written by Bill Catchings of the Columbia University Center for
; Computing Activities, 612 W. 115th St., New York, NY 10025.
;
; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,
; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many
; others.
;
;       This file contains the system-dependent code and data for KERMIT.
;       It will be probably be broken into independent files to generate
;       overlays for the various systems, one or more overlay possible
;       from each file.  For now, we will leave it in one piece.
;
; revision history:
; edit 2, 22 July, 1987 by OBSchou.  Massaged code to work with CPXCOM.ASM
;
; edit 1, 2nd June, 1987 by OBSchou.  Extracted all Apple related code.
;
;
;      Family is the string used in VERSION to say which of several 
;       smaller overlay files are used.  These are (will be) derived from 
;       the juge CPXSYS.ASM file, in which case we will never get here.  
;       Just a Dollar, but put a sting in for a family of machines.
;
family:	db      'CPXAPP.ASM (2)  22-jul-87$'    ; Used for family versions....
;
	
IF apple
.printx	* Assembling KERMIT-80 for the Apple ][	*
ENDIF;apple
IF apmmdm
.printx	*   with Z80 Softcard &	Micromodem II	*
ENDIF;apmmdm
IF ap6551
.printx	*     with Z80 Softcard	& 6551 ACIA	*
ENDIF;ap6551
IF ap6850;[32]
.printx	*     with Z80 Softcard	& 6850 ACIA	*
ENDIF;ap6850 [32]
IF apcps;[22]
.printx	*     with Softcard & CPS Multifunction	card	 *
ENDIF;apcps

;

IF ap6551 OR ap6850 OR apcps;[9] [14]
				;jb eg. Apple SSC, Videx PSIO, Basis 108
apslot	EQU	2		;jb set equal to slot containing serial card
				;jb  set to 1 for Basis built-in port
ENDIF;jb ap6551 [9] apcps ap6850 [14]
IF ap6850	;[14] offset in slot I/O space for ACIA registers
		;e.g. PACT=00,01: AIO-I=05,04: AIO-II=0D,0C: Aristocard=0B,0A
apdat	EQU	0DH		;data reg.=0E080h+apslot*10h*apdat
apstat	EQU	0CH		;comm/stat reg.=0E080h+apslot*10h+apstat
ENDIF;ap6850


IF apmmdm
;APPLE Slot 2 contains Micromodem II.
MNPORT	EQU	0E0A7H	;Communications Port.
mnprts	EQU	0E0A6H	;Communications Port Status.
mnmodm	EQU	0E0A5H	;Modem Control Port.
orgmod	EQU	8EH	;Modem Originate Mode.
OUTPUT	EQU	02H	;Output Buffer Empty.
INPUT	EQU	01H	;Input Register Full.
apinc1	EQU	03H	;First Init Character for 6850 ACIA (Reset)
apinc2	EQU	11H	;Second Init Character for ACIA (8-bits)
apoffh	EQU	80H	;Set if OFFHOOK
AP300	EQU	1	;300 Baud
z80	EQU	TRUE	;Z80 Softcard
ENDIF;apmmdm

IF ap6551		;jb
mnport	EQU	0E088H+(10H*apslot)	;jb Communications Port.
mnprts	EQU	0E089H+(10H*apslot)	;jb Communications Port Status.
mnprtc	EQU	0E08BH+(10H*apslot)	;jb Communications Control
mnprtm	EQU	0E08AH+(10H*apslot)	;jb Communications Master (command)
output	EQU	10H		;jb Output Buffer Empty.
input	EQU	08H		;jb Input Register Full.
mncinb	EQU	18H		;jb Control Port Initialization Byte
				;jb  (8-bit, no parity, 1-stop, 1200 baud)
mnminb	EQU	0BH		;jb Master Port Initialization Byte
				;jb  (DTR, RTS, no interrupts)
z80	EQU	TRUE		;Z80 Softcard
ENDIF;ap6551

IF ap6850 ;[32]
mnport	EQU	0E080H+(10H*apslot)+apdat	;Communications Port.
mnprts	EQU	0E080H+(10H*apslot)+apstat	;Communications Port Status.
OUTPUT	EQU	02H		;Output Buffer Empty.
INPUT	EQU	01H		;Input Register Full.
apinc1	EQU	03H		;First Init Character for 6850 ACIA (Reset)
apinc2	EQU	15H		;Second Init Character for ACIA (8 data, 1 stop bit)
z80	EQU	TRUE		;Z80 Softcard
ENDIF;[32] ap6850

IF apcps		;[22]
mnport	EQU	0E0FAH+(100H*apslot)	; Communications Port.
mnprts	EQU	0E0FBH+(100H*apslot)	; Communications Port Status.
mnprtc	EQU	0E0FEH+(100H*apslot)	; Communications Control
output	EQU	1		; Output Buffer Empty.
input	EQU	2		; Input Register Full.
TxEmpty	EQU	04h		; Transmitter empty flag
apmod1	EQU	4EH		; Mode Byte 1 (1 stop, no parity,8-bit, 16x)
				; Mode Byte 2 is speed control byte
apcmd	EQU	37H		; Command Byte (RTS,Error reset,RxE,DTR,TxE)
z80	EQU	TRUE		;Z80 Softcard
ENDIF;[22] apcps

IF apple 
defesc	EQU	']'-100O        ;The default escape character.
ENDIF;apple 


; default to VT52-EMULATION ON.

vtval	EQU	1



sysxin:		; continuation of systemm dependent initialisation code

IF ap6551
	lda	mnprtc		; read control port
	ani	0fH		; extract low order nybble
	sta	speed		; store as comm line speed
	sta	speed+1		;  (16 bits, to match speed table entries)
	mvi	a,mnminb	;jb initialization routine
	sta	mnprts		;jb
	sta	mnprtm		;jb initialize master (command) port
	mvi	a,mncinb	;jb
	sta	mnprtc		;jb initialize control port
ENDIF;ap6551

IF ap6850 ;[32]
	mvi	a,apinc1	;Init ACIA
	sta	mnprts
	mvi	a,apinc2	;Set ACIA bits per character
	sta	mnprts
ENDIF;[32] ap6850

IF apcps		;[22]
	lxi	h,3737h		;Default 1200 baud
	shld	speed		;Store as port speed
	xchg
	call	sysspd		;Initialise the port
ENDIF;[22] apcps

	ret			; return from system-dependent routine

;

;
;       system-dependent termination processing
;       If we've changed anything, this is our last chance to put it back.
sysexit:

	ret

;
;       system-dependent processing for start of CONNECT command
;
syscon:
IF apmmdm
	call	ckdial		;See if dialing is required.
	 jmp	kermit		;Go to command loop if aborted.
ENDIF;apmmdm

	ret

conmsg:		; Messages printed when entering transparent (CONNECT) mode:
;

IF apmmdm
;This code was mostly taken from
;       APMODEM.ASM V2.1
;       Based on MODEM.ASM by Ward Christensen
;       Modified for the Apple ][ by Gordon Banks 1-Jan-81
;       Micromodem ][ dialer option by Dav Holle  2-Feb-81
;       Code modified for KERMIT by Scott Robinson 14-Oct-82
;
;Come here to see if we need to dial a number.
;
ckdial:	lda	mnport		;access the data port
	lda	mnprts		;check status
	ani	4		;do we already have carrier?
	jz	rskp		;Yes, just continue
	xra	a		;Hangup Phone for starters
	sta	mnmodm
	lxi	b,1000		;Delay for a second
	call	delay
	mvi	a,8FH		;orgmod+ap300+apoffh
	sta	holdd		;storing mode for after dialing
	mvi	A,8DH		;Go Offhook to start dialing sequence
	sta	mnmodm
	mvi	a,apinc1	;Init ACIA
	sta	mnport
	mvi	a,apinc2	;Set ACIA bits per character
	sta	mnport

	lxi	b,2500		;wait 2.5 seconds for dial tone
	call	delay
	lxi	d,dialms	;Ask the user for the number
	call	prtstr
;
gtdial:	mvi	c,conin		;Get a character
	call	bdos
	push	psw		;save it
	cpi	30H		;is it big enough to dial?
	jc	dialed		;no
	cpi	3AH		;is it too big to dial?
	jnc	dialed		;yes
	ani	0FH		;ok, it's a digit, get its value
	jnz	dialnz		;dial nonzero digits as-is
	mvi	A,10		;dial zero as ten
;
dialnz:	mov	e,a		;count pulses in E-reg
dopuls:	mvi	a,0DH		;put it on-hook
	sta	mnmodm
	lxi	b,61		;61-millisec pulse
	call	delay
	mvi	a,8DH		;take it off-hook again...
	sta	mnmodm
	lxi	b,39		;39-millisec delay between pulses
	call	delay
	dcr	e		;any more pulses to do?
	jnz	dopuls		;yep, do 'em
	lxi	b,600		;delay 600 msecs between digits
	call	delay
;
dialed:	pop	psw		;get back the char
	cpi	cr		;do we have a CR (done dialing)?
	jnz	gtdial		;no, keep on dialin'
	lxi	d,dialm2
	call	prtstr

tictoc:	mvi	c,dconio	;Direct console input.
	mvi	e,0FFH
	call	bdos
	ora	a		;Have a charcter?
	jnz	nodial		;If so we abort
	lda	mnport		;access the data port
	lda	mnprts		;get modem status
	ani	4		;carrier?
	jnz	tictoc		;No
;
	lda	holdd		;get the old modem control byte
	sta	mnmodm		;turn our carrier on

	lxi	d,dialm3
	call	prtstr
	jmp	rskp
nodial:	xra	a		;Hangup the modem.
	sta	mnmodm
	ret			;Return to abort the command.
;
holdd:	db	0		;Modem setup code
dialms:	DB	'Number to Dial: $'
dialm2:	DB	CR,LF,'Awaiting Carrier....(any key aborts)$'
dialm3:	DB	cr,lf,'Connected.',CR,LF,'$'
;
;DELAY wait for the number of millisecs in B,C
;
delay:	push	b		;save B,C
	push	d		;save D,E
	inr	b		;bump B for later DCR
;
delay1:	mvi	e,126		;delay count for 1 millisec (Apple Z80
				;clock=2.041MHz)
;
delay2:	dcr	e		;count
	jnz	delay2		;down
;
	dcr	c		;more millisecs?
	jnz	delay1		;yes
	dcr	b		;no - more in hi byte?
	jnz	delay1		;yes
	pop	d		;no,    restore D,E
	pop	b		;       restore B,C
	ret
ENDIF;apmmdm
;

;
;       syscls - system-dependent close routine
;       called when exiting transparent session.
;
syscls:
	ret
;

;
;       sysinh - help for system-dependent special functions.
;       called in response to <escape>?, after listing all the
;       system-independent escape sequences.
;
sysinh:
IF apmmdm OR apcps OR ap6850 ;
	lxi	d,inhlps	; we got options...
	call	prtstr		; print them.
ENDIF	;apmmdm OR apcps OR ap6850

	ret


;additional, system-dependent help for transparent mode
; (two-character escape sequences)
inhlps:

IF apcps OR ap6850
	db	cr,lf,'B  Transmit a BREAK'
ENDIF;apcps OR ap6850

IF apmmdm 
	db	cr,lf,'D  Drop the line'
ENDIF;apmmdm 

	db	'$'                     ;[hh] table terminator

;
;       sysint - system dependent special functions
;       called when transparent escape character has been typed;
;       the second character of the sequence is in A (and in B).
;       returns:
;       non-skip: sequence has been processed
;       skip:   sequence was not recognized
sysint:	ani	137O		; convert lower case to upper, for testing...
IF apmmdm
	cpi	'D'             ;Disconnect Modem?
	jnz	intc00		;No.
	xra	a		;Yes, hangup the modem
	sta	mnmodm
	ret			; command has been executed
intc00:
ENDIF;apmmdm

IF ap6850 OR apcps		; [22] [25] ... some more
	cpi	'B'             ; send break?
	jz	sendbr		; yes, go do it.  return nonskip when through.
ENDIF;[22] ap6850 OR apcps 

	jmp	rskp		; take skip return - command not recognized.


;

IF ap6850 ;[32]
sendbr:
;
;       Ensure that the transmitter has finished sending buffered chars
sndbr1:	lda	mnprts		; get UART status
	ani	output		; everything sent?
	jz	sndbr1		; no, wait a bit more
;
; Begin sending a break
	mvi	a,apinc2
	ori	60h		;transmit break level, CTS high
	sta	mnprts
;
; Wait for 250 milliseconds (using hundredths second delay routine)
	mvi	a,25
	call	delay
;
; Resume normal operation
	mvi	a,apinc2
	sta	mnprts
;
	ret			;done
ENDIF;[32] ap6850

IF apcps		;[22]
sendbr:
;
;       Ensure that the transmitter has finished sending buffered chars
sndbr1:	lda	mnprts		; get UART status
	ani	TxEmpty		; everything sent?
	jz	sndbr1		; no, wait a bit more
;
;       Unmask command register
	mvi	a,80h
	sta	mnprtc
;
;       Begin sending a break by setting bit in UART command register
	mvi	a,3Fh		; Set TxEna, DTR, RxEna, SBreak, ErrRst, RTS
	sta	mnprts
;
;       Wait for 250 milliseconds (using hundredths second delay routine)
	mvi	a,25
	call	delay
;
;       Resume normal operation by clearing the SendBreak command bit
	mvi	a,37h		;Set TxEna, DTR, RxEna, ErrRst, RTS
	sta	mnprts
;
;       Remask command register
	mvi	a,0
	sta	mnprtc
;
	ret			;done
ENDIF;[22] apcps

;

;
;       sysflt - system-dependent filter
;       called with character in E.
;       if this character should not be printed, return with A = zero.
;       preserves bc, de, hl.
;       note: <xon>,<xoff>,<del>, and <nul> are always discarded.
sysflt:
	mov	a,e		; get character for testing

	ret

;       mdmflt - modem filter [30]
;       called with character to be sent to printer in E
;       with parity set as appropriate.
;       return with accumulator = 0 do do nothing,
;                               <> 0 to send char in E.
mdmflt:
	mov	a,e		;[30] get character to test
	ret



;       prtflt - printer filter [30]
;       called with character to be sent to printer in E
;       returns with a = 0 to do nothing
;                    a <> 0 to print it.
;
;       this routine for those printer that automatically insert
;       a lf on cr, or cr for lf.  Should this be shifted to 
;       the system indep. stuff, in say 4.06?
prtflt:
	mov	a,e		; [30] get character to test

	ret


;

;
; system-dependent processing for BYE command.
;  for apmmdm, heath, and lobo, hang up the phone.
sysbye:
IF apmmdm
	xra	a		;Hangup our end, too.
	sta	mnmodm
ENDIF;apmmdm

	ret
;

;       This is the system-dependent command to change the baud rate.
;       DE contains the two-byte value from the baud rate table; this
;       value is also stored in 'speed'.
sysspd:

; Set the speed for Apple with 6551 ACIA
IF ap6551
	lda	mnprtc		;jb read control port
	ani	0F0H		;jb zap low order nybble
	ora	e		;jb put rate in low order nybble
	sta	mnprtc		;jb send to control port
	ret
ENDIF;ap6551

; Set the speed for Apple with CPS Multifunction card
IF apcps		;[22]
	mvi	a,80h
	sta	mnprtc
	lda	mnprts		;read command register to reset 2651
	mvi	a,apmod1	;first mode byte
	sta	mnport
	mvi	a,4		;waste some time before sending second byte
spdwt:	dcr	a		; 4 T-states
	jnz	spdwt		; 10 T-states
	mov	a,e		;second mode byte is speed byte
	sta	mnport
	mvi	a,apcmd		;command byte
	sta	mnprts
	xra	a
	sta	mnprtc
	ret
ENDIF;[22] apcps


IF ap6551					;jb
spdtbl:	db	0DH				;jb 13 entries
	db	03H,'110$',     03H,03H ;jb
	db	04H,'1200$',    08H,08H ;jb
	db	05H,'134.5$',   04H,04H ;jb
	db	03H,'150$',     05H,05H ;jb
	db	04H,'1800$',    09H,09H ;jb
	db	05H,'19200$',   0FH,0FH ;jb
	db	04H,'2400$',    0AH,0AH ;jb
	db	03H,'300$',     06H,06H ;jb
	db	04H,'3600$',    0BH,0BH ;jb
	db	04H,'4800$',    0CH,0CH ;jb
	db	03H,'600$',     07H,07H ;jb
	db	04H,'7200$',    0DH,0DH ;jb
	db	04H,'9600$',    0EH,0EH ;jb

sphtbl:	db	cr,lf,'  110    134.5  150    300    600   1200   1800'
	db	cr,lf,' 2400   3600   4800   7200   9600  19200$'
ENDIF;ap6551

IF apcps		;[22]
spdtbl:	db	10H				; 16 entries
	db	03H,'110$',     32h,32h
	db	04H,'1200$',    37h,37h
	db	05H,'134.5$',   33h,33h
	db	03H,'150$',     34h,34h
	db	04H,'1800$',    38h,38h
	db	05H,'19200$',   3fh,3fh
	db	04H,'2000$',    39h,39h
	db	04H,'2400$',    3ah,3ah
	db	03H,'300$',     35h,35h
	db	04H,'3600$',    3bh,3bh
	db	04H,'4800$',    3ch,3ch
	db	02H,'50$',      30h,30h
	db	03H,'600$',     36h,36h
	db	04H,'7200$',    3dh,3dh
	db	02H,'75$',      31h,31h
	db	04H,'9600$',    3eh,3eh

sphtbl:	db	cr,lf,'   50    75   110    134.5  150    300    600   1200'
	db	cr,lf,' 1800  2000  2400   3600   4800   7200   9600  19200$'
ENDIF;[22] apcps



; The following conditionals were once a huge if not statement.  There
; wasn't enough room to add the lobo to the list, so it had to be broken
; into 2, which you can't do with an if not.  I redid it as two ifs and
; applied them to those that wouldn't set baud. [Hal Hostetler]
IF apmmdm OR ap6850 ;[32]
spdtbl	equ	0		; SET BAUD not supported.
sphtbl	equ	0
ENDIF;appmdm OR ap6850 [32]
;
;
;       no ports available for Apple
prttbl	EQU	0		;SET PORT not supported
prhtbl	EQU	0

;
;

;       This is the system-dependent SET PORT command.
;       HL contains the argument from the command table.
sysprt:

	ret
;

;
;       selmdm - select modem port
;       selcon - select console port
;       selmdm is called before using inpmdm or outmdm;
;       selcon is called before using inpcon or outcon.
;       For iobyt systems, diddle the I/O byte to select console or comm port;
;       For Decision I, switches Multi I/O board to console or modem serial
;       port.  [Toad Hall]
;       For the rest, does nothing.
;       preserves bc, de, hl.
selmdm:
	ret

selcon:
	ret
;

;       Get character from console, or return zero.
;       result is returned in A.  destroys bc, de, hl.
;
inpcon:
	mvi	c,dconio	;Direct console I/O BDOS call.
	mvi	e,0FFH		;Input.
	call	BDOS
	ret
;

;
;       Output character in E to the console.
;       destroys bc, de, hl
;
outcon:

	mvi	c,dconio	;Console output bdos call.
	call	bdos		;Output the char to the console.
	ret
;

;
;       outmdm - output a char from E to the modem.
;               the parity bit has been set as necessary.
;       returns nonskip; bc, de, hl preserved.
outmdm:
IF apple 
	push	h
outmd1:	lxi	h,mnprts	;address of the port status register
outmd2:	mov	a,m		; get port status in A
	ani	output		;Loop till ready.
	jz	outmd2
outmd3:	lxi	h,mnport	;address of port data register
	mov	m,e		; write the character
	pop	h
	ret
ENDIF;apple 




;

;
;       get character from modem; return zero if none available.
;       for IOBYT systems, the modem port has already been selected.
;       destroys bc, de, hl.
inpmdm:
IF apple 
inpmd1:	lda	mnprts		;Get the port status into A.
	ani	input		;See if the input ready bit is on.
	rz			;If not then return.
inpmd2:	lda	mnport		;If so, get the char.
ENDIF;apple


ret			; return with character in A


;
;       flsmdm - flush comm line.
;       Modem is selected.
;       Currently, just gets characters until none are available.

flsmdm:	call	inpmdm		; Try to get a character
	ora	a		; Got one?
	jnz	flsmdm		; If so, try for another
	ret			; Receiver is drained.  Return.
;

;
;       lptstat - get the printer status. Return a=0ffh if ok, or 0 if not.
lptstat:
	xra	a
	ret

;
;       outlpt - output character in E to printer
;       console is selected.
;       preserves de.
outlpt:
	push	d		; save DE in either case
	call	prtflt		; go through printer filter [30]
	ana	a		; if A = 0 do nothing,
	jz	outlp1		; [30] if a=0 do nothing

	mvi	c,lstout
	call	bdos		;Char to printer

outlp1:	pop	d		; restore saved register pair
	ret
;

;
;       Screen manipulation routines
;       csrpos - move to row B, column C
;
;       csrpos for terminals that use a leadin sequence followed
;        by (row + 31.) and (column + 31.)
;
csrpos:	push	b		; save coordinates
	lxi	d,curldn	; get cursor leadin sequence
	call	prtstr		; print it
	pop	h		; restore coordinates
	mov	a,h		; get row
	adi	(' '-1)         ; space is row one
	mov	e,a
	push	h
	call	outcon		; output row
	pop	h
	mov	a,l		; get column
	adi	(' '-1)         ; space is column one
	mov	e,a
	jmp	outcon		; output it and return

;
; delchr - make delete look like a backspace.  Unless delete is a printing
;       character, we just need to print a backspace. (we'll output clrspc
;       afterwards)
;       For Kaypro and Vector General, delete puts a blotch on the screen.
;       For Apple and Osborne 1, delete moves but doesn't print.
delchr:

	lxi	d,delstr
	jmp	prtstr

; erase the character at the current cursor position
clrspc:	mvi	e,' '
	call	outcon
	mvi	e,bs		;get a backspace
	jmp	outcon

; erase the current line
clrlin:	lxi	d,eralin
	jmp	prtstr

; erase the whole screen, and go home. preserves b (but not c)
clrtop:	lxi	d,erascr
	jmp	prtstr

IF apple
sysver:	db	'Apple II CP/M$'
outlin:	db	('^'-100O),esc,'Y',cr,lf,'  $'
erascr:	db	('^'-100O),esc,'Y$'     ;Clear screen and go home.
eralin:	db	cr,esc,'T$'             ;Clear line.
delstr:	db	bs,bs,'$'               ; Adjust for delete
curldn:	db	esc,'=$'                ;Cursor lead-in
ttab:					;Table start location.
ta:	db	('K'-100O),'$',0,0      ;Cursor up.
tb:	db	12O,'$',0,0             ;Cursor down.
tc:	db	('F'-100O),'$',0,0      ;Cursor right.
td:	db	'$',0,0,0               ;(can't) Cursor left
te:	db	'$',0,0,0               ;(can't) Clear display
tf:	db	'$',0,0,0               ;(can't) Enter graphics mode
tg:	db	'$',0,0,0               ;(can't) Exit graphics mode
th:	db	('^'-100O),'$',0,0      ;Cursor home.
ti:	db	('K'-100O),'$',0,0      ;Reverse linefeed.
tj:	db	esc,'Y$',0              ;Clear to end of screen.
tk:	db	esc,'T$',0              ;Clear to end of line.
ENDIF;apple

ovlend	equ	$	; End of overlay

	END
