;	MSStack.inc
;
;	Interrupt level 2, 3, 4, 5, 6, 7,(10, 11, 12, 14, 15 - AT level)
;	should follow the standard Interrupt Sharing Scheme which has
;	a standard header structure.
;	Fyi, the following shows the relations between
;	the interrupt vector and interrupt level.
; VEC(Hex)    2  8  9  A  B  C	D  E  70  72  73  74  76  77
; LVL(Deci)   9  0  1  2  3  4	5  6   8  10  11  12  14  15
;	MSSTACK module modifies the following interrupt vectors
;	to meet the standard Interrupt Sharing standard;
;	  A, B, C, D, E, 72, 73, 74, 76, 77.
;	Also, for interrupt level 7 and 15, the FirstFlag in a standard header
;	should be initialized to indicat whether this interrupt handler is
;	the first (= 80h) or not.  The FirstFlag entry of INT77h's
;	program header is initialized in STKINIT.INC module.
;	FirstFlag is only meaningful for interrupt level 7 and 15.
;

;  User specifies the number of stack elements - default = 9
;						 minimum = 8
;						 maximum = 64
;
;  Intercepts Asynchronous Hardware Interrupts only
;
;  Picks a stack from pool of stacks and switches to it
;
;  Calls the previously saved interrupt vector after pushing flags
;
;  On return, returns the stack to the stack pool
;


; This is a modification of STACKS:
; 1. To fix a bug which was causing the program to take up too much space.
; 2. To dispense stack space from hi-mem first rather than low-mem first.
;    . Clobbers the stack that got too big instead of innocent stack
;    . Allows system to work if the only stack that got too big was the most
;      deeply nested one
; 3. Disables NMI interrupts while setting the NMI vector.
; 4. Does not intercept any interupts on a PCjr.
; 5. Double checks that a nested interrupt didn't get the same stack.
; 6. Intercepts Ints 70, 72-77 for PC-ATs and other future products

;The following variables are for MSSTACK.inc
		EVEN
		dw	0	; SPARE FIELD BUT LEAVE THESE IN ORDER
StackCount	dw	0
StackAt 	dw	0
StackSize	dw	0
Stacks		dw	0
		dw	0

FirstEntry	dw	Stacks
LastEntry	dw	Stacks+(DefaultCount*EntrySize)-EntrySize
NextEntry	dw	Stacks+(DefaultCount*EntrySize)-EntrySize

;End of variables defined for MSSTACK.

;*******************************************************************
;Macro Interrupt handler for the ordinary interrupt vectors and
;the shared interrupt vectors.
;*****************************
Stack_Main	MACRO	AA
	ASSUME	DS:NOTHING
	ASSUME	ES:NOTHING
	ASSUME	SS:NOTHING
PUBLIC	Int&AA
PUBLIC	Old&AA
;-----------------------------
	ife	IntSharingFlag		;if not IntSharingFlag
;-----------------------------
	Old&AA	DD	0
Int&AA	PROC	FAR
;-----------------------------
    else				;for shared interrupt. A Header exists.

PUBLIC	FirstFlag&AA
Int&AA	PROC	FAR
	jmp	short	  Entry_Int&AA&_Stk
	Old&AA	dd	  0		;Forward pointer
		dw	  424Bh 	;compatible signature for Int. Sharing
	FirstFlag&AA db   0		;the firstly hooked.
	jmp	short	Intret_&AA	;Reset routine. We don't care this.
		db	  7 dup (0)	;Reserved for future.
Entry_Int&AA&_Stk:
;-----------------------------
	endif
;-----------------------------

;
; Keyboard interrupt must have a three byte jump, a NOP and a zero byte
; as its first instruction for compatibility reasons
	ifidn	<&aa>,<09>
	jmp	Keyboard_lbl
	nop
	db	0
Keyboard_lbl	label	near
	endif

; This patches INTERRUPT 75h to be "unhooked".  We do this Wierdness,
; rather than never hooking INT 75h, to maintain maximum compat. with IBMs
; post production patch.
	push	ax

	ifidn	<&aa>,<02>

; *********************************************************************
;
; This is special support for the PC Convertible / NMI handler
;
;	On the PC Convertible, there is a situation where an NMI can be 
;	caused by using the "OUT" instructions to certain ports.  When this
;	occurs, the PC Convertible hardware *GUARANTEES* that **NOTHING** 
;	can stop the NMI or interfere with getting to the NMI handler.  This
;	includes other type of interrupts (hardware and software), and
;	also includes other type of NMI's.  When any NMI has occured,
;	no other interrtupt (hardware, software or NMI) can occur until
;	the software takes specific steps to allow further interrupting.
;
;	For PC Convertible, the situation where the NMI is generated by the
;	"OUT" to a control port requires "fixing-up" and re-attempting.  In
;	otherwords, it is actually a "restartable exception".  In this
;	case, the software handler must be able to get to the stack in
;	order to figure out what instruction caused the problem, where
;	it was "OUT"ing to and what value it was "OUT"ing.  Therefore,
;	we will not switch stacks in this situation.  This situation is
;	detected by interrogating port 62h, and checking for a bit value
;	of 80h.  If set, *****DO NOT SWITCH STACKS*****.
;
; *********************************************************************

	push	es
	mov	ax,0f000h
	mov	es,ax
	cmp	byte ptr es:[0fffeh],mdl_convert	;check if convertible
	pop	es
	jne	Normal&aa

	in	al,62h
	test	al,80h
	jz	Normal&aa

Special&aa:
	pop	ax
	jmp	dword ptr Old&aa

Normal&aa:

; *********************************************************************

	endif

	push	bp
	push	es
	mov	es, cs:[STACKS+2]	; Get segment of stacks

	mov	bp,NextEntry		; get most likely candidate
	mov	al,Allocated
	xchg	AllocByte,al		; grab the entry
	cmp	al,Free 		; still avail?
	jne	NotFree&aa

	sub	NextEntry,EntrySize	; set for next interrupt

Found&aa:
	mov	SavedSP,sp		; save sp value
	mov	SavedSS,ss		; save ss also
;	mov	IntLevel,aa&h		; save the int level

	mov	ax,bp			; temp save of table offset

	mov	bp,NewSP		; get new SP value
	cmp	es:[bp],ax		; check for offset into table
	jne	FoundBad&aa

	mov	ax,es			; point ss,sp to the new stack
	mov	ss,ax
	mov	sp,bp

	pushf				; go execute the real interrupt handler
	call	dword ptr old&aa	;  which will iret back to here

	mov	bp,sp			; retrieve the table offset for us
	mov	bp,es:[bp]		   ;  but leave it on the stack
	mov	ss,SavedSS	     ; get old stack back
	mov	sp,SavedSP

;	cmp	AllocByte,Allocated	; If an error occured,
;	jne	NewError&aa		;  do not free us

	mov	AllocByte,Free		; free the entry
	mov	NextEntry,bp		; setup to use next time

NewError&aa:
	pop	es
	pop	bp			; saved on entry
	pop	ax			; saved on entry

INTRET_&AA:								  ;3.30
	iret				; done with this interrupt

NotFree&aa:
	cmp	al,Allocated		; error flag
	je	findnext&aa		;  no, continue
	xchg	AllocByte,al		;  yes, restore error value

FindNext&aa:
	call	LongPath
	jmp	Found&aa

FoundBad&aa:
	cmp	bp,FirstEntry
	jc	findnext&aa
	mov	bp,ax			; flag this entry
	mov	AllocByte,Clobbered
;	add	bp,EntrySize		;  and previous entry
;	mov	AllocByte,Overflowed
;	sub	bp,EntrySize
	jmp	findnext&aa		; keep looking

int&aa	endp


	endm

;*****************************						  ;3.30
;End of Macro definition						  ;3.30
;********************************************************************	  ;3.30
; THESE ARE THE INDIVIDUAL INTERRUPT HANDLERS				  ;3.30
									  ;3.30
	IRP	A,<02,08,09,70> 					  ;3.30
	IntSharingFlag=0						  ;3.30
	Stack_Main &A							  ;3.30
	ENDM								  ;3.30
									  ;3.30
	IRP	A,<0A,0B,0C,0D,0E,72,73,74,76,77>			  ;3.30
	IntSharingFlag=1						  ;3.30
	Stack_Main &A							  ;3.30
	ENDM								  ;3.30
									  ;3.30
;********************************************************************	  ;3.30
;Common routines							  ;3.30

longpath:
	mov	bp,LastEntry		; start with last entry in table

LPLOOPP:								  ;3.30
	cmp	AllocByte,Free		; is entry free?
	jne	inuse			;  no, try next one

	mov	al,Allocated
	xchg	AllocByte,al		; allocate entry
	cmp	al,Free 		; is it still free?
	je	found			;  yes, go use it

	cmp	al,Allocated		; is it other than Allocated or Free?
	je	inuse			;  no, check the next one

	mov	AllocByte,al		;  yes, put back the error state

inuse:
	cmp	bp,FirstEntry
	je	Fatal
	sub	bp,EntrySize
	JMP	LPLOOPP 						  ;3.30

found:
	ret

	page

fatal	proc	near
	push	ds							  ;3.30
	mov	ax, 0f000h		;loook at the model byte	  ;3.30
	mov	ds, ax							  ;3.30
	cmp	ds:byte ptr [0fffeh], mdl_convert	;convertible?	  ;3.30
	pop	ds							  ;3.30
	jne	Skip_NMIS						  ;3.30
									  ;3.30
	mov	al,07h				; disable PC Convertible NMIs
	out	72h,al

Skip_NMIS:								  ;3.30
	cli					; disable and mask
	mov	al,0ffh 			;   all other ints
	out	021h,al
	out	0a1h,al

	mov	si,cs
	mov	ds,si
	mov	si,offset fatal_msg

fatal_loop:
	lodsb
	cmp	al,'$'
	je	fatal_done

	mov	bl,7						      ;3.30*
	mov	ah,14						      ;3.30*
	int	010h			; whoops, this enables ints   ;3.30*
	jmp	fatal_loop

fatal_done:
	jmp	fatal_done
fatal	endp
