;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   STARTEND.ASM
;
;   Copyright (c) Microsoft Corporation 1989, 1990. All rights reserved.
;
;   This module contains the routines which initialize, and clean
;   up the driver after Libentry/WEP/Enable/Diable called by windows.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

?PLM=1  ; pascal call convention
?WIN=0  ; Windows prolog/epilog code
?DF=1

	PMODE=1
	.xlist
	include  cmacros.inc
        include  int31.inc
	include  windows.inc
        include  mmddk.inc
	include  mmsystem.inc
	include  timer.inc
        .list

        externA     __WinFlags

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   Local data segment
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


sBegin DATA

	; ISR support

	public	lpOLDISR
	lpOldISR    dd	?

ifdef RMODE_INT
        public  RModeOldISR
        RModeOldISR   dd  0

        public  RModeCodeSegment
        RModeCodeSegment dw ?

endif   ;RMODE_INT

	externW     Events
	externW     wNextTime

sEnd DATA

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   Code segment
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        externFP    tddISR                      ; in local.asm
        externFP    tddSetInterruptPeriodFar    ; in timer.asm

ifdef   RMODE_INT
        externW     RmodeDataSegment            ; in local.asm
        externFP    tddRModeISR                 ; in local.asm
endif
        externFP    GetSelectorBase             ; kernel
        externFP    AllocCStoDSAlias            ; kernel
        externFP    FreeSelector                ; kernel

sBegin  CodeInit
        assumes cs,CodeInit
        assumes ds,Data
        assumes es,nothing

;----------------------------Private-Routine----------------------------;
; SegmentFromSelector
;
;   Converts a selector to a segment...note that this routine assumes
;   the memory pointed to by the selector is below the 1Meg line!
;
; Params:
;   AX = selector to convert to segment
;
; Returns:
;   AX = segment of selector given
;
; Error Returns:
;   None
;
; Registers Destroyed:
;   none
;
;-----------------------------------------------------------------------;

assumes ds,Data
assumes es,nothing

SegmentFromSelector proc near

    cCall   GetSelectorBase,<ax>        ;DX:AX = base of selector
rept 4
    shr     dx,1
    rcr     ax,1
endm
    ;AX now points to *segment* (iff selector is based below 1Mb)

    ret

SegmentFromSelector endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   @doc INTERNAL
;
;   @api WORD | Enable | This function enables the driver.  It
;       will hook interrupts and validate the hardware.
;
;   @rdesc Returns 1 if successfull, and 0 otherwise.
;
;   @comm This function is automatically invoked when the library is
;       first loaded. It is included so that win386 could call it
;       when it switches VMs.
;
;   @xref Disable
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

cProc Enable286 <FAR, PUBLIC> <si, di>

cBegin
	; make sure clock interrupts are disabled until after
	; service routine has been initialized!!
	AssertSLI
	cli

	; get the currently owned timer interrupt vector

	; get interrupt vector, and specify timer interrupt number
;       mov	ax,03500H + TIMERINTERRUPT
;       push	es
;       int	21h 			; get the current vector in ES:BX
;       mov	lpOldISR.Sel,es
;       mov	lpOldISR.Off,bx		; save the old vector
;       pop	es
;
;       ; set vector to our isr
;
;       ; set interrupt vector function, and specify the timer interrupt number
;       mov	ax,02500h + TIMERINTERRUPT
;       push	ds
;       mov     dx,seg tddISR
;       mov	ds,dx
;       assumes	ds,nothing
;       mov     dx,offset tddISR
;       int	21h			; set the new vector
;       pop	ds
;       assumes	ds,DATA
;
;       mov	ax,[wNextTime]
;       not	ax
;       mov	[wNextTime],ax		; force set of period
;       call    tddSetInterruptPeriodFar

ifdef	RMODE_INT
	;
	; if running under DOSX set the RMODE interrupt too
	;
	mov	ax,__WinFlags
	test	ax,WF_PMODE
	jz	enable_no_dosx

	mov	ax,seg tddRModeISR
	call	SegmentFromSelector

	or	dx,dx		; ACK! above 1Mb
	jnz	enable_no_dosx

	mov	[RModeCodeSegment],ax	; save the segment of the code segment

	mov	ax,ds			; get SEGMENT of our data segment
	call	SegmentFromSelector
	push	ax			; save on stack

	mov	ax,seg tddRModeISR	; write data SEGMENT into _INTERRUPT
	cCall	AllocCStoDSAlias,<ax>	; code segment -- requires a data alias
	mov	es,ax
	pop	ax
	mov	es:[RModeDataSegment],ax
	cCall	FreeSelector,<es>	; don't need CS alias any longer

	mov	ax,Get_RM_IntVector	; get the real mode IRQ0 vector
	mov	bl,DOSX_IRQ + TIMERINTERRUPT
	int	31h			; DOSX get real mode vector in CX:DX

	mov	RModeOldISR.lo,dx	; save old ISR
	mov	RModeOldISR.hi,cx

	mov	cx,RModeCodeSegment	; CX:DX --> real mode ISR
	mov	dx,offset tddRModeISR

	mov	ax,Set_RM_IntVector	; DOSX Set Vector Function
	int	31h			; Set the DOS vector real mode

enable_no_dosx:
endif
	sti

	mov	ax,1
cEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   @doc INTERNAL
;
;   @api WORD | Disable | This function disables the driver.
;	It disables the hardware, unhooks interrupts and removes
;	all time events from the queue.
;
;   @rdesc Returns 1 if successfull, and 0 otherwise.
;
;   @comm This function is called automatically when Windows unloads
;       the library and invokes the WEP() function.  It is included
;       here so that WIN386 can use it when switching VMs.
;
;   @xref Enable
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


cProc Disable286 <FAR, PUBLIC> <si, di>

    ; note that all this is in the reverse order to Enable

cBegin
    AssertSLI
    cli
    ; set timer back to 55ms BIOS service
    xor     cx,cx		; 65536 ticks per period

    mov     al,TMR_MODE3_RW	; Read/Write counter 0 mode 3 (two bytes)
    out     TMR_CTRL_REG,al

    mov     al,cl
    out     TMR_CNTR_0,al	; write low byte

    mov     al,ch
    out     TMR_CNTR_0,al       ; write high byte

ifdef RMODE_INT
    ;
    ; check for a REAL mode int handler and un-hook it.
    ;
    mov     dx,RModeOldISR.lo
    mov     cx,RModeOldISR.hi
    jcxz    disable_no_dosx

    mov     bl,DOSX_IRQ + TIMERINTERRUPT
    mov     ax,Set_RM_IntVector     ;DOSX Set Vector Function
    int     31h                     ;Set the DOS vector real mode

disable_no_dosx:
endif

    ; restore the old interrupt vector

    mov     ax,02500h + TIMERINTERRUPT
    ; set interrupt vector function, and specify the timer interrupt number

    push    ds
    lds     dx,lpOldISR
    assumes ds,nothing
    int     21h 		; reset the old vector
    pop ds
    assumes ds,DATA

    sti
    mov     ax,1
cEnd

sEnd

end
