;---------------------------------------------------------------------------
;
;  WRTAPE - This CP/M program writes the specified file to the cassette 
;	serial port. Typically this is used to write original Altair 
;	software like 4K BASIC, 8K BASIC, etc. to a cassette tape. 
;
;	Ver	Date	    Desc
;	1.0    10/20/13   Initial version
;	1.1    06/18/23   Allow selection of 2nd port on an 88-2SIO for
;			  output. Default to .TAP extension. Add help
;			  banner if no parameters entered.
;
;---------------------------------------------------------------------------
; BDOS Equates

BDOS	equ	5		;entry point for BDOS calls
CONIN	equ	1		;console input
PRINT	equ	9		;display message on console
CONSTAT	equ	11		;get console status
OPEN	equ	15		;0FFh = file not found
READ	equ	20		;0=OK, 1=EOF
FCB	equ	5Ch		;system FCB
PARAM1	equ	FCB+1		;command line parameter 1 in FCB
PARAM2	equ	PARAM1+16	;command line parameter 2
SYSBUF	equ	80h		;system I/O buffer

; 88-SIO Equates (original cassette interface)

SIOSR	equ	6		;status register
SIODR	equ	7		;data register
TBE	equ	80h		;transmit buffer empty

; 88-2SIO Equates (2nd port on 2SIO)

SIO2SR	equ	12h		;status register
SIO2DR	equ	13h		;data register
TDRE	equ	02h		;transmit data register empty

; Misc Equates

CR	equ	13		;carriage return
LF	equ	10		;line feed
BS	equ	8		;back space
CTRLC	equ	3		;control-c

	org	100h
;----------------------------------------------------------------------------
;  Open the specified file and copy it to the specified port
;----------------------------------------------------------------------------
	lda	PARAM1		;file specified?
	cpi	' '
	jnz	havep1		;yes
	lxi	d,mHelp		;else display usage message
	mvi	c,print
	call	bdos
	ret			;return to CPM

havep1	mvi	a,3		;reset possible 2SIO port
	out	SIO2SR
	mvi	a,15h		;rts on, 8N1
	out	SIO2SR

	lda	PARAM1+8	;extension present?
	cpi	' '
	jnz 	haveExt		;yes	
	lxi	h,'T'+256*'A'	;else, 'TAP' default extension
	shld	PARAM1+8	
	mvi	a,'P'
	sta	PARAM1+10
	
haveExt	lda	PARAM2		;2SIO specified?
	cpi	'2'
	jnz	openFil		;normal ACR, go open file

; Patch code for 2SIO instead of the SIO useed with the ACR

	mvi	a,SIO2SR	;point to 2SIO status register
	sta	sndLoop+1
	inr	a		;point to 2SIO data register
	sta	datPort+1
	mvi	a,TDRE		;ready to xmit bit for 2SIO
	sta	maskVal+1
	xra	a		;2SIO status is not inverted
	sta	xorVal+1

; Loop reading 128 byte sectors and writing through serial port until EOF

openFil	lxi	d,FCB		;system FCB has specified file
	mvi	c,OPEN		;open the file on the command line
	call	BDOS
	inr	a		;0FFh = open error
	jz	badFile

	xra	a
	sta	secNum		;init sector number to zero
	lxi	d,mSector	;"Sector: 00"
	mvi	c,PRINT
	call	BDOS

sendSec	lxi	d,FCB		;read next sector
	mvi	c,READ
	call	BDOS
	ora	a		;0 = successful sector read
	jnz	allDone		;finished

; Send the sector out the cassette or 2SIO port

	lxi	h,SYSBUF	;sector was read into SYSBUF
	shld	bufPtr		;save as bufPtr

sndLoop	in	SIOSR		;ready to xmit?
xorVal	xri	0FFh		;ACR to positive logic
maskVal	ani	TBE		;transmit buffer empty?	
	jnz	sndChar		;yes, send next byte

	call	chkAbrt		;else, abort requested?
	jnz	sndLoop		;no, keep sending
	jmp	abort		;else, abort requestd

; sndChar - Port is ready for next character, send it

sndChar	lhld	bufPtr		;h = pointer to next character
	mov	a,m		;get byte to send
datPort	out	SIODR		;and send it
	inx	h
	shld	bufPtr

	mov	a,h		;only works if buffer at 080h-0FFh
	cpi	1
	jnz	sndLoop

; Increment and display the sector number

	lxi	h,secNum	;increment secNum
	inr	m	
	mov	a,m
	lxi	h,secHex	;move secNum to secHex in ASCII
	call	outHex

	lxi	d,mSecHex	;display current sector number
	mvi	c,PRINT
	call	BDOS
	jmp	sendSec		;repeat
	
;-----------------------------------------------------------------------------
; Check for user typing Ctrl-C to abort. Returns zero if CTRL-C typed.
;-----------------------------------------------------------------------------
chkAbrt	mvi	c,CONSTAT	;see if user has typed anything
	call	BDOS
	ora	a	
	jz	caExit		;nothing typed, exit

	mvi	c,CONIN		;read the character
	call	BDOS
caExit	cpi	CTRLC		;control-c typed?
	ret
	
;-----------------------------------------------------------------------------
;  outHex - convert the value in A to ASCII hex pointed to by H.
;     Clobbers A,B,H.
;-----------------------------------------------------------------------------
outHex	mov	b,a		;save a
	rrc			;put msb into lsb
	rrc
	rrc
	rrc
	ani	0fh		;get nibble alone

	adi	'0'		;convert to printable ASCII
	cpi	03Ah		;A-F?
	jc	msnDone

	adi	7		;make A to F printable 

msnDone	mov	m,a		;store the printable msn

	inx	h		;move to the lsn
	mov	a,b		;get original byte back
	ani	0fh		;get nibble alone

	adi	'0'		;convert to printable ASCII
	cpi	03Ah		;A-F?
	jc	lsnDone

	adi	7		;make A to F printable 

lsnDone	mov	m,a		;store the printable lsn
	ret

;--------------------------------------------------------------------------
; Exit routines
;--------------------------------------------------------------------------
badFile	lxi	d,mNoFile	;file not found message
	mvi	c,PRINT
	call	BDOS
	ret			;exit to CP/M

abort	lxi	d,mAbort	;operation aborted message
	mvi	c,PRINT
	call	BDOS
	ret			;exit to CP/M

allDone	lxi	d,mDone		;operation complete message
	mvi	c,PRINT
	call	BDOS
	ret			;exit to CP/M

;-------------------------------------------------------------------------
; Data and variables
;-------------------------------------------------------------------------
mHelp	db	CR,LF,'WRTAPE Ver 1.1',CR,LF,LF
	db	'Writes an Altair cassette or paper tape image to',CR,LF
	db	'the ACR port (SIO at 6/7) or the 2nd port on an',CR,LF
	db	'88-2SIO (at 12h/13h)',CR,LF,LF
	db	'Usage: WRTAPE file[.ext] [2]',CR,LF
	db	'       Default extension is .TAP',CR,LF
	db	'       ''2'' specifies 2nd port on 2SIO',CR,LF,'$'

mNoFile	db	CR,LF,'Specified File Not Found',CR,LF,'$'
mAbort	db	CR,LF,LF,'File Transfer Aborted',CR,LF,'$'
mDone	db	CR,LF,LF,'File Transfer Complete',CR,LF,'$'
mSector	db	CR,LF,'Current Sector: 00$'
mSecHex	db	BS,BS
secHex	db	'00$'		;ASCII hex sector number ends up here
secNum	ds	1		;sector number in binary
bufPtr	ds	2		;pointer into disk sector

	end
