; Modified from the PE1CHL version to work with NOS.
; This file cannot be used with the PE1CHL sources.
; 1/21/90
; Ken Mitchum, KY3B km@speedy.cs.pitt.edu km@cadre.dsl.pitt.edu
; SCC interrupt handler for IBM-PC

; Comments sprayed liberally by G8FSL


include asmgloba.h
	LOCALS
	%MACS
	.LALL

	extrn	Stktop,Spsave,Sssave,doret:proc,eoi:proc
;	extrn	scctim:proc,porg:proc
	extrn	Sccvecloc,Sccpolltab,Sccmaxvec:byte

ifdef	LARGEDATA
	extrn	Sccchan:dword
else
	extrn	Sccchan:word
endif
	
	.CODE
dbase	dw	@Data		; save loc for ds (must be in code segment)


;=====================================================================
; sccvec is the interrupt handler for SCC interrupts using INTACK
;=====================================================================

	public	sccvec
	label	sccvec	far

	cli			; this code is not re-entrant, so make sure it
				; is not interrupted. some multi-taskers
				; intercept interrupt handlers, so be careful!


	push	ds		; save on user stack

	mov	ds,cs:dbase

	mov	Sssave,ss	; stash user stack context
	mov	Spsave,sp

	mov	ss,cs:dbase	; set up interrupt stack
	lea	sp,Stktop

	PUSHALL			; save user regs on interrupt stack

	push	es
	call	eoi
ifndef	LARGEDATA
	mov	es,ax		; small data assumes ES == DS
endif
	cld			; in case "movsb" or "movsw" is used


; Read SCC interrupt vector and check it

sccint:	mov	dx,Sccvecloc
	out	dx,al		; Generate INTACK by writing to Sccvecloc.
	jmp	short d1	; Delay
d1:	jmp	short d2
d2:	jmp	short d3
				; Because 'No Vector' bit is clear in WR9, when
				; INTACK is used the SCC requiring service puts
				; the vector on the data bus for the next read.
d3:	in	al,dx		; Read the vector.
	cmp	al,Sccmaxvec	; Check for a legal vector
	jnc	clrret		; It should not be >= the maximum
				; If it is, ignore the interrupt

; Extract channel number and status from vector. Determine handler address.

; Vector includes status. Top 4 bits are set with SCC channel number in init
; code (SCC.C).

	mov	bl,al		; Copy vector (need it later for status)
	shr	bl,1		; Discard least significant bit
	jc	clrret		; It should not be a 1
	and	bx,7ch		; Clear 2 reason bits, leaving high order bits
				; and the A/B channel bit. Also clear BH.
	xor	bl,04h		; Toggle A/B channel bit. Now A=0,B=1.

				; Now use BX as an index into Sccchan array.
				; Sccchan (in SCC.C) is an array of pointers
				; to scc channel structures.

ifdef	LARGEDATA
	les	si,Sccchan[bx]	; Sccchan entries are 32 bit pointers if LARGEDATA.
else
	shr	bl,1		; Discard another bit (Sccchan pointers are 16 bit).
	mov	si,Sccchan[bx]	; Read address of channel structure.
endif
ifdef	LARGEDATA		; Test for NULL.
	push	ax		; If LARGEDATA, both ES and SI = 0 indicates
	mov	ax,es		; the board has been attached, but this
	test	ax,ax		; channel hasn't.
	pop	ax
	jne	nn0
endif
	test	si,si		; Test for NULL.
	je	clrret		; No channel struct, ignore it.
nn0:				; AX still holds the original vector, including status.
	and	ax,06h		; Isolate status info from vector.
	add	ax,ax		; Double it - make index in FAR PTR array.
	mov	bx,ax		; It must be in BX, so move it.

; Call the handler (defined in C), with Sccchan struct as a parameter

; Note that ES:SI is the pointer to the Sccchan struct, and that bits 1 and 2
; of the Vector (i.e. the status bits, identifing the cause of the interrupt)
; are about to be added to the pointer.
; This means that the first 4 elements of the Sccchan struct MUST be:
;	Transmit Buffer Empty handler pointer
;	External/Status Conditions handler pointer
;	Receive Character Available handler pointer
;	Special Receive Condition handler pointer
; This is expressed in SCC.H too.
; See also a few lines below - the CTRL register I/O address must follow
; immediately after these handler pointers in the Sccchan structure.

	push	es
	push	si		; Put channel struct as a parameter
ifdef	LARGEDATA
	call	dword ptr es:[bx+si]	; Call the handler
else
	call	dword ptr [bx+si]	; Call the handler
endif
	pop	si		; Get channel struct back
	pop	es

; Reset highest priority interrupt

; The CTRL register I/O address must follow immediately after the 4
; handler pointers in the Sccchan structure - i.e offset 16 bytes from
; the start of the structure. This is hinted at in SCC.H

ifdef	LARGEDATA
	mov	dx,es:16[si]	; Get control register address
else
	mov	dx,16[si]	; Get control register address
endif
	mov	al,38h		; "Reset Highest IUS" opcode
	out	dx,al		; (write to WR0).
	jmp	short d4	; settling delay
d4:	jmp	short d5
d5:

; Determine if more interrupt requests are coming in from the SCCs

	jmp	sccint		; keep trying until no vector returned

; Clear the ISR bit in the PIC and return from interrupt

clrret: 

; SCC isn't setup for chained vectors, so I've commented out the
; "clear chain vector" code (3/95 G8FSL)

;	mov	ax,0		; clear chain vector value (4/92 KA9Q)
;	mov	dx,0

	jmp	doret		; execute code in pcint.asm

;	sccvec	endproc



;=====================================================================
; sccnovec is the interrupt handler for SCC interrupts using polling
;=====================================================================

	public sccnovec
	label sccnovec far
	
	cli			; this code is not re-entrant, so make sure it
				; is not interrupted. some multi-taskers
				; intercept interrupt handlers, so be careful!


	push	ds		; save on user stack
	mov	ds,cs:dbase

	mov	Sssave,ss	; stash user stack context
	mov	Spsave,sp

	mov	ss,cs:dbase	; set up interrupt stack
	lea	sp,Stktop

	PUSHALL			; save user regs on interrupt stack
	push	es
	call	eoi
ifndef	LARGEDATA
	mov	es,ax		; small data assumes ES == DS
endif

	cld			; in case "movsb" or "movsw" is used


; Find the SCC generating the interrupt by polling all attached SCCs
; reading RR3A (the interrupt pending register)

sccintnv:
	lea	si,Sccpolltab	; Point to polling table

				; Sccpolltab (in SCC.C) is an array of io addresses
				; (= int16) of SCC channel ctrl registers to poll.
				; The last active entry in the array is
				; followed by a 0 entry.
sccpoll:
	mov	dx,[si]		; Get chan A CTRL address
	inc	si
	inc	si
	test	dx,dx		; End of table without finding it
	je	clrret		; Then return from interrupt
	mov	al,3		; Select access to read register 3A
	out	dx,al		; (Interrupt pending for both channels)
	jmp	short d6	; Delay
d6:	jmp	short d7
d7:	jmp	short d8
d8:	in	al,dx
	test	al,al		; Test if nonzero (i.e. there is an int pending)
	jnz	sccip		; Yes, handle it
	inc	si		; No, next A CTRL
	inc	si
	jmp	sccpoll

; Read SCC interrupt vector from RR2B, it should always be correct
; Extract channel number and status from vector. Determine handler address.

; Vector includes status. Top 4 bits are set with SCC channel number in init
; code (SCC.C).


; si now points to the entry in Sccpolltab after the channel A CTRL address.
; this is the channel B CTRL address.

sccip:	mov	dx,[si]		; Read B CTRL address
	mov	al,2		; Select access to read register 2B
	out	dx,al		; (Interrupt vector, including status)
	jmp	short d9	; Delay
d9:	jmp	short d10
d10:	jmp	short d11
d11:	in	al,dx		; Read the vector
				; Vector Includes Status bit was set in WR9,
				; so bits 1, and 2 encode the reason, bit 3 = A or B.
	mov	bl,al		; Copy vector (need it later for status)
	shr	bl,1		; Discard least significant bit.
	and	bx,7ch		; Clear 2 reason bits, leaving high order bits
				; and the A/B channel bit. Also clear BH.
	xor	bl,04h		; Toggle A/B channel bit. Now A=0,B=1.

				; Now use BX as an index into Sccchan array.
				; Sccchan (in SCC.C) is an array of pointers
				; to scc channel structures.
ifdef	LARGEDATA
	les	si,Sccchan[bx]	; Sccchan entries are 32 bit pointers if LARGEDATA.
else
	shr	bl,1		; Discard another bit (Sccchan pointers are 16 bits).
	mov	si,Sccchan[bx]	; Read address of channel structure.
endif
ifdef	LARGEDATA		; Test for NULL
	push	ax		; If LARGEDATA, then both ES and SI will
	mov	ax,es		; both be 0 if the board has been attached
	test	ax,ax		; but the channel hasn't been.
	pop	ax		; If not LARGEDATA, then only SI needs to be
	jne nn1			; tested.
endif
	test	si,si		; Test for NULL
	je	clrret		; No channel struct, so channel not attached, ignore it.

nn1:				; AX still holds the original vector, including status
	and	ax,06h		; Isolate status info from vector.
	add	ax,ax		; Double it - make index into FAR PTR array.
	mov	bx,ax		; It must be in BX, so move it.

; Call the handler (defined in C), with Sccchan struct as a parameter

; Note that ES:SI is the pointer to the Sccchan struct, and that bits 1 and 2
; of the Vector (i.e. the status bits, identifing the cause of the interrupt)
; are about to be added to the pointer.
; This means that the first 4 elements of the Sccchan struct MUST be:
;	Transmit Buffer Empty handler pointer
;	External/Status Conditions handler pointer
;	Receive Character Available handler pointer
;	Special Receive Condition handler pointer
; This is expressed in SCC.H too.
	
	push	es
	push	si		; Put channel struct as a parameter.
ifdef	LARGEDATA
	call	dword ptr es:[bx+si]	; Call the handler
else
	call	dword ptr [bx+si]	; Call the handler
endif
	pop	si		; Remove parameter from stack
	pop	es

; Check for more interrupt pending bits

	jmp	sccintnv

;	sccnovec	endproc

	end
