	.Z80
	PAGE 70

;	TX.MAC
;	transmits files from the ELZET80 via console.

;	First, TX sends the file name, a string 
;	of up to 14 (2+8+'.'+3) chars followed by a 00H.
;	TX expects to receive as acknowledgement of a 
;	non-empty file name the ACK char (0FH).

;	Next, it sends a 2-byte integer denoting the file 
;	length in 128-byte records (low byte, then high byte).
;	Again, TX waits for acknowledgement of a non-zero 
;	file length.

;	Then, the signalled number of records are transmitted.
;	Each record is expected to be acknowledged with an
;	ACK char.

;	If an ambiguous file name was given, this whole 
;	procedure is repeated for all files that match the 
;	ambiguous name.

;	End of transmission is signalled with an empty file name.

;	31 Jan 1988	A.Herrmann
;

TX::	JP START

;--------------- Constants and Variables ---------

BDOS	EQU 5		;BDOS ENTRY ADDRESS
FBASE	EQU 6		;FDOS BASE ADDRESS

CONSTA	EQU 7		;CONSOLE STATUS PORT
CONDAT	EQU 5		;CONSOLE DATA PORT

ACK	EQU 0FH		;ACKNOWLEDGE CHAR

RECSIZ	EQU 128		;FILE RECORD SIZE

BUFSTA:	DW 0		;BUFFER START
BUFEND:	DW 0		;BUFFER END
DATEND:	DW 0		;DATA END

INPBUF:	DB 14		;INPUT STRING BUFFER
	DB 0		;ACTUAL LENGTH
	DS 14,0
	DB 0		;NULL TERMINATOR

STRING:	DS 5,0		;OUTPUT STRING BUFFER
CRLF:	DB 0DH,0AH,'$'

FCB:	DS 36		;FILE CONTROL BLOCK
FCBLEN	EQU $-FCB

FILSIZ:	DW 0		;FILE SIZE IN RECORDS

;--------------- Output Strings ------------------

HEADER:	DB 0CH
	DB 'TX.MAC - Version 0.1   30 Jan 88  (C) A.Herrmann'
	DB 0DH,0AH,0AH,'$'

ASKMSG:	DB 0DH,0AH
	DB 'Enter file name: '
	DB '$'

;--------------- Start of program ------------------

START:	LD DE,HEADER	;Header message
	CALL PRINT

	LD HL,PRGEND	;Find data buffer start
	LD L,0
	INC H
	LD (BUFSTA),HL

;--------------- Main loop ------------------------

ASKNAM:			;Ask user for (possibly
	LD HL,ASKMSG	; ambiguous) file name
	CALL PRINT
	LD DE,INPBUF
	CALL GETSTR	;Get string from console
	LD HL,INPBUF+2	;Teminate string with 00
	LD A,(INPBUF+1)
	OR A
	PUSH AF
	ADD A,L
	LD L,A
	LD (HL),0
	POP AF
	JP NZ,NXTNAM

	
	



NXTNAM:			;Handle next file name

BLDFCB:			;Build File Control Block 
	LD DE,INPBUF
	SBC HL,DE
	JP Z,EXIT	;If empty file name, exit.

	EX DE,HL
	LD DE,FCB
	CALL BF

	CALL SNDACK	;acknowledge reception of name

RXSIZE:			;Receive file size (2-byte integer)
	CALL GETCH	;Low Byte first
	LD L,A
	PUSH HL
	CALL GETCH	;High byte last
	POP HL
	LD H,A
	LD (FILSIZ),HL
	CALL SNDACK	;acknowledge reception of size

RXDATA:			;Receive file data 
	LD HL,(BUFSTA)	;HL = buffer start
RXDAT1:
	PUSH HL
	LD HL,(FILSIZ)
	LD A,L		;More bytes to come?
	OR H
	POP HL
	JP Z,DONE

	LD BC,RECSIZ
RXDAT2:			;Get next record
	PUSH BC
	PUSH HL
	CALL GETCH	;Get next char from console
	POP HL
	POP BC
	LD (HL),A	; and store it away
	INC HL
	EX DE,HL	;End of buffer reached?
	LD HL,(BUFEND)
	SCF
	SBC HL,DE
	EX DE,HL
	CALL C,OVRFLW

	DEC BC
	LD A,B		;Record complete?
	OR C
	JP NZ,RXDAT2

	PUSH HL		;Yes, decrement record counter
	LD HL,(FILSIZ)
	DEC HL
	LD (FILSIZ),HL
	CALL SNDACK	;Acknowledge record
	POP HL
	JP RXDAT1

OVRFLW:			;flush data buffer to disk
	PUSH HL
	PUSH BC

	POP BC
	POP HL
	RET		;For the time being...

;--------------- Write file to disk --------------

DONE:
	LD A,L		;Fill record (i.e.,
	AND 07FH	; up to ..7FH or ..FFH)
	JR Z,OKAY
	XOR A		;with 0's
	LD (HL),A	;(Actually, this shouldn't
	INC HL		;be necessary.)
	JR DONE

OKAY:	LD (DATEND),HL	;Save end of actual data
	LD DE,(BUFSTA)
	XOR A
	SBC HL,DE	;Calculate file length
	JR Z,EMPTY	;File empty?

	LD C,19		;Delete file if already exists
	LD DE,FCB
	CALL BDOS

	LD C,22		;Open file
	LD DE,FCB
	CALL BDOS

	LD DE,(BUFSTA)	;First write address

WLOOP:			;Loop writing to disk
	PUSH DE
	LD C,26		;Set DMA to DE
	CALL BDOS
	LD DE,FCB
	LD C,21		;Write record
	CALL BDOS
	POP DE
	OR A		;Error?
	JR NZ,DSKERR

	LD HL,RECSIZ	;No,
	ADD HL,DE	;go to next record
	EX DE,HL
	LD HL,(DATEND)	;End of data reached?
	SCF
	SBC HL,DE
	JR NC,WLOOP

	LD C,16		;Yes, close file
	LD DE,FCB
	CALL BDOS
	CP 0FFH
	JR Z,DSKERR

EMPTY:
	JP RXNAME	;Continue with next file


;--------------- End of main loop -----------------

DSKERR:
	JP EXIT

;--------------- SUBROUTINEN ----------------------

GETCH:			;Get char from console port
	IN A,(CONSTA)
	BIT 0,A
	JR Z,GETCH	;Wait until character available
	IN A,(CONDAT)
	RET		;Return char in A

SNDACK:			;Send acknowledge char via console
	IN A,(CONSTA)
	BIT 2,A
	JR Z,SNDACK	;Wait until transmit buffer empty
	LD A,ACK
	OUT (CONDAT),A
	RET

PRINT:			;String output to console
	LD C,9
	CALL BDOS
	RET

GETSTR:			;Get input string from console
	LD C,10
	CALL BDOS
	RET

CONVRT:			;Convert integer in HL
	LD DE,STRING	; into 5-digit string
	LD BC,5

	EX DE,HL
	ADD HL,BC
	DEC HL
	EX DE,HL
	LD B,C

	PUSH DE		;HL >= A000H ?
	EX DE,HL
	LD HL,0A000H
	SCF
	SBC HL,DE
	JR NC,CONVR0

	EX DE,HL	;Yes, subtract A000H = 40960 
	LD DE,0A000H
	CCF
	SBC HL,DE
	CALL DIV10	; and divide only the difference
	POP DE
	CALL RCODE	;Encode the remainder
	DEC B		;lest one digit
	PUSH DE
	LD DE,1000H	;add 1000H = 4096 to the quotient
	ADD HL,DE
	POP DE
	JR CONVR1	; and continue as normally

CONVR0:	EX DE,HL
	POP DE

CONVR1: CALL DIV10	;Divide HL by 10
	CALL RCODE	;Encode remainder
	DJNZ CONVR1	;all digits generated?
	RET

DIV10:			;divides HL by 10
	PUSH BC
	PUSH DE
	LD B,12		;Dividend has 12+4 bits
	LD D,0A0H	;Divisor (=10) in upper half byte of D
	LD E,0
DIVBIT:	XOR A		;Clear carry flag
	ADD HL,HL	;Shift HL 1 bit left
	PUSH AF		;Sace carry flag
	XOR A		;Clear carry flag
	SBC HL,DE	;DE fits in HL?
	INC HL		;Let's assume so
	JR NC,DIVCNT	;If not,
	POP AF		; and if no carry before,
	JR C,DIVNXT
	ADD HL,DE	; subtract and
	DEC HL		; decrement quotient
	JR DIVNXT
DIVCNT:	POP AF
DIVNXT:	DJNZ DIVBIT
	POP DE
	POP BC
	RET

RCODE:			;encodes the remainder in HL
	LD A,H		;shift remainder from upper half byte
	RR A		;into lower half byte
	RR A
	RR A
	RR A
	AND 0FH		;Mask it
	OR 30H		;Encode it as ASCII char
	LD (DE),A	;Store it
	LD A,H		;New quotient is in L and
	AND 0FH		;lower half byte of H
	LD H,A
	DEC DE
	RET


;BF - Build File Control Block
; formats a 00H-byte-terminated string (presumed to be a filename)
; into an FCB, setting the disk and file name and type and clearing
; the remainder of the FCB to 0's.

; Entry parameters:
;	DE -> File Control Block (36 Bytes)
;	HL -> File name string (00H-byte terminated)

; Exit parameters
;	The built File Control Block

; Calling sequence
;	LD DE,FCB
;	LD HL,FILENAME
;	CALL BF

; This subroutine was taken from Andy Johnson-Laird's
; "The Programmer's CP/M Handbook", pp.100 (vi. for more
; comments)

BF:
	INC HL
	LD A,(HL)
	DEC HL
	CP ':'
	JP NZ,BF$ND
	LD A,(HL)
	AND 00011111B
	INC HL
	INC HL
	JP BF$SD

BF$ND:
	XOR A
BF$SD:
	LD (DE),A
	INC DE
	LD C,8
	CALL BF$GT

	CP '.'
	JP NZ,BF$NT
	INC HL

BF$NT:
	LD C,3
	CALL BF$GT

	LD B,0
	LD C,24
	CALL BF$FT
	RET

BF$GT:
	LD A,(HL)
	OR A
	JP Z,BF$SFT
	CP '*'
	JP Z,BF$QFT
	CP '.'

	JP Z,BF$SFT
	LD (DE),A

	INC DE
	INC HL
	DEC C
	JP NZ,BF$GT

BF$SKIP:
	LD A,(HL)
	OR A
	RET Z
	CP '.'
	RET Z
	INC HL
	JP BF$SKIP

BF$SFT:
	LD B,' '
	JP BF$FT

BF$QFT:
	LD B,'?'
	CALL BF$FT
	JP BF$SKIP

BF$FT:
	PUSH AF
	LD A,B

BF$FTL:
	LD (DE),A
	INC DE
	DEC C
	JP NZ, BF$FTL
	POP AF
	RET



;--------------- PROGRAMM-ENDE --------------------

EXIT:	JP 0		;WARMBOOT

PRGEND	EQU $

	END
