;---------------------------------------------------------------------------
; 
; PATCHES.ASM
;    This file contains patches to Altair 4K BASIC 4.0 to direct console
;    I/O through an alternate serial port based on sense switches A15 and
;    A14. If A14 is raised, console input comes from the alternate port
;    as well as the BASIC console. If A15 is raised, console output goes
;    to the alternate port as well as the BASIC console. This allows
;    loading and saving programs through what BASIC thinks is the console
;    (as done with a Teletype), but is actually an alternate port con-
;    nected to a cassette or paper tape reader
;
;    The content of this file is initially at the end of the BASIC image
;    on tape, however, BASIC reclaims RAM after initialization by freeing
;    up the space used by the initialization code. The initialization code
;    is at the end of the BASIC image, so the patch code would be freed as
;    well if left in the same location. To get around this problem, the
;    initialization code for this file copies the patch subroutines down
;    into the BASIC image immediately following the trig routines and then
;    adjusts the end of BASIC to point just past the end of the patch.
;
;    Ver      Date	Notes
;    ---    --------	----------------------------------------------------
;    1.0    02/24/20	Original, Mike Douglas
;    1.1    03/01/20	Allow assembly time selection of a variety of serial
; 		     	ports instead of only SIO at 6/7 
;    1.2    10/04/20	Swap sense switch A15 and A14 so A15 enables output
;			and A14 selects input 
;---------------------------------------------------------------------------
;
; Following are patches that must be manually applied to the BASIC image
;     in order to properly call the init routine below.
;
;  0E0Ch	0E		(memory test starts at 0Exx)
;     change to
;	  	0F		(change to start at 0Fxx)
;---------
;
;  0E84h	xchg		EB
;		mvi m,0		36 00
;     change to
;	  	call init	CD 40 0F (value of init label below)
;---------------------------------------------------------------------------

TRUE	equ	1
FALSE	equ	0

; Select the alternate serial port here

SIO00	equ	FALSE		;88-SIO at 0/1
SIO06	equ	TRUE		;88-SIO at 6/7 (e.g., cassette)
S2IO10	equ	FALSE		;88-2SIO at 10h/11h
S2IO12	equ	FALSE		;88-2SIO at 12h/13h

; SIO at 0/1

  if SIO00
ALTSR	equ	00h		;status register
ALTDR	equ	01h		;data register
ALTPOL	equ	0FFh		;status polarity (FFh to invert)
ALTRDA	equ	01h		;received data available mask
ALTTRE	equ	80h		;transmit register empty mask
  endif
		
; SIO at 6/7 (cassette)

  if SIO06
ALTSR	equ	06h		;status register
ALTDR	equ	07h		;data register
ALTPOL	equ	0FFh		;status polarity (FFh to invert)
ALTRDA	equ	01h		;received data available mask
ALTTRE	equ	80h		;transmit register empty mask
  endif

; 2SIO at 10h/11h

  if S2IO10
ALTSR	equ	10h		;status register
ALTDR	equ	11h		;data register
ALTPOL	equ	00h		;status polarity (FFh to invert)
ALTRDA	equ	01h		;received data available mask
ALTTRE	equ	02h		;transmit register empty mask
  endif

; 2SIO at 12h/13h

  if S2IO12
ALTSR	equ	12h		;status register
ALTDR	equ	13h		;data register
ALTPOL	equ	00h		;status polarity (FFh to invert)
ALTRDA	equ	01h		;received data available mask
ALTTRE	equ	02h		;transmit register empty mask
  endif

;------------------------------------------------------------------------
; init - This code is called when BASIC has completed the startup dialog
;     and is about to use the value in DE to set the address for the end
;     of BASIC (start of RAM). This code copies the patch subroutines
;     from their load location down into the BASIC image just past the
;     trig functions, then modifies DE to adjust the end of BASIC to
;     just past the relocated patch code.
;------------------------------------------------------------------------
	org	0F40h		;start above BASIC's initial stack

init	push	b		;save registers
	push	h

	lxi	d,patch		;DE=source address
	lxi	h,pDest		;HL=destination address
	mvi	b,pSize		;B=length to copy

move	ldax	d		;move the patch down into BASIC
	mov	m,a
	inx	h
	inx	d
	dcr	b
	jnz	move

; Get port number, bit mask, and jump polarity for the console port
;    from the BASIC image.

	lda	0394h		;console input port
	sta	input+1		;patch our code
	inr	a		;console data port
	sta	input+8		;patch our code

	lhld	0396h		;console input status mask
	shld	input+3		;   and jump instruction

	mvi	a,JMP		;insert jump to our output code 
	sta	038Fh		;   into the BASIC image
	lxi	h,output
	shld	0390h

	sta	0393h		;insert jump to our input code
	lxi	h,input		;   into the BASIC image
	shld	0394h

  if (S2IO10 or S2IO12)
	mvi	a,03h		;reset ACIA on 2SIO board
	out	ALTSR

	mvi	a,11h		;8N2
	out	ALTSR
  endif

	pop	h		;restore registers
	pop	b

	lxi	d,pEnd-pOffset	;D=new address for end of BASIC
	xchg			;do original xchg
	mvi	m,0		;do original move
	ret

;------------------------------------------------------------------------
; The patch code that remains in BASIC starts here.
;------------------------------------------------------------------------
patch	equ	$
pDest	equ	0D24h		;destination address of the patch
pOffset	equ	$-pDest		;offset of addresses to final location

; Console input patch - if A14 raised, input from alternate port as well as
;    the normal console.

input	equ	$-pOffset
	in	0		;port number gets patched
	ani	0		;mask is patched
	jz	inAlt		;jump opcode patched, followed if no char

	in	0		;port number gets patched
	ani	7Fh
	ret			;return character

inAlt	equ	$-pOffset
	in	0FFh		;A=sense switches
	ani	40h		;A14 raised?
	jz	input		;no, ignore alternate port

	in	ALTSR		;A=UART status register
	xri	ALTPOL		;invert status bits if needed
	ani	ALTRDA		;receive data available?
	jz	input		;no

	in	ALTDR		;get the character
	ani	7Fh		;strip parity
	ret			;return to original caller

; Console output patch - the character has already been output to
;    the console. If A15 raised, output the character on the stack
;    to the alternate port as well

output	equ	$-pOffset
	in	0FFh		;A=sense switches
	ani	80h		;A15 raised?
	jnz	outAlt		;yes, output to the alternate port
	
	pop	psw		;remove saved character
	ret			;and exit

outAlt	equ	$-pOffset
	in	ALTSR		;A=UART status register
	xri	ALTPOL		;invert status bits if needed
	ani	ALTTRE		;xmit register empty?
	jz	output		;no

	pop	psw		;get back character to send
	out	ALTDR		;send it
	ret

pEnd	equ	$
pSize	equ	pEnd-patch	;length of patch code


	end
