;	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

; === alternative section STACKSCODE

 assume cs:STACKSCODE

;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.

; === no alternative section

;*******************************************************************
;Macro Interrupt handler for the ordinary interrupt vectors and
;the shared interrupt vectors.
;*****************************
%imacro Stack_Main 1-*
 %rep %0
	ASSUME	DS:NOTHING
	ASSUME	ES:NOTHING
	ASSUME	SS:NOTHING
PUBLIC	Int%1
PUBLIC	Old%1
;-----------------------------
	%ifn	IntSharingFlag		;if not IntSharingFlag
;-----------------------------
	Old%1	DD	0
Int%1	PROC	FAR
;-----------------------------
	%else				;for shared interrupt. A Header exists.

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

;
; Keyboard interrupt must have a three byte jump, a NOP and a zero byte
; as its first instruction for compatibility reasons
	%ifidni	%1, 09
	jmp	short Keyboard_lbl
	nop
	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

	%ifidni	%1, 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
 assume es:nothing
mdl_convert equ MDL_CONVERT	; NASM port equate
	cmp	byte ptr [es:0fffeh],mdl_convert	;check if convertible
	pop	es
 assume es:nothing
	jne	Normal%1

	in	al,62h
	test	al,80h
	jz	Normal%1

Special%1:
	pop	ax
	jmp	far [cs:Old%1]

Normal%1:

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

	%endif

	push	bp
	push	es
STACKS equ Stacks	; NASM port label
	mov	es, [cs:STACKS+2]	; Get segment of stacks
 assume es:STACKSCODE

	mov	bp,[cs:NextEntry]		; get most likely candidate
Allocated equ allocated	; NASM port equate
	mov	al,Allocated
	xchg	[AllocByte],al		; grab the entry
	cmp	al,Free 		; still avail?
	jne	NotFree%1

	sub	word [cs:NextEntry],EntrySize	; set for next interrupt

Found%1:
	mov	[SavedSP],sp		; save sp value
	mov	[SavedSS],ss		; save ss also
;	mov	IntLevel,%1h		; 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%1

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

	pushf				; go execute the real interrupt handler
	call	far [cs:Old%1]		;  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]
 assume ss:nothing

;	cmp	AllocByte,Allocated	; If an error occured,
;	jne	NewError%1		;  do not free us

	mov	byte [AllocByte],Free		; free the entry
	mov	[cs:NextEntry],bp		; setup to use next time

NewError%1:
	pop	es
 assume es:nothing
	pop	bp			; saved on entry
	pop	ax			; saved on entry

INTRET_%1:								  ;3.30
	iret				; done with this interrupt
Intret_%1 equ INTRET_%1

NotFree%1:
	cmp	al,Allocated		; error flag
	je	FindNext%1		;  no, continue
	xchg	[AllocByte],al		;  yes, restore error value

FindNext%1:
LongPath equ longpath	; NASM port label
	call	LongPath
	jmp	Found%1

FoundBad%1:
	cmp	bp,[cs:FirstEntry]
	jc	FindNext%1
	mov	bp,ax			; flag this entry
Clobbered equ clobbered	; NASM port equate
	mov	byte [AllocByte],Clobbered
;	add	bp,EntrySize		;  and previous entry
;	mov	AllocByte,Overflowed
;	sub	bp,EntrySize
	jmp	FindNext%1		; keep looking

int%1	endp
  %rotate 1
 %endrep
	%endmacro

;*****************************						  ;3.30
;End of Macro definition						  ;3.30
;********************************************************************	  ;3.30
; THESE ARE THE INDIVIDUAL INTERRUPT HANDLERS				  ;3.30

%iassign IntSharingFlag 0
	Stack_Main 02,08,09,70

%iassign IntSharingFlag 1
	Stack_Main 0A,0B,0C,0D,0E,72,73,74,76,77

;********************************************************************	  ;3.30
;Common routines							  ;3.30

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

LPLOOPP:								  ;3.30
	cmp	byte [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,[cs:FirstEntry]
stack_Fatal equ stack_fatal	; NASM port label
	je	stack_Fatal
	sub	bp,EntrySize
	JMP	LPLOOPP 						  ;3.30

.found:
	ret

;	page

stack_fatal	proc	near
	push	ds							  ;3.30
	mov	ax, 0f000h		;loook at the model byte	  ;3.30
	mov	ds, ax
 assume ds:nothing
	cmp	byte ptr [0fffeh], mdl_convert	;convertible?	  ;3.30
	pop	ds							  ;3.30
 assume ds:nothing
	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
 assume ds:STACKSCODE
fatal_msg equ FATAL_MSG	; NASM port label
	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
stack_fatal	endp

 assume cs:SYSINITGROUP
