
;   QINT.ASM
;
;   handle= OpenQInts()
;	    CloseQInts(handle:A0)
;	    SetQPri(handle:D0, pri:D1)
;	    SetQVector(handle:D0, vector:D1, signo:D2, arg:D3, pri:A0)

		public	_lOpenQInts
		public	_lCloseQInts
		public	_lSetQPri
		public	_lSetQVector

		public	_LVOFindTask
		public	_LVOAllocMem
		public	_LVOFreeMem
		public	_LVOForbid
		public	_LVOPermit
		public	_LVODisable
		public	_LVOEnable
		public	_LVOEnqueue
		public	_LVORemove
		public	_LVOSetSignal
		public	_LVOSetExcept

QINT_SIZE	EQU	32

QINT_NODE	EQU	0
QINT_VECTOR	EQU	14
QINT_SIGMASK	EQU	18
QINT_ARG	EQU	22
QINT_UNUSED	EQU	26

HAN_SIZE	EQU	36+32*QINT_SIZE
HAN_A4		EQU	0
HAN_A5		EQU	4
HAN_EXCODE	EQU	8
HAN_EXDATA	EQU	12
HAN_INTS	EQU	16
HAN_INHAN	EQU	18
HAN_PRI 	EQU	20
HAN_LIST	EQU	22
HAN_QINT	EQU	36

MEMF_CLEAR	EQU	$10000
MEMF_PUBLIC	EQU	$1

LH_HEAD 	EQU	0
LH_TAIL 	EQU	4
LH_TAILPRED	EQU	8

LN_TYPE 	EQU	8
LN_PRI		EQU	9

TC_EXCEPTCODE	EQU	42
TC_EXCEPTDATA	EQU	38


_lOpenQInts:
	    movem.l D2/D3/A2/A6,-(sp)
	    move.l  4,A6
	    move.l  #HAN_SIZE,D0
	    move.l  #MEMF_CLEAR|MEMF_PUBLIC,D1
	    jsr     _LVOAllocMem(A6)
	    tst.l   D0
	    beq     .oqi1
	    move.l  D0,A2
	    move.b  #-128,HAN_PRI(A2)
	    movem.l A4/A5,HAN_A4(A2)
	    lea.l   HAN_LIST+LH_HEAD(A2),A0
	    lea.l   HAN_LIST+LH_TAIL(A2),A1
	    move.l  A1,(A0)
	    move.l  A0,LH_TAILPRED(A0)
.oqi1	    movem.l (sp)+,D2/D3/A2/A6
	    rts

_lCloseQInts:			     ; A0 = handle
	    movem.l D2/D3/A2/A6,-(sp)
	    move.l  4,A6
	    move.l  A0,A2	    ; A2 = handle
	    move.l  A0,D0	    ; skip if null
	    beq     .cqi1
	    moveq.l #31,D3	    ; D3 = sig number.
.cqi2	    move.w  D3,-(sp)
	    move.l  D3,D2	    ; SetQVector(han, NULL, sig#, 0, 0)
	    move.l  A2,D0	    ;		 D0   D1    D2	 D3  A0
	    moveq.l #0,D1
	    move.l  D1,D3
	    move.l  D1,A0
	    jsr     _lSetQVector
	    move.w  (sp)+,D3
	    dbf     D3,.cqi2

.cqi1	    movem.l (sp)+,D2/D3/A2/A6
	    rts



	    ;	_EXCEPT ,  Exception handler.
	    ;
	    ;entry:
	    ;	D0 = exceptions that occured
	    ;	A1 = Handle
	    ;
	    ;auto:
	    ;	A2	= Handle
	    ;	A4/A5	= Possible Global Base Registers
	    ;	D4	= Bit# count
	    ;	D6	= Exceptions that occured


_except:
	    move.l	4,A6		;A6 = Exec Base
	    move.l	D0,D6		;D6 = Exception Bit Mask
	    movem.l	HAN_A4(A1),A4/A5;get global base register(s).
	    move.l	A1,A2		;A2 = HANDLE BASE

	    ;	Queue any exceptions which are interrupts.  Exceptions
	    ;	for interrupts which are queued are NOT reenabled until
	    ;	they are actually run.
	    ;
	    ;	Note that in the loop we must loop to .ex2 to decrement
	    ;	D4, which doesn't occur when we find a '1'.  The Z bit
	    ;	must be set when we loop to .ex2

.ex0	    moveq.l	#31,D4		;D4 = BIT NUMBER
.ex1	    btst.l	D4,D6		; test bits
.ex2	    dbne	D4,.ex1 	; until found a '1'
	    beq 	.ex10		;or loop exhausted (D4 == -1)

	    move.l	D4,D5		;Calculate address of QINT.
	    asl.l	#5,D5		;D5 = index * sizeof(QINT)
	    pea.l	HAN_QINT(A2)
	    add.l	(sp)+,D5        ;     + Base of QINT array
	    move.l	D5,A3		;A3 = QINT address
	    tst.l	QINT_VECTOR(A3) ;Is this exception vectored?
	    beq 	.ex2		;no, somebody else owns it

.ex3	    bclr.l	D4,D6		;clear exception bit.
	    jsr 	_LVOForbid(A6)  ;Important operation!
	    move.l	A3,A1		;A1 = node
	    lea.l	HAN_LIST(A2),A0 ;A0 = List Base
	    move.b	#5,LN_TYPE(A1)  ;mark node as being queued
	    jsr 	_LVOEnqueue(A6)
	    jsr 	_LVOPermit(A6)  ;enable exceptions
	    clr.w	D7		;Force Z cc set.
	    bra 	.ex2		;loop (force decrement/loop)

	    ;	Call the handler.  The handler is allowed to trash
	    ;	everything except D6/A7.  Called with A2 = Handle,
	    ;	A6 = Exec Base

.ex10	    tst.w	HAN_INHAN(A2)   ;no need to call handler?
	    bne 	.ex11
	    bsr 	_handler	;call handler
.ex11	    move.l	D6,D0		;D0 = exception mask
	    beq 	.ex12		;we processed all exceptions
	    move.l	HAN_EXCODE(A2),A0 ;somebody else owns some exceptions
	    move.l	HAN_EXDATA(A2),A1 ;restore exception data pointer
	    jmp 	(A0)            ;call him with remaining exceptions.
.ex12	    rts

	    ;entry:
	    ;	A2	= Handle
	    ;	A4/A5	= Possible Global Base Registers
	    ;	A6	= EXEC base
	    ;
	    ;auto:
	    ;	A2	= Handle;
	    ;	A3	= Qint
	    ;	A4/A5	= GBRs
	    ;	A6	= EXEC base
	    ;	D4	= savepri

_handler:
	    move.b	#1,HAN_INHAN(A2)
	    jsr 	_LVOForbid(A6)
.hloop	    move.l	HAN_LIST+LH_HEAD(A2),A3
	    lea.l	HAN_LIST+LH_TAIL(A2),A0
	    cmp.l	A0,A3			;List is empty
	    beq 	.hbreak
	    move.b	HAN_PRI(A2),D4          ;Check priority of node
	    cmp.b	QINT_NODE+LN_PRI(A3),D4
	    bgt 	.hbreak 		;savepri > qintpri
	    move.l	A3,A1			;A1 = node to remove
	    jsr 	_LVORemove(A6)
	    move.b	#0,QINT_NODE+LN_TYPE(A3)
	    move.b	QINT_NODE+LN_PRI(A3),HAN_PRI(A2)
	    jsr 	_LVOPermit(A6)
	    move.l	QINT_ARG(A3),-(sp)
	    move.l	QINT_VECTOR(A3),A0
	    jsr 	(A0)
	    move.l	4,A6
	    addq.l	#4,sp
	    move.b	D4,HAN_PRI(A2)
	    tst.l	QINT_VECTOR(A3)         ;Reenable the exception
	    beq 	.h1			;only if the vector still
	    move.l	QINT_SIGMASK(A3),D0     ;exists.
	    move.l	D0,D1
	    jsr 	_LVOSetExcept(A6)
.h1	    jsr 	_LVOForbid(A6)
	    bra 	.hloop

.hbreak     moveq.l	#0,D2
	    move.b	D2,HAN_INHAN(A2)
	    jsr 	_LVOPermit(A6)
	    move.l	D2,D0
	    move.l	D2,D1
	    jsr 	_LVOSetExcept(A6)
	    rts

	    ;	SetQVector()
	    ;
	    ;	D0 = handle (copy to A2)
	    ;	D1 = vector (copy to A3)
	    ;	D2 = signo
	    ;	D3 = arg    (copy to D5)
	    ;	A0 = pri
	    ;
	    ;	A2 = handle
	    ;	A3 = vector
	    ;	A4 = qint
	    ;	A5 = task
	    ;
	    ;	D2 = pri
	    ;	D3 = sigmask
	    ;	D4 = oldvector
	    ;	D5 = arg
	    ;	D6 =
	    ;	D7 =

_lSetQVector:
	    movem.l D2-D7/A2-A6,-(sp)
	    move.l  4,A6
	    move.l  D0,A2		;   A2 = Handle
	    move.l  D1,A3		;   A3 = Vector
	    move.l  D3,D5		;   D5 =

	    moveq.l #0,D3		;   D3 = signal mask
	    bset.l  D2,D3		;   D2 = signal
	    asl.l   #5,D2		;   A4 = qint (han->QInt + signo)
	    add.l   #HAN_QINT,D2
	    add.l   A2,D2
	    move.l  D2,A4		;   A4 = QINT
	    move.l  A0,D2		;   D2 = priority
	    move.w  #0,A1		;   A5 = task
	    jsr     _LVOFindTask(A6)
	    move.l  D0,A5

	    jsr     _LVOForbid(A6)      ;   Forbid
	    move.l  QINT_VECTOR(A4),D4  ;   D4 = oldvector
	    move.l  A3,D0
	    bne     .sq100

	    ;	If new vector is NULL, remove old vector if it exists.

	    tst.l   D4
	    beq     .sqdone
	    moveq.l #0,D0
	    move.l  D3,D1
	    jsr     _LVOSetExcept(A6)
	    cmp.b   #5,QINT_NODE+LN_TYPE(A4)
	    bne     .sq1
	    move.l  A4,A1
	    jsr     _LVORemove(A6)
	    move.b  #0,QINT_NODE+LN_TYPE(A4)
.sq1	    sub.w   #1,HAN_INTS(A2)
	    bne     .sq2
	    jsr     _LVODisable(A6)
	    move.l  HAN_EXCODE(A2),TC_EXCEPTCODE(A5)
	    move.l  HAN_EXDATA(A2),TC_EXCEPTDATA(A5)
	    jsr     _LVOEnable(A6)
.sq2	    bra     .sqdone

	    ;	If new vector not null, replace (possibly NULL) old vector

.sq100	    tst.l   D4
	    bne     .sq150
	    add.w   #1,HAN_INTS(A2)     ; oldvector is null, setup task
	    cmp.w   #1,HAN_INTS(A2)
	    bne     .sq150
	    jsr     _LVODisable(A6)
	    move.l  TC_EXCEPTCODE(A5),HAN_EXCODE(A2)
	    move.l  TC_EXCEPTDATA(A5),HAN_EXDATA(A2)
	    move.l  #_except,TC_EXCEPTCODE(A5)
	    move.l  A2,TC_EXCEPTDATA(A5)
	    jsr     _LVOEnable(A6)

.sq150	    move.l  D5,QINT_ARG(A4)
	    move.b  D2,QINT_NODE+LN_PRI(A4)
	    move.l  D3,QINT_SIGMASK(A4)
	    move.l  D3,D0
	    move.l  D3,D1
	    jsr     _LVOSetExcept(A6)
	    bra     .sqdone

.sqdone     move.l  A3,QINT_VECTOR(A4)
	    jsr     _LVOPermit(A6)
	    moveq.l #0,D0
	    move.l  D0,D1
	    jsr     _LVOSetExcept(A6)
	    move.l  D4,D0
	    movem.l (sp)+,D2-D7/A2-A6
	    rts

	    ;	SetQPri
	    ;
	    ;	D0 = handle
	    ;	D1 = newpri
	    ;
	    ;	A2 = handle
	    ;	D2 = newpri
	    ;	D3 = oldpri
_lSetQPri:
	    movem.l D2/D3/A2/A6,-(sp)
	    move.l  4,A6
	    move.l  D0,A2
	    move.l  D1,D2
	    jsr     _LVODisable(A6)
	    move.b  HAN_PRI(A2),D3
	    move.b  D2,HAN_PRI(A2)
	    jsr     _LVOEnable(A6)
	    cmp.b   D2,D3
	    ble     .sqpdone		    ; D3 <= D2 (old <= new)
	    lea.l   HAN_LIST+LH_TAIL(A2),A0
	    cmp.l   HAN_LIST+LH_HEAD(A2),A0
	    beq     .sqpdone

	    movem.l D4/A3,-(sp)
	    jsr     _handler		    ;A2 = handle, A6 = execbase
	    movem.l (sp)+,D4/A3
.sqpdone:
	    moveq.l #0,D0
	    move.b  D3,D0
	    movem.l (sp)+,D2/D3/A2/A6
	    rts

