.386p
INCL_DOSMISC    EQU     1
INCL_DOSERRORS  EQU     1
INCL_TYPES      EQU     1
INCL_DEF        EQU     1

EXTRN   IDC_STOP_S:FAR               ; Device Specific Stop   Function
EXTRN   IDC_START_S:FAR              ; Device Specific Start  Function
EXTRN   IDC_PAUSE_S:FAR              ; Device Specific Pause  Function
EXTRN   IDC_RESUME_S:FAR             ; Device Specific Resume Function
EXTRN   IDC_GETIMAGE_S:FAR           ; Device Specific Get Image to Streaming Buffer
EXTRN   FIND_OI:FAR                  ; Find Open Instance by File Number
;*********************************************
;**** This is a generic Stream handler Template for the Capture PDD
;**** This routine provides the IDC interface to a Capture PDD.
;**** This IDC entry point is envoked by the Stream Handler to
;**** preform such actions as:
;****     Starting the Stream reading data
;****     Pausing the Stream
;****     Resuming the Stream
;****     Enableing time based Events
;****     Disableing time based Events
;****     Stoping the Stream
;**** This Routine also provides code need to notify the Stream Handler of:
;****     Stream Buffer Filled with data
;****     Overrun of Stream Buffer
;****     Underrun of data
;****     Time Based Event occured
;**** This module was designed as a generic stream templete for the
;**** Capture card device driver, it contains calls to several
;**** Device specific rotuines such as:
;****     Turn on Interrupts when stream is started (for time based capture)
;****     Read an Image from the capture card into the stream buffer
;****     Turn off Interrupts when stream is paused
;****     Turn Interrupt back on when stream is resumed
;****     Turn off Interrupts when stream is Stopped
;*********************************************
; Define the C calling macros
LBegin  macro len
        push    ebp
        mov     ebp,esp
        sub     esp,len
        endm
LEnd    macro
        mov     esp,ebp
        pop     ebp
        endm
;*********************************************

MAIN            EQU     1             ; used in ssmdd.inc
.xlist
        include devsym.inc
        include basemaca.inc
        include basedef.inc
        include devhlp.inc
;       include bse.inc
        include infoseg.inc

;        include ptrace.inc              ; PTRACE macros
        include os2medef.inc
        include meerror.inc
        include ssm.inc
        include shdd.inc
        include vididc.inc
        include video.inc
        include vidin.inc
.list



DATA   SEGMENT  DWORD USE16 PUBLIC 'DATA'      ; changed from code to data
;        ASSUME   CS: FLAT, DS: FLAT, SS: FLAT, ES: FLAT

ALIGN 4
        EXTRN   FlatSel:WORD
        EXTRN   selector1:WORD        ; Segment address for Cards RAM
        EXTRN   device_hlp:DWORD
        EXTRN   Ginfo_Seg:WORD        ; Global Info Segment
        EXTRN   Ginfo_offset:WORD     ; Global Info Offset
        EXTRN   usr_X_Left:DWORD      ; Offset in X from the left of the image
        EXTRN   usr_Y_Top:DWORD       ; Offset in Y from the top of the image
        EXTRN   usr_Y_Height:DWORD    ; Height of image
        EXTRN   usr_X_Width:DWORD     ; Width  of image
        EXTRN   usr_DX_Left:DWORD     ; Offset in X from the left of the image
        EXTRN   usr_DY_Top:DWORD      ; Offset in Y from the top of the image
        EXTRN   usr_DY_Height:DWORD   ; Height of image
        EXTRN   usr_DX_Width:DWORD    ; Width  of image
        EXTRN   usr_ScaleFactor:DWORD ; Scale image down by this factor
        EXTRN   usr_FPS:WORD          ; Frames Per Second to capture

PUBLIC  MIN_NUM_BUFFS
PUBLIC  IDC_S_TRACE
PUBLIC  SI_LIST, SI_CURR

TIC_TOC         dd   0       ; Number of interrupt we have received
TIC_NEXT        dd   0       ; Time to Read Next Image
SI_CURR         dd   0       ; Current Stream Instance Structure
SI_LIST         dd   0       ; List of all Stream Instance Structures
Interrupt_on    dd   0       ; 0 = interrupts are turned OFF ignore Clock Ticks
Sys_clock       dd   0       ; Time from System/DOS clock
Skipped         dd   0       ; Number of frames skipped on this interrupt
Overrun         dd   0       ; overruns (no buffer when intr) occured 1=true 0=false
round           dd   0       ; Number of times tic_next rounded up on this interrupt

;............................. Buffer for tace Data ....................................

eye_see_you    db      'JOVIAN DEVICE DRIVER TRACE DATA '
Trace_ptr      dd      0
IDC_S_trace    equ     $
trace_start    equ     $
               dw     312 dup(0)
Trace_end      equ     $
               dw     16 dup(0)
               db     '!TRACE DATA END!'

F_EYE          dd     'SIMG'

;............................. Buffer for tace Data ....................................

IDC     equ   es:[bx]                 ; Pointer to IDC Request packet
public intr_active, Block_Wait, Run_Wait
INTR_ACTIVE   dd   0         ; 1= Interrupt handler is active   0 = Not Active
BLOCK_WAIT    dd   0         ; Number of Times Block Occured
RUN_WAIT      dd   0         ; Number of Times Run   Occured
Min_NUM_BUFFS dd   2         ; Minimum number of Read Buffers needed from the SH
Status_time   dd   0         ; Time reported on Last DDCMD_STATUS
DATA   ENDS


_TEXT   SEGMENT  DWORD USE16 PUBLIC 'CODE'
        ASSUME   CS: FLAT, DS: FLAT, SS: FLAT, ES: FLAT



subttl IDC Entry Strategy Routine
page


Procedure IDC_ENTRY,FAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

;************************I D C   E N T R Y    P O I N T ************************************
;*
;* FUNCTION:    This Decodes the IDC call from VDSH (video Stream Handler) and
;*              jumps to the appropriate handler
;*
;* INPUT:       bp+6  -> Request packet.
;*
;*
;*
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;* NOTES:       All handlers should load AX with the Return Code
;*
;******************************************************************************************

        IDC_Parm     equ dword ptr [bp+8]  ; Sel:off pair
        IDC_Parm_off equ word ptr [bp+8]   ; Parameter offset
        IDC_Parm_sel equ word ptr [bp+10]  ; Parameter selector


        push    ebp                     ; Save the base stack pointer
        mov     ebp, esp                ; Local Base pointer for this segment

        push    ds                      ; Save Regs
        push    es
        push    fs
        push    ebx
        push    ecx
        push    esi
        push    edi

        mov     ax, DATA                ; DS -> PDD data segment
        mov     ds, ax

        mov     ax,FlatSel               ; load up flat selector for VMAlloced memory
        mov     fs,ax

        ; Make ES:BX point to parameters on the stack
        mov     ax,IDC_Parm_sel         ; Selector for Parameter packet
        mov     es,ax                   ; Load ES with Parameter Packet Sleector
        mov     bx,IDC_Parm_off         ; Offset to IDC Parameter packet

        ; Decode the Function being requested and call the routine to handle the function
        mov     eax,IDC.comm_ulFunction ; Which Function is being requested

IDCFUNC00:
        cmp     eax,DDCMD_SETUP
        jne     IDCFUNC01
        CALL    IDCFUNC_SETUP
        jmp     IDCFunc_Entry_DONE

IDCFUNC01:
        cmp     eax,DDCMD_READ
        jne     IDCFUNC02
        call    IDCFUNC_READ
        jmp     IDCFunc_Entry_DONE

IDCFUNC02:
        cmp     eax,DDCMD_STATUS
        jne     IDCFUNC03
        call    IDCFUNC_STATUS
        jmp     IDCFunc_Entry_DONE

IDCFUNC03:
        cmp     eax,DDCMD_CONTROL
        jne     IDCFUNC04
        call    IDCFUNC_CONTROL
        jmp     IDCFunc_Entry_DONE

IDCFUNC04:
        cmp     eax,DDCMD_REG_STREAM
        jne     IDCFUNC05
        call    IDCFUNC_REG_STREAM
        jmp     IDCFunc_Entry_DONE

IDCFUNC05:
        cmp     eax,DDCMD_DEREG_STREAM
        jne     IDCFUNC06
        call    IDCFUNC_DEREG_STREAM
        jmp     IDCFunc_Entry_DONE

IDCFUNC06:
        mov     eax,-1                          ; !!! Invalid Function

IDCFunc_Entry_DONE:
        mov     dx,0                            ; DX:AX is the RC (DX always zero)

        pop     edi
        pop     esi
        pop     ecx
        pop     ebx                             ; Restore Regs
        pop     fs
        pop     es
        pop     ds
        pop     ebp
        ret
ENDPROC IDC_Entry

;************************** I D C F U N C    S E T U P *************************************
;*
;* FUNCTION:
;*
;* INPUT:       es:bx -> Request packet.
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;******************************************************************************************
subttl IDC FUNCTION SETUP
page
Procedure IDCFUNC_SETUP, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        ; Check for Valid Stream Handle
        mov     ecx,IDC.setu_hStream     ; Get Stream Handle
        call    FIND_SI                  ; Is this a valid Stream Handle?
        cmp     eax,0                    ; 0=Invalid Stream Handle
        je      SETUP_ISH                ; Exit with Invalid Stream Handle

        mov     SI_CURR,EAX              ; Make this the Current Stream Instance

        ; Get Pointer to Setup parms
        les     cx,IDC.setu_pSetupParm   ; Get Addressabliity to Parm List (stream time)

        ; Get Stream time from parm list
        mov     si,cx                    ; offset to parms (with time in it)
        mov     ecx,es:[si]              ; get Stream Time
        mov     fs:[eax].SI_ulSHTime,ecx ; Save SI time in Stream Instance structure

        ; Report we assume all event to be reoccuring
        mov     dword ptr es:[si+4],1    ; Events are reoccuring  !!! use ne equates shdd.inc

        ; Get System Time  in milliseconds
        push    gs                  ; Save Segment for Card
        push    eax                 ;
        mov     ax,[Ginfo_Seg]      ; Global Info Segment
        mov     gs,ax               ; Load Segment for Global Info
        mov     bx ,[Ginfo_offset]  ; Global Info Offset
        mov     edx,gs:[bx+4]       ; Read Global Info tenths of milliseconds
        pop     eax
        pop     gs

        mov     fs:[eax].SI_ulSTime_Base,edx ; Save System Time setup occured
        sub     edx,ecx                      ; Delta between System Time - Stram Handler Time
        mov     fs:[eax].SI_ulTime_Delta,edx ; Save Delta between System Time and SH time

        mov     ax,0                    ; !!! Say Function worked
        jmp     SETUP_DONE

SETUP_ISH:                              ; Exit with Invalid Stream Handle
        mov     ax,-1                   ; !!! Say Invalid Stream Handle

SETUP_DONE:
        ret
ENDPROC IDCFUNC_SETUP

;************************** I D C C M D    R E A D ****************************************
subttl IDC FUNCTION READ
page
Procedure IDCFUNC_READ, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        pushFD
        CLI                             ; Disable Interrupts

        ; Check for Valid Stream Handle
        mov     ecx,IDC.read_hStream     ; Get Stream Handle
        call    FIND_SI                  ; Is this a valid Stream Handle?
        cmp     eax,0                    ; 0=Invalid Stream Handle
        je      READ_ISH                 ; Exit with Invalid Stream Handle

        mov     edx,IDC.read_ulBufferSize; New Buffer Length
        mov     ebx,IDC.read_pBuffer     ; New Buffer Address
        CALL    ADD_IBUFF               ; Add It Now

        mov     ax,0                    ; !!! Say Function worked
        jmp     READ_DONE

READ_ISH:                               ; Exit with Invalid Stream Handle
        mov     ax,-1                   ; !!! Invalid Stream Handle

READ_DONE:
        POPFD                           ; Enable Interrupts
        ret
ENDPROC IDCFUNC_READ

;************************** I D C C M D    S T A T U S ************************************
subttl IDC FUNCTION STATUS
page
Procedure IDCFUNC_STATUS, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        ; Make Sure Status is for the current SI
        mov     eax,SI_CURR              ; Get Current Stream Instance
        cmp     eax,0                    ; No Current Stream?
        je      STATUS_NCS               ; Error - Not Current Stream
        mov     edx,fs:[eax].SI_hStream
        cmp     IDC.stat_hStream,edx     ; Is this the current stream?
        jne     STATUS_NCS               ; Error - Not Current Stream

        ; Set Stream time in parm list
        mov     IDC.stat_ulStatusSize,SIZE STATUS_PARM ; Size of ParmList
        mov     cx,offset Status_time        ; Offset to Current Time
        mov     word ptr IDC.stat_pStatus,cx ; Pointer to current time (16:16)
        mov     word ptr IDC.stat_pStatus+2,ds
        mov     edx,sys_clock
        sub     edx,fs:[eax].SI_ulTime_Delta ; Convert to SH Stream time
        mov     status_time,edx              ; Set Stream Time

        mov     ax,0                    ; !!! Say Function worked
        jmp     STATUS_DONE

STATUS_NCS:                             ; Error - Not Current Stream
        mov     ax,-1                   ; !!! Say Not Current Stream

STATUS_DONE:

        ret
ENDPROC IDCFUNC_STATUS

;************************** I D C C M D    C O N T R O L *********************************
;*
;* FUNCTION:    This Decodes the IDC Control commands and calls the
;*              appropriate handler
;*
;* INPUT:       es:bx -> Request packet.
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;* NOTES:       All handlers should Set AX with the Return Code
;*
;******************************************************************************************
subttl IDC FUNCTION Control
page
Procedure IDCFUNC_CONTROL, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        ; Decode the Command being requested and call the routine to handle the function
        mov     eax,IDC.cont_ulCmd       ; Which Command is being requested

IDCCCMD00:
        cmp     eax,DDCMD_START          ; START Streaming request?
        jne     IDCCCMD01
        call    IDCCMD_START
        jmp     IDCFUNC_CONTROL_DONE

IDCCCMD01:
        cmp     eax,DDCMD_STOP           ; STOP Streaming request?
        jne     IDCCCMD02
        call    IDCCMD_STOP
        jmp     IDCFUNC_CONTROL_DONE

IDCCCMD02:
        cmp     eax,DDCMD_PAUSE          ; PAUSE Streaming request?
        jne     IDCCCMD03
        call    IDCCMD_PAUSE
        jmp     IDCFUNC_CONTROL_DONE

IDCCCMD03:
        cmp     eax,DDCMD_RESUME         ; RESUME Streaming request?
        jne     IDCCCMD04
        call    IDCCMD_RESUME
        jmp     IDCFUNC_CONTROL_DONE

IDCCCMD04:
        cmp     eax,DDCMD_ENABLE_EVENT   ; ENABLE  EVENT request?        !!! --NOT SUPPORTED
        jne     IDCCCMD05
        call    IDCCMD_ENABLE_EVENT
        jmp     IDCFUNC_CONTROL_DONE

IDCCCMD05:
        cmp     eax,DDCMD_DISABLE_EVENT  ; DISABLE EVENT request?        !!! --NOT SUPPORTED
        jne     IDCCCMD06
        call    IDCCMD_DISABLE_EVENT
        jmp     IDCFUNC_CONTROL_DONE

IDCCCMD06:
        mov     eax,-1                   ; !!! Invalid Function
IDCFUNC_CONTROL_DONE:

        ret
ENDPROC IDCFUNC_CONTROL

;************************** I D C C M D    R E G _ S T R E A M ****************************
subttl IDC FUNCTION REG_STREAM
page
Procedure IDCFUNC_REG_STREAM, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        CLI
;        int   3
        mov   ecx,IDC.regi_ulSysFileNum         ; Get System File Number
        call  FIND_OI
        cmp   eax,0
        je    REG_STREAM_FAIL           ; Didn't Find OPEN File Handle
        cmp   fs:[eax].OI_SI,0          ; No Stream Instance Registered
        jne   REG_STREAM_FAIL           ; - Stream Already Registered
        mov   esi,eax                   ; Save Open Instance Pointer

        ; Allocate and Initialize an IDC_SH  structure
        mov   ecx,SIZE STREAM_INSTANCE ; Size of storage to allocate
        add   ecx,SIZE STREAM_INSTANCE_EVENT * SIE_Num_Events
        mov   edi,-1
        ;         876543210
        mov   eax,000000010B ; Allocate in Global address space, fixed  storage
        mov   dl,DevHlp_VMAlloc
        call  device_hlp
        jc    REG_STREAM_FAIL           ; Storage for SI not available

        mov   fs:[esi].OI_SI,eax        ; Stream Instance Registered
        mov   ecx,IDC.regi_hStream      ; Get Stream Handle
        mov   fs:[esi].OI_hStream,ecx   ; Stream Instance Registered


        ; Calculate the size of 1 Image + it's Header for SI structure
        push    eax
        push    ebx
        mov     eax,usr_DY_Height        ; Height of image
        xor     edx,edx                  ; Zero edx for multiply
;        mov     ecx,usr_ScaleFactor      ; Scale image down by this factor
;        div     ecx                      ; EAX = size of Y scaled image
        mov     ebx,eax                  ; Save number of Scanlines
        mov     eax,usr_DX_Width         ; PELs Width
        shl     eax,1                    ; Convert PELs to Bytes
        xor     edx,edx                  ; Zero edx for multiply
;        div     ecx                      ; EAX = bytes per scanline
;        xor     edx,edx                  ; Zero edx for multiply
        mul     ebx                      ; EAX = size of scaled image in bytes
        add     eax,size VIDEO_FRAME_HDR ; allow room for header
        mov     ecx,eax                  ; Save size of 1 Image + the header
        pop     ebx
        pop     eax


        ; Initialize new SI structure
        mov   fs:[eax].SI_Stopping,0            ; Stream not stopping
        mov   fs:[eax].SI_ulImgSize,ecx         ; Save Size of one Image plus it's header
        mov   fs:[eax].SI_pNext_SI,0            ; add to SI list end
        mov   ecx,IDC.regi_hStream              ; Get Stream Handle
        mov   fs:[eax].SI_hStream,ecx           ; Save Stream Handle in SI struc
        mov   ecx,IDC.regi_ulSysFileNum         ; Get System File Number
        mov   fs:[eax].SI_ulSysFileNum,ecx      ; Save System File number in SI struc
        mov   ecx,IDC.regi_pSHDEntryPoint       ; Get Stream Handler Entry Point
        mov   fs:[eax].SI_pSHDEntryPoint,ecx    ; Save Stream Handler Entry Point
        mov   fs:[eax].SI_ulSHTime,0            ; Init later by SETUP
        mov   fs:[eax].SI_ulSTime_Base,0
        mov   fs:[eax].SI_pFree_img_buffs,0     ; NULL free Read Buffer List
        mov   fs:[eax].SI_pFree_img_Tail,0      ; NULL free Read Buffer List Tail

        mov   fs:[eax].SI_pCur_Img_Buff,0       ; NULL no Image buffer currently in use
        mov   fs:[eax].SI_ulCur_Img_Len,0       ; Remaining space in current buffer
        mov   fs:[eax].SI_pCur_Img_Free ,0      ; Address of free space in Current Buffer
        mov   fs:[eax].SI_ulCur_Img_Size,0      ; Size of Entire Buffer
        mov   fs:[eax].SI_pEventQ,0             ; Null Event Queue

        ; Load up SI with Image Size and Position Info (can't change  without Deregister)
        mov   ecx,usr_X_Left
        mov   fs:[eax].SI_X_Left,ecx
        mov   ecx,usr_Y_Top
        mov   fs:[eax].SI_Y_Top,ecx
        mov   ecx,usr_Y_Height
        mov   fs:[eax].SI_Y_Height,ecx
        mov   ecx,usr_X_Width
        mov   fs:[eax].SI_X_Width,ecx
        mov   ecx,usr_DX_Left
        mov   fs:[eax].SI_DX_Left,ecx
        mov   ecx,usr_DY_Top
        mov   fs:[eax].SI_DY_Top,ecx
        mov   ecx,usr_DY_Height
        mov   fs:[eax].SI_DY_Height,ecx
        mov   ecx,usr_DX_Width
        mov   fs:[eax].SI_DX_Width,ecx
        mov   ecx,usr_ScaleFactor
        mov   fs:[eax].SI_ScaleFactor,ecx
        mov   cx,usr_FPS
        mov   fs:[eax].SI_ulFPS,cx
        mov   fs:[eax].SI_ulSTime,0
        mov   fs:[eax].SI_ulCTime,0
        mov   fs:[eax].SI_ulFrames,0
        mov   fs:[eax].SI_ulSkipped,0
        mov   fs:[eax].SI_ulMissing,0
        mov   fs:[eax].SI_ulOverrun,0
        mov   fs:[eax].SI_ulUnderrun,0

        ; Create Queue of Free Stream Instance Events (SIE) elements
        push  ebx
        mov   ecx,SIE_Num_Events              ; Number of Events
        mov   edx,SIZE STREAM_INSTANCE        ; Size of SI prior to SIEs
        add   edx,eax                         ; Pointer to storage for Elements
Q_SIE_Free_Elements:
        mov   ebx,fs:[eax].SI_pEventQ_Free    ; head of List points to
        mov   fs:[edx].SIE_NEXT,ebx           ; New element points to old head
        mov   fs:[eax].SI_pEventQ_Free,edx    ; Queue to head of List
        add   edx,Size STREAM_INSTANCE_EVENT  ; Advance to next element
        loop  Q_SIE_Free_Elements
        pop   ebx

        ; add new Stream Instance to List
        call    ADD_SI

        ; Respond to SH's Register Stream
        mov     ecx,fs:[eax].SI_ulImgSize                  ; Size of one Image plus it's header
        mov     IDC.regi_ulBufSize,ecx                     ; Minimum Data buffer size in bytes
        mov     eax,Min_Num_Buffs                          ; Minimum number of Data buffers
        mov     IDC.regi_ulNumBufs,eax                     ; Minimum number of Data buffers
        mov     IDC.regi_ulAddressType,ADDRESS_TYPE_LINEAR ; addr ptr type for data buffer
        mov     IDC.regi_ulBytesPerUnit,0                  ; PDD output (required)
        mov     IDC.regi_mmtimePerUnit,0                   ; PDD output (required)
        mov     ax,0                                       ; !!! Say Function worked
        jmp     REG_STREAM_DONE


REG_STREAM_FAIL:
        mov     ax,1                     ; !!! Say Function Failed

REG_STREAM_DONE:
        POPFD
        ret
ENDPROC IDCFUNC_REG_STREAM

;************************** I D C C M D   D E R E G _ S T R E A M ***********************
subttl IDC FUNCTION DEREG_STREAM
page
Procedure IDCFUNC_DEREG_STREAM, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING
        PUSHFD
        CLI


        ;!!! Scan and remove Stream Instance from List  (only one SI at this time)
        mov     ecx,IDC.dere_hStream    ; Get Stream Handle
        call    REMOVE_SI               ; Remove the Stream Instance
        cmp     eax,0                   ; 0 = Invalid Stream Hnadle
        je      DEREG_STREAM_ISH

        push    eax
        mov     ecx,fs:[eax].SI_ulSysFileNum; Get System File Number
        call    FIND_OI                 ; Find Open Instance with File Num
        cmp     eax,0                   ; Was Open Instance with File Num Found
        je      DEREG_NO_OI             ; - Instance not found (should not occur)
;        INT 3
        mov     fs:[eax].OI_SI,0        ; Stream Instance DeRegistered
        mov     fs:[eax].OI_hStream,0   ; Stream Instance DeRegistered
DEREG_NO_OI:                            ; - No Open Instance Found
        pop   eax
        ; Free the IDC_SH  structure
        mov   dl,DevHlp_VMFree
        call  device_hlp
        jc    DEREG_STREAM_FAIL         ; Did we free the storage   Carry=1 is a failure

        mov   ax,0                      ; !!! Say Function worked
        jmp   DEREG_STREAM_DONE

DEREG_STREAM_FAIL:
        mov     ax,-2                    ; !!! Say Function Failed (what to do with SI struc)
        jmp   DEREG_STREAM_DONE

DEREG_STREAM_ISH:
        mov     ax,-1                    ; !!! Say Invalid Stream Handle

DEREG_STREAM_DONE:
        POPFD
        ret
ENDPROC IDCFUNC_DEREG_STREAM


;************************** I D C   F U N C T I O N   S T A R T *************************
subttl IDC FUNC CONTROL START
page
;            Start the Timer Interrupts
;
Procedure IDCCMD_START, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

;       les     bx,IDC.cont_parm   ; Get Addressability to parms

        ; Make Sure Start is for the current SI
        mov     eax,SI_CURR              ; Get Current Stream Instance
        cmp     eax,0                    ; No Current Stream?
        je      START_NCS                ; Error - Not Current Stream
        mov     edx,fs:[eax].SI_hStream
        cmp     IDC.cont_hStream,edx     ; Is this the current stream?
        jne     START_NCS                ; Error - Not Current Stream

;        ; Calculate the size of 1 Image + it's Header for SI structure
;        mov     eax,usr_DY_Height         ; Height of image
;;        xor     edx,edx                  ; Zero edx for multiply
;;        mov     ecx,usr_ScaleFactor      ; Scale image down by this factor
;;        div     ecx                      ; EAX = size of Y scaled image
;        mov     ebx,eax                  ; Save number of Scanlines
;        mov     eax,usr_DX_Width         ; PELs Width
;        shl     eax,1                    ; Convert PELs to Bytes
;        xor     edx,edx                  ; Zero edx for multiply
;;        div     ecx                      ; EAX = bytes per scanline
;;        xor     edx,edx                  ; Zero edx for multiply
;        mul     ebx                      ; EAX = size of scaled image in bytes
;        add     eax,size VIDEO_FRAME_HDR ; allow room for header
;        mov     ecx,eax                  ; Save size of 1 Image + the header

        mov     eax,[SI_CURR]            ; Get Stream Instance buffers address

;        mov     fs:[eax].SI_ulImgSize,ecx; Save Size of one Image plus it's header
        mov     fs:[eax].SI_ulFrames,0   ; Number of Frames Read in
        mov     fs:[eax].SI_ulSkipped,0  ; Total Frames Skipped
        mov     fs:[eax].SI_ulMissing,0  ; Frames missing since last read reported
        mov     fs:[eax].SI_ulOverrun,0  ; Total Overruns  on this stream
        mov     fs:[eax].SI_ulUnderrun,0 ; Total Underruns on this stream
        mov     fs:[eax].SI_ulUnderrun,0 ; Total Underruns on this stream

        push    gs                  ; Save Segment for Card
        push    eax                 ;
        mov     ax,[Ginfo_Seg]      ; Global Info Segment
        mov     gs,ax               ; Load Segment for Global Info
        mov     bx ,[Ginfo_offset]  ; Global Info Offset
        mov     eax,gs:[bx+4]       ; Read Global Info tenths of milliseconds
        mov     [Tic_next],eax      ; get time of next image capture
        pop     eax
        pop     gs

        ; Get Delta between System Time and Stream Time in milliseconds
        mov     ebx,Tic_next                 ; Time Streaming was started
        mov     fs:[eax].SI_ulSTime_Base,ebx ; Save System Time setup occured
        sub     ebx,fs:[eax].SI_ulSHTime     ; Delta between System Time - Stram Handler Time
        mov     fs:[eax].SI_ulTime_Delta,ebx ; Save Delta between System Time and SH time

        mov     ebx,Tic_next            ; Save time Streaming was started
        mov     fs:[eax].SI_ulSTime,ebx ; Save time Streaming was started
        mov     bx,usr_FPS              ; Save Frames Per Second
        mov     fs:[eax].SI_ulFPS,bx    ; Save Frames Per Second

        ; Calucate info for time between Image capture
        mov   ax,1000                 ; Millisecond in 1 second (system time in milliseconds)
        mov   dx,0                    ; High part of div dividend
        mov   bx,[usr_FPS]            ; Notify every N Frames/Sec
        div   bx                      ;
;        mov   [org_remain],dx        ; Save Divide Remainder
;        mov   [cur_remain],0         ; Current Remainder init to none
        and   eax,0ffffh              ; zero out high 2 bytes of eax
        mov   ebx,SI_CURR                 ; Get Current Stream Instance
        mov   fs:[ebx].SI_ulTic_Delta,eax ; Time Between Image Reads
        mov   fs:[ebx].SI_ulCur_Remain,0  ; Current remainder to handle rounding error
        mov   fs:[ebx].SI_ulOrg_Remain,dx ; Remainder from Divide

        add   [Tic_next],eax          ; Time to read next image

        ; Turn Interrupts on
        mov     Interrupt_on,1              ; Mark interrupts are on (process them)
        ;******* D E V I C E   D E P E N D E N T *********************************(below)
        ; Call Device Dependent Routine for Start Streaming   (turns on Interrupts)
        call    IDC_START_S
        ;******* D E V I C E   D E P E N D E N T *********************************(above)

        mov     ax,0                    ; !!! Say Function worked
        jmp     START_DONE

START_NCS:
        mov     ax,-1                    ; !!! Say Not the Current Stream
        jmp     START_DONE

START_DONE:

        ret
ENDPROC IDCCMD_START

;************************** I D C   F U N C T I O N   S T O P ***************************
subttl IDC FUNC CONTROL STOP
page
Procedure IDCCMD_STOP, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

;       Turn Off Timer Interrupts
        pushFD
        CLI                             ; Disable Interrupts

        ; Make Sure this is for the current SI
        mov     eax,SI_CURR              ; Get Current Stream Instance
        cmp     eax,0                    ; Any Stream in the List
        je      STOP_NCS                 ; Error - Invalid Stream
        mov     edx,fs:[eax].SI_hStream  ; Get Current Stream handle
        cmp     IDC.cont_hStream,edx     ; Is this the current stream?
        jne     STOP_NCS                 ; Error - Stream not current

        ; Turn off interrupts
        mov     Interrupt_on,0              ; Mark interrupts are off (ignore them)
        ;******* D E V I C E   D E P E N D E N T *********************************(below)
        ; Call Device Dependent Routine for Stop Streaming   (turns off Interrupts)
        call    IDC_STOP_S
        ;******* D E V I C E   D E P E N D E N T *********************************(above)

        mov     eax,SI_CURR              ; Get Current Stream Instance

        ; Check if Interrupt routine is active
        cmp    INTR_ACTIVE,0             ; Is Interrupt routine Running now
        je     INTR_STOPPED              ; NO - Interrupt Active now

INTR_WAIT:
        ; Block and wait till Interrupt routine is done with the current buffer
        ;  This is not a problem because VCSH and other task time calls can
        ;  not get in if the interrupt handle is active
        ;  the only call at interrupt time that occurs is Stop Flush or
        ;  stop Discard from VCSH after I have passed back the last buffer
        ;  so I wouldn't be coping into a buffer I don't own.
;        MOV   FS:[EAX].SI_Stopping,1     ; Mark that Stop is Pending
;        PUSH  EAX
;        PUSH  EBX
;        PUSH  ECX
;        MOV   EBX,EAX                    ; BX = Low word of Event ID
;        SHR   EAX,16                     ; AX = High word of Event ID
;        MOV   DI,-1                      ; Time Limit High (-1 = never)
;        MOV   CX,-1                      ; Time limit low
;        MOV   DH,1                       ; Non-Interrruptable
;        mov   dl,DevHlp_ProcBlock
;        inc   block_wait
;   INT 3
;        call  device_hlp                 ; Put Caller to sleep
;        CLI
;   INT 3
;        POP   ECX
;        POP   EBX
;        POP   EAX
;        cmp    INTR_ACTIVE,0             ; Is Interrupt routine Running now
;        jne    INTR_WAIT                 ; YES - wait for it to stop

INTR_STOPPED:
        ; Zero Buffer Addresses to stop later interrupts from using buffers
        mov    fs:[eax].SI_pFree_img_buffs,0     ; NULL free Read Buffer List
        mov    fs:[eax].SI_pCur_Img_Buff,0       ; NULL no Image buffer currently in use
        mov    fs:[eax].SI_ulCur_Img_Len,0       ; Remaining space in current buffer
        mov    fs:[eax].SI_pCur_Img_Free ,0      ; Address of free space in Current Buffer
        mov    fs:[eax].SI_ulCur_Img_Size,0      ; Size of Entire Buffer
;        mov    SI_CURR,0                         ; No Current Stream Instance


        mov     ax,0                    ; !!! Say Function worked
        jmp     STOP_DONE

STOP_NCS:
        mov     ax,-1                    ; !!! Say Not Current Stream Handle
        jmp     STOP_DONE

STOP_DONE:
        popfd
        RET
ENDPROC IDCCMD_STOP

;************************** I D C   F U N C T I O N   P A U S E *************************
subttl IDC FUNC CONTROL PAUSE
page
Procedure IDCCMD_PAUSE, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        CLI

        ; Make Sure Start is for the current SI
        mov     eax,SI_CURR              ; Get Current Stream Instance
        cmp     eax,0                    ; No Current Stream?
        je      PAUSE_ISH                ; Error - Not Current Stream
        mov     edx,fs:[eax].SI_hStream  ; Get Current Stream Handle
        cmp     IDC.cont_hStream,edx     ; Is this the current stream?
        jne     PAUSE_ISH                ; If Invalid Handle

        ; Get System Time  in milliseconds
        mov     edx,Tic_next                 ; Time of next capture
        sub     edx,sys_clock                ; less current time
        mov     fs:[eax].SI_ulResume_Tic_Delta,edx; Time to next capture on resume
        mov     edx,sys_clock                ; Current time
        mov     fs:[eax].SI_ulPause_Time,edx ; Save time of pause
        sub     edx,fs:[eax].SI_ulTime_Delta ; Convert to SH Stream time

        ; Set Stream time in parm list
        push    es
        mov     IDC.cont_ulParmSize,4    ; Size of ParmList
        les     cx,IDC.cont_pParm        ; Get Addressabliity to Parm List (stream time)
        mov     si,cx                    ; offset to parms (with time in it)
        mov     es:[si],edx              ; Set Stream Time
        pop     es

        ; Turn off interrupts (well very slow any way)
        mov     Interrupt_on,0              ; Mark interrupts are off (ignore them)
        ;******* D E V I C E   D E P E N D E N T *********************************(below)
        ; Call Device Dependent Routine for Pause Streaming   (turns off Interrupts)
        call    IDC_PAUSE_S
        ;******* D E V I C E   D E P E N D E N T *********************************(above)

        mov     ax,0                    ; !!! Say Function worked
        jmp     PAUSE_DONE

PAUSE_ISH:                              ; Invalid Stream Handle
        mov     ax,-1                   ; !!! Say Invalid Stream Handle

PAUSE_DONE:

        POPFD
        RET
ENDPROC IDCCMD_PAUSE

;************************** I D C   F U N C T I O N   R E S U M E ***********************
subttl IDC FUNC CONTROL RESUME
page
Procedure IDCCMD_RESUME, NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        CLI

        ; Make Sure Start is for the current SI
        mov     eax,SI_CURR              ; Get Current Stream Instance
        cmp     eax,0                    ; No Current Stream?
        je      RESUME_ISH               ; Error - Not Current Stream
        mov     edx,fs:[eax].SI_hStream  ; Get Current Stream Handle
        cmp     IDC.cont_hStream,edx     ; Is this the current stream?
        jne     RESUME_ISH               ; If Invalid Handle


        ; Get new system time after restart
        push    gs
        push    eax
        mov     ax,[Ginfo_seg]      ; Find Global Info segment
        mov     gs,ax               ; Load up global info Segment
        mov     bx ,[Ginfo_offset]  ; Global Info Offset
        mov     ecx,gs:[bx+4]       ; Read Global Info tenths of milliseconds
        mov     [sys_clock],ecx     ; Current System Time
        pop     eax
        pop     gs

        mov     ebx,fs:[eax].SI_ulPause_Time ; Time when pause occured
        sub     ecx,ebx                      ; Length of Pause
        add     fs:[eax].SI_ulSTime_Base,ebx ; Recalculated start time

        mov    ebx,fs:[eax].SI_ulResume_Tic_Delta; Delta to next capture on resume
        mov    edx,sys_clock                ; Time When Resume Occured
        add    edx,ebx                      ; Setup + Delta = time to read next image
        mov    [Tic_next],edx               ; Time to read next image

        ; Turn Interrupts back on
        mov     Interrupt_on,1              ; Mark interrupts are on (process them)
        ;******* D E V I C E   D E P E N D E N T *********************************(below)
        ; Call Device Dependent Routine for Resume Streaming   (turns on Interrupts)
        call    IDC_RESUME_S
        ;******* D E V I C E   D E P E N D E N T *********************************(above)

        mov     ax,0                        ; !!! Say Function worked
        jmp     RESUME_DONE

Resume_ISH:                                 ; Invalid Handle on Resume
        mov     ax,-1                       ; !!! Say Stream Handle

RESUME_DONE:
        POPFD
        RET
ENDPROC IDCCMD_RESUME

;***************************************************************************
;* FUNCTION:    Enable Event  (Add an Event to the Event LIst)
;*
;* INPUT:       ES:BX  = Control / Enable Event Parms
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;***************************************************************************
Procedure IDCCMD_ENABLE_EVENT,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        CLI                                   ; Turn Off Interrupt (Critical Section)

        ; Check for Valid Stream Handle
        mov     ecx,IDC.cont_hStream     ; Get Stream Handle
        call    FIND_SI                  ; Is this a valid Stream Handle?
        cmp     eax,0                    ; 0=Invalid Stream Handle
        je      Add_Event_Fail_ISH       ; Invalid Stream Handle

        ; Check For Duplicate Event Handle alrady in Queue
        mov   edx,IDC.cont_hEvent     ; Get Evnet handle to be added
        mov   ecx,fs:[eax].SI_pEventQ ; First Event in Event Queue
dup_event_scan:                       ; Walk Event Queue
        cmp   ecx,0                   ; Is this the end of the List
        je    Walk_dup_Event_done     ; If End of the Queue (Event was not found)
        cmp   fs:[ecx].SIE_hEVENT,edx ; Is the Event Already in the Queue
        je    Add_Event_FAIL_SE       ; Event already in the Queue
        mov   ecx,fs:[ecx].SIE_NEXT   ; Next Event in the Queue
        jmp   dup_event_scan
Walk_dup_Event_done:

        ; Allocate a Stream Instance Event structure
        mov   edi,fs:[eax].SI_pEventQ_Free  ; Get Address of First Free Element
        cmp   edi,0                         ; Was it NULL
        je    ADD_EVENT_FAIL_NS             ; Did we get the storage (Max Events Exceeded)
        mov   ecx,fs:[edi].SIE_Next         ; Next Element after the one we are allocating
        mov   fs:[eax].SI_pEventQ_Free,ecx  ; Update head to next Element

        mov   edx,IDC.cont_hEvent           ; Save Event Handle
        mov   fs:[edi].SIE_hEVENT,EDX

        ; Get Stream Time From Parm List
        push  es
        les   cx,IDC.cont_pParm               ; Get Addressabliity to Parm List (stream time)
        mov   si,cx
        mov   edx,es:[si]                     ; Get Stream Time
        MOV   fs:[edi].SIE_TIME,EDX           ; Time between Events
        pop   es

        push  gs                              ; Save Segment for Card
        push  eax                             ;
        mov   ax,[Ginfo_Seg]                  ; Global Info Segment
        mov   gs,ax                           ; Load Segment for Global Info
        mov   bx ,[Ginfo_offset]              ; Global Info Offset
        mov   ecx,gs:[bx+4]                   ; Read Global Info tenths of milliseconds
        pop   eax
        pop   gs
        add   edx,ecx                         ; Time for Notify to occur
        mov   fs:[edi].SIE_NTIME,EDX          ; Time of Event   !!!! (what format is time)


        MOV   EDX,fs:[eax].SI_pEventQ         ; Get The Last Free Buffer
        MOV   fs:[edi].SIE_NEXT,EDX           ; Update New Event to point to old Head
        MOV   fs:[eax].SI_pEventQ,edi         ; The new Event in now the head

        mov     ax,0                          ; !!! Say Function worked
        JMP   Add_Event_Done

ADD_EVENT_FAIL_NS:                            ; No Stoage Available
        MOV   EAX,-1                          ;!!!
        JMP   Add_Event_Done

Add_Event_FAIL_SE:                            ; Event already in Event Queue
        MOV   EAX,-2                          ;!!!
        JMP   Add_Event_Done

Add_Event_Fail_ISH:                           ; Inalid Stream Handle
        MOV   EAX,-3                          ;!!!
        JMP   Add_Event_Done

Add_Event_Done:
        POPFD
        RET
EndProc IDCCMD_ENABLE_EVENT

;***************************************************************************
;* FUNCTION:    Disable Event  (Remove an Event from the Event LIst)
;*
;* INPUT:       ES:BX  = Control / Diable Event Parms
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;***************************************************************************
Procedure IDCCMD_DISABLE_EVENT,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        CLI                                   ; Turn Off Interrupt (Critical Section)

        ; Check for Valid Stream Handle
        mov     ecx,IDC.cont_hStream     ; Get Stream Handle
        call    FIND_SI                  ; Is this a valid Stream Handle?
        cmp     eax,0                    ; 0=Invalid Stream Handle
        je      Diable_Event_ISH         ; Invalid Stream Handle

        ; Check For Valid Event Handle (should alraedy be in the Queue)
        mov   edx,IDC.cont_hEvent     ; Get Evnet handle to be added
        mov   ecx,fs:[eax].SI_pEventQ ; First Event in Event Queue
        mov   ebx,0                   ; Assume first buffer in the list
valid_event_scan:                     ; Walk Event Queue
        cmp   ecx,0                   ; Is this the end of the List
        je    Valid_Event_FAIL_NIQ    ; If End of the Queue (Event Not in Queue}
        cmp   fs:[ecx].SIE_hEVENT,edx ; Is the Event Already in the Queue
        je    Valid_Event_found       ; Found Event in the Queue
        mov   ebx,ecx                 ; Previous SIE elelemt in the queue
        mov   ecx,fs:[ecx].SIE_NEXT   ; Next Event in the Queue
        jmp   Valid_event_scan

Valid_Event_found:
        ; Remove event from the queue
        mov  edx,fs:[ecx].SIE_Next    ; Next element after the one to be freeed
        cmp  ebx,0                    ; No previous elelemt (head of Queue)
        je   Disable_event_RemoveHead ; It was at the head of the Queue
        mov  fs:[ebx].SIE_Next,edx    ; Make Previous point to Next element
        jmp  Disable_Event_Removed    ;

Disable_event_RemoveHead:             ; element to remove is at the head
        mov  fs:[eax].SI_pEventQ,edx  ; Make Head point to next element

Disable_Event_Removed:
                                            ; ECX = Buffer to be FREEed
        ; Free the Stream Instance Event (SIE)  structure
        mov   edx,fs:[eax].SI_pEventQ_Free  ; Get Address of First Free Element
        mov   fs:[ecx].SIE_Next,edx         ; Point to old head
        mov   fs:[eax].SI_pEventQ_Free,ecx  ; Update head to new Element

        mov   ax,0                           ; !!! Say Function worked
        JMP   Disable_Event_Done

Diable_Event_ISH:                            ; Invalid Stream Handle
        MOV   EAX,-1                          ;!!!
        JMP   Disable_Event_Done

VALID_EVENT_FAIL_NIQ:                         ; Event Not In the Queue
        MOV   EAX,-2                          ;!!!
        JMP   Disable_Event_Done

Disable_Event_Done:
        POPFD
        RET
EndProc IDCCMD_DISABLE_EVENT


;***************************************************************************
;  Timer Interrupt handler (called by system timer code every  clock tick
;
;***************************************************************************
Procedure TimeIntrpt,FAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

;        cli

        BTS     INTR_ACTIVE,1    ; Check for nested reentrent interrupt handler
        jnc     not_nested       ; not rentrent
;        int 3
        jmp     nest_exit        ; We are reentrent (should not occur)
not_nested:


        push    ds
        push    gs
        push    fs
        push    eax
        push    ebx

        mov     ax,DATA                 ; DS -> DGROUP
        mov     ds,ax
        inc     [tic_toc]               ; Count Interrupts

        mov     ax,FlatSel               ; load up flat selector for VMAlloced memory
        mov     fs,ax
        mov     [skipped],0         ; Number of frames skipped on this interrupt


;!!!!!!!!  Get Time Info
        mov     ax,[Ginfo_seg]      ; Find Global Info segment
        mov     gs,ax               ; Load up global info Segment
        mov     bx ,[Ginfo_offset]  ; Global Info Offset
        mov     ecx,gs:[bx+4]       ; Read Global Info tenths of milliseconds
        mov     [sys_clock],ecx     ; Save tenths of milliseconds for trace info

        mov     ebx,[tic_next]      ; get time to capture next image
        sub     ecx,ebx             ; Compare next image and current image times
        jb      Exit_tIntr          ; Too early wait till later

        ; Are the interrupts(tickcount) turned off
        ;  we cann't ResetTimer turn them off completely if we want to set TICKCOUNT
        ;  from the interrupt level tickcount must already be on se we just pretend they are
        ;  off
        cmp     interrupt_on,0       ; are we pretending TickCount/Timer is turned off
        je       Timer_is_off        ; Pretend the Tic never occured

        mov     eax,[SI_CURR]        ; Get Current Stream Instance buffers address

        mov     [skipped],0         ; Number of frames skipped on this interrupt
        mov     overrun,1           ; Assume Overrun occurs (no Buffer available)
        cmp     fs:[eax].SI_pCur_Img_Buff,0 ; Are we reading multiple images into one SH Buffer
        jne     Got_a_Buffer

        ; Get a new buffer
        CALL    REMOVE_IBUFF
        CMP     EBX,0               ; Are We out of Buffers
        je      Find_Next_Time      ; no Buffer to copy image into

        ; Save Info on New Buffer
        mov     fs:[eax].SI_pCur_Img_Free,ebx  ; Start of Free space in Current Streaming Buffer
        mov     fs:[eax].SI_pCur_Img_Buff,ebx  ; Start of Current Streaming Buffer
        mov     fs:[eax].SI_ulCur_Img_Len,edx  ; Remaining space in current buffer
        mov     fs:[eax].SI_ulCur_Img_Size,edx ; Size of Entire Buffer

Got_a_Buffer:
        mov     edx,fs:[eax].SI_ulImgSize      ; Size of 1 image plus a header
        sub     fs:[eax].SI_ulCur_Img_Len,edx  ; Bytes in current buffer after this image
        mov     overrun,0           ; Overrun did not occurs
        mov     [skipped],-1        ; Number of frames skipped on this interrupt

        inc     fs:[eax].SI_ulFrames; Record Total Frames Read for stream

Find_next_time:
        mov     edi,eax                     ; Save Addrress of SI Structure
        mov     edx,fs:[edi].SI_ulTic_Delta ; Time between image captures
        ; Check to see if we have missed 1 or more frames...
        mov     eax,ecx             ; Time past next time to capture

        mov     bx,[usr_FPS]        ; Speed of capture in Frames per Second
        mov     [round],0           ; Number of time tic_next rounded-up on this interrupt
next_cap_time:
        inc     [skipped]           ; Number of frames skipped on this interrupt
        mov     ax,fs:[edi].SI_ulCur_Remain ; Current remainder to handle rounding error
        add     ax,fs:[edi].SI_ulOrg_Remain ; Remainder from Divide 1000 MPS / N FPS
        cmp     ax,bx               ; Has Remainder rolled-over
        jb      roundup_end         ; Jmp if Remainder < than divisor
        sub     ax,bx               ; calculate new remainder
        inc     [tic_next]          ; Time to round up 1
        inc     [round]             ; Count number of times rounding up occurs
roundup_end:
        mov     fs:[edi].SI_ulCur_Remain,ax ; Current remainder (time Fraction)
        add     [tic_next],edx      ; add delta to last time to capture for new time
        sub     ecx,edx             ; decrement time difference by one frame time
        cmp     ecx,edx             ; Greater than 1 more frame time?
        jns     next_cap_time       ; any more frames skipped?

ReadImage:

        mov    ecx,skipped
        mov    eax,[SI_CURR]             ; Get Current Stream Instance
        add    fs:[eax].SI_ulMissing,ecx ; Number of Frames Skipped from last read
        add    fs:[eax].SI_ulSkipped,ecx ; Total Frames skipped since stream was started

        cmp    overrun,0                 ; Did and Overrun occurs (no buffer to copy into)?
        jne    Exit_nobuff               ; no Buffer to copy image into (tell SH)

        add     fs:[eax].SI_ulUnderrun,ecx ; Total Underruns on this stream

        ;******* D E V I C E   D E P E N D E N T *********************************(below)
        ; Call Device Dependent Routine fo copt image into Streaming Buffer
        call    IDC_GETIMAGE_S           ; Copy the image into the streaming buffer
        ;******* D E V I C E   D E P E N D E N T *********************************(above)


       ; Fill in Header for this Image
       mov       eax,[SI_CURR]                  ; Get Stream Instance buffers address
       mov       ebx,fs:[eax].SI_pCur_Img_Free  ; Address for Buffer Header
       mov       ecx,fs:[eax].SI_ulFrames       ; Record Total Frames Read for stream
       mov       fs:[ebx].vfh_FrameNumber,ecx   ; Fill in header Images copied
       mov       ecx,fs:[eax].SI_ulmissing      ; Number of Frames skipped since last copy
       mov       fs:[ebx].vfh_FramesSkipped,ecx ; Fill in header Images Skipped
       mov       fs:[eax].SI_ulCTime,ecx        ; Time this image was captured
       sub       ecx,fs:[eax].SI_ulTime_Delta   ; Convert to SH Stream time
       mov       fs:[ebx].vfh_StreamTime,ecx    ; Save Current System Time in Header
       mov       ecx,fs:[eax].SI_ulImgSize      ; Get Size of one Image plus it's header
       sub       ecx,size VIDEO_FRAME_HDR       ; Make size of Frame/Image only
       mov       fs:[ebx].vfh_FrameSize,ecx     ; Image + Header size into the header
;      mov       ecx,fs:[eax].SI_ulSkipped      ; Total Frames skipped since stream was started
;      mov       fs:[ebx].vfh_reserved4,ecx     ; Total skipped frames in the Stream from start
       mov       fs:[eax].SI_ulmissing,0        ; Reset missing/unreported frames in stream

       ; If no more images will fit in the buffer Tell the SH
       mov     edx,fs:[eax].SI_ulImgSize      ; Size of 1 image plus a header
       add     fs:[eax].SI_pCur_Img_Free,edx  ; Where to put next Image
       cmp     fs:[eax].SI_ulCur_Img_Len,edx  ; Bytes in current buffer after this image
       ja      Got_Image

       ; Tell SH that Buffer is full
       sub       esp,size shd_reportint_parm   ; Room for parm packet on stack
       lea       esi,[esp]                     ; Paramet Packet addr
       mov       ss:[esi].repi_ulFunction,SHD_REPORT_INT
       mov       ecx,fs:[eax].SI_hStream ; Get Stream Handle
       mov       ss:[esi].repi_hStream,ecx
       mov       ebx,fs:[eax].SI_pCur_Img_Buff ; Start of Current Streaming Buffer
       mov       ss:[esi].repi_pBuffer,ebx     ; Buffer with Images(s) in it
       mov       fs:[eax].SI_pCur_Img_Buff,0   ; Buffer No Longer available
       mov       ss:[esi].repi_ulFlag,SHD_READ_COMPLETE
       mov       ecx,fs:[eax].SI_ulCur_Img_Size ; Size of Entire Buffer
       sub       ecx,fs:[eax].SI_ulCur_Img_Len  ; - Bytes remaining in this Buffer
       mov       ss:[esi].repi_ulStatus,ecx     ; = Bytes read/used in this Buffer
       mov       ecx,sys_clock               ; Sytem Time Event occured
       mov       fs:[eax].SI_ulCTime,ecx     ; Time this image was captured
       sub       ecx,fs:[eax].SI_ulTime_Delta   ; Convert to SH Stream time
       mov       ss:[esi].repi_ulStreamTime,ecx ; Stream Time image was captured
       push      ss
       push      si
       call      fs:[eax].SI_pSHDEntryPoint    ; Tell Stream Handler
       pop       si
       pop       ss
       add       esp,size shd_reportint_parm   ; Room for parm packet on stack

got_image:
       cmp       skipped,0                     ; Did an underrun occur
       je        Exit_tIntr
                                               ; *** U N D E R R U N ***
       mov       ecx,ERROR_DEVICE_UNDERRUN     ; Error = UnderRun = missed Interrupt(s)
       jmp       Report_lost_Image

Exit_nobuff:                                   ; *** O V E R R U N ***
       add      fs:[eax].SI_ulOverrun,ecx      ; Total buffers missed because of Overruns
       mov      ecx,ERROR_DEVICE_OVERRUN       ; Error = OverrRun = No Buffer to copy into

Report_lost_Image:
       ; Tell SH Buffer OVERRUN  (no buffers to put image in)
       sub       esp,size shd_reportint_parm   ; Room for parm packet on stack
       lea       esi,[esp]                     ; Paramet Packet addr
       mov       ss:[esi].repi_ulFunction,SHD_REPORT_INT
       mov       eax,[SI_CURR]                 ; Get Stream Instance buffers address
       mov       ebx,fs:[eax].SI_hStream       ; Get Stream Handle
       mov       ss:[esi].repi_hStream,ebx
       mov       ss:[esi].repi_pBuffer,0
       mov       ss:[esi].repi_ulFlag,ERROR
       mov       ss:[esi].repi_ulStatus,ecx     ; Error = Overrun/Underrun
       mov       ebx,sys_clock                  ; Sytem Time Event occured
       sub       ebx,fs:[eax].SI_ulTime_Delta   ; Convert to SH Stream time
       mov       ss:[esi].repi_ulStreamTime,ebx ; Stream Time when overrun occured
       push      ss
       push      si
       call      fs:[eax].SI_pSHDEntryPoint    ; Tell Stream Handler
       pop       si
       pop       ss
       add       esp,size shd_reportint_parm   ; Room for parm packet on stack

Exit_tIntr:
       ;**** Do Notify/Event Processessing ***
       ; Walk Event List
       mov       eax,[SI_CURR]           ; Get Stream Instance buffers address
       mov       ebx,fs:[eax].SI_pEventQ ; Get Head of Event Queue for SI
Next_Event:
       cmp       ebx,0                   ; Is this the end of the Event list
       je        End_of_EventQ           ; No more Events in the Queue
       mov       ecx,fs:[ebx].SIE_NTime  ; What Time is event scheduled for
       mov       edx,fs:[ebx].SIE_NEXT   ; Get Next Event in the Queue
       cmp       ecx,sys_clock           ; Is it time to Notify SH yet
       ja        Not_time_yet            ; Skip notify if not time Yet

       push      edx
       ; Calculate new time for Event
       mov       ecx,fs:[ebx].SIE_Time   ; Time between Notifies
       add       fs:[ebx].SIE_NTime,ecx  ; Time this event is rescheduled for

       ;   EAX = pointer to Stream Instance       (SI)  Stucture
       ;   EBX = pointer to Stream Instance Event (SIE) Stucture
       ; Tell SH that the Event has occured

       sub       esp,size shd_reportevent_parm             ; Room for parm packet on stack
       lea       esi,[esp]                                 ; Paramet Packet addr
       mov       ss:[esi].repe_ulFunction,SHD_REPORT_EVENT ; Report Event Function
       mov       edx,fs:[eax].SI_hStream                   ; Get Stream Handle
       mov       ss:[esi].repe_hStream,edx
       mov       edx,fs:[ebx].SIE_hEvent                   ; Get Event Handle
       mov       ss:[esi].repe_hEvent,edx
       mov       edx,sys_clock                             ; Sytem Time Event occured
       sub       edx,fs:[eax].SI_ulTime_Delta              ; Convert to SH Stream time
       mov       ss:[esi].repe_ulStreamTime,edx
       push      ss
       push      si
       call      fs:[eax].SI_pSHDEntryPoint                ; Tell Stream Handler
       pop       si
       pop       ss
       add       esp,size shd_reportevent_parm             ; Room for parm packet on stack
       pop       edx

Not_time_yet:
       mov       ebx,edx                    ; Get Next Event in the Queue
       jmp       Next_Event
End_of_EventQ:

Timer_is_off:
;!!!!  TEMP CODE to trace frame skipping info %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TOP
;!!!!  TEMP CODE to trace frame skipping info %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TOP
        ; update Trace Record
        mov     eax, offset trace_start
        add     [trace_ptr],16
        mov     ecx, offset trace_end
        sub     ecx, offset trace_start
        cmp     ecx,[trace_ptr]
        ja      skip1
        mov    [trace_ptr],16               ; Second Trace Record
skip1:
        ; Log Trace Data
        mov     eax, offset trace_start     ; Add Trace Record .....
        add     eax, trace_ptr              ; Start of trace record
        mov     edx,[Tic_Toc]               ; Count of Interrupts
        mov     ds:[eax],dx
        mov     edx,skipped                 ; Number of images skipped on this interrupt
        mov     ds:[eax]+2,dx
        mov     edx,[sys_clock]             ; tenths of milliseconds
        mov     ds:[eax+4],edx
        mov     ebx,[SI_CURR]               ; Get Stream Instance buffers address
        mov     edx,fs:[ebx].SI_ulFrames    ; Record Total Frames Read for stream
        mov     ds:[eax+8],dx
        mov     edx,fs:[ebx].SI_ulUnderrun  ; Total buffers missed because of Underrun
        mov     ds:[eax+10],dx
        mov     edx,fs:[ebx].SI_ulOverrun   ; Total buffers missed because of Overruns
        mov     ds:[eax+12],dx
        mov     edx,fs:[ebx].SI_ulSkipped   ; Total Frames skipped since stream was started
        mov     ds:[eax+14],dx

        mov     ecx,[Skipped]        ; Number of Frames skipped on this interrupt
        cmp     ecx,0                ; Were we too slow and skipped some frames
        jne     skip2                ; No frames were skipped  (don't trace)

        sub    [trace_ptr],16        ; Don't Trace - backup and reuse trace record
skip2:
        nop
;!!!!  TEMP CODE to trace frame skipping info %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% BOTTOM
;!!!!  TEMP CODE to trace frame skipping info %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% BOTTOM


        pushfd
        CLI
        MOV   INTR_ACTIVE,0                   ; No Longer nested/reentrent interrupt
        CMP   fs:[ebx].SI_STOPPING,0
        je    Running

        ; Unblock Stop request waiting for Stream Buffer uses to end
        ;  This is not a problem because VCSH and other task time calls can
        ;  not get in if the interrupt handle is active
        ;  the only call at interrupt time that occurs is Stop Flush or
        ;  stop Discard from VCSH after I have passed back the last buffer
        ;  so I wouldn't be coping into a buffer I don't own.
        ;
;        int 3
;        MOV     EAX,EBX                 ; AX = High word of Event ID
;        SHR     EAX,16                  ; BX = Low  word of Event ID
;        MOV     DL,DevHlp_ProcRun
;        inc     run_wait
;        CALL    device_hlp              ; Let Stop Run now
Running:
        POPFD

        pop     ebx
        pop     eax
        pop     fs
        pop     gs
        pop     ds

nest_exit:
;        sti
        ret

EndProc TimeIntrpt

;***************************************************************************
;* FUNCTION:    Add a buffer to the list of Free Buffers from the Stream Handler
;*              This will be a FIFO list    (SH must get buffers back in FIFO order)
;*
;* INPUT:       EDX  = Buffer Length
;*              EBX  = Buffer Address
;*              EAX  = Stream Instance (SI) structure Address
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;***************************************************************************
Procedure ADD_IBUFF,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        PUSH  ECX
        CLI                                   ; Turn Off Interrupt (Critical Section)

        MOV   ECX,fs:[eax].SI_pFree_img_Tail  ; Get The Last Free Buffer
        CMP   ECX,0                           ; Is list Empty?
        jne   ADD_buff_to_nonempty_list

Add_buff_to_Empty_list:                       ; add buff to empty list of buffer
        MOV   fs:[eax].SI_pFree_img_buffs,EBX ; Update Head of List
        JMP   ADD_Buff_Done

ADD_buff_to_nonempty_list:                    ; Add buffer to nonEmpty list of buffer
        MOV   fs:[ecx].SIFBH_Next,EBX         ; Update old last buffer to point to new buffer

ADD_Buff_Done:
        MOV   fs:[ebx].SIFBH_Next,0           ; Next Buffer after the Last one is NULL
        MOV   fs:[ebx].SIFBH_Len,edx          ; Length of this buffer
        MOV   fs:[eax].SI_pFree_img_Tail,EBX  ; Update Tail of List to point to new buffer

        POP   ECX
        POPFD
        RET
EndProc ADD_IBUFF

;***************************************************************************
;* FUNCTION:    Remove a buffer from the list of Free Buffers from the Stream Handler
;*              This is a FIFO list  (SSM require buffers are returned in FIFO order)
;*
;* INPUT:
;*              EAX  = Stream Instance (SI) structure Address
;*
;* EXIT-NORMAL:
;*              EBX  = Buffer Address
;*              EDX  = Buffer Length
;*
;* EXIT_ERROR:
;*              EBX = 0
;*              EDX = 0
;***************************************************************************
Procedure REMOVE_IBUFF,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        PUSH  ECX
        CLI                                   ; Turn Off Interrupt (Critical Section)

        MOV   EDX,0                           ; Assume No buffer found, Length = 0
        MOV   EBX,fs:[eax].SI_pFree_img_buffs ; Get First buffer in Free List
        CMP   EBX,0                           ; Is the List Empty
        JE   REMOVE_IBUFF_DONE

REMOVE_IBUFF_NOTEMPTY:
        ; Remove First Buffer and make the Second buffer the Frist Buffer
        MOV   ECX,fs:[ebx].SIFBH_Next          ; Next Free Buffer of the First Buffer
        MOV   EDX,fs:[ebx].SIFBH_Len           ; Lenght of this Buffer
        MOV   fs:[eax].SI_pFree_img_buffs,ECX  ; Make Next buffer first in Free List

        CMP   fs:[eax].SI_pFree_img_Tail,EBX   ; Is the List Now Empty? Buff removed was Tail
        jNE   REMOVE_IBUFF_DONE
        MOV   fs:[eax].SI_pFree_img_Tail,0     ; Null Tail pointer out when Empty


REMOVE_IBUFF_DONE:

        POP   ECX
        POPFD
        RET
EndProc REMOVE_IBUFF


;***************************************************************************
;* FUNCTION:    Find a Stream Instance (SI) in the SI_LIST
;*
;* INPUT:
;*              ECX  = Stream Handle to Find
;*
;* EXIT-NORMAL: EAX  = pointer to SI
;*
;*
;* EXIT_ERROR:  EAX = 0 (not in the List)
;*
;***************************************************************************
Procedure FIND_SI,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        PUSH  ECX
        CLI                                   ; Turn Off Interrupt (Critical Section)

        MOV   EAX,SI_List                     ; Find first SI in the List
FIND_SI_Next:
        CMP   EAX,0                           ; Is list Empty?
        je    FIND_SI_NIL                     ; Stream Handle - Not in List

        cmp   fs:[eax].SI_hStream,ecx         ; Is this to correct SI
        je    FIND_SI_DONE                    ; We Found the SI

        mov   eax,fs:[eax].SI_pNext_SI        ; Try next SI
        jmp   FIND_SI_Next                    ; Loop until all SI(s) are checked


FIND_SI_NIL:                                  ; Not in the List

FIND_SI_DONE:                                 ; We Found the SI


        POP   ECX
        POPFD
        RET
EndProc FIND_SI

;***************************************************************************
;* FUNCTION:    Add a Stream Instance (SI) to the SI_LIST
;*              This will be a LIFO list
;*
;* INPUT:       EAX  = New Element to be added   (SI)
;*
;* EXIT-NORMAL:
;*
;* EXIT_ERROR:
;*
;***************************************************************************
Procedure ADD_SI,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        PUSH  ECX
        CLI                                   ; Turn Off Interrupt (Critical Section)

        MOV   ECX,SI_List                     ; Find first SI in the List
        mov   fs:[eax].SI_pNext_SI,ECX        ; New SI points to old  Head of the SI_LIST
        MOV   SI_List,EAX                     ; Add New SI at the head of the SI_LIST

        POP   ECX
        POPFD
        RET
EndProc ADD_SI

;***************************************************************************
;* FUNCTION:    Remove a Stream Instance (SI) from the SI_LIST
;*
;* INPUT:
;*              ECX  = Stream Handle of the (SI) to be removed
;*
;* EXIT-NORMAL:
;*              EAX  = Buffer Address for SI
;*
;* EXIT_ERROR:
;*              EAX = 0   (SI not Found)
;***************************************************************************
Procedure REMOVE_SI,NEAR
        ASSUME cs:_TEXT,ds:DATA,es:NOTHING,ss:NOTHING

        PUSHFD
        PUSH  ECX
        PUSH  EDX
        CLI                                   ; Turn Off Interrupt (Critical Section)

        MOV   EAX,SI_LIST                     ; Get First SI in the SI_LIST
        MOV   EDX,0                           ; 0 = Delete from List Head

REMOVE_SI_NEXT:
        CMP   EAX,0                           ; Is the List Empty
        JE   REMOVE_SI_Done                   ; Stream Handle - Not in List


        cmp   fs:[eax].SI_hStream,ecx         ; Is this to correct SI
        je    REMOVE_SI_HIL                   ; We Found the SI - Handle In List

        mov   edx,eax                         ; Remember Previous Element
        mov   eax,fs:[eax].SI_pNext_SI        ; Try next SI
        jmp   REMOVE_SI_Next                  ; Loop until all SI(s) are checked


REMOVE_SI_HIL:                                ; Handle found In List
        cmp   edx,0                           ; Is the Item at the List Head
        je    REMOVE_SI_HEAD                  ; Remove Element from the Head
        mov   ecx,fs:[eax].SI_pNext_SI        ; Next Item after Item being Deleted
        mov   fs:[edx].SI_pNext_SI,ecx        ; Previous points to new next
        Jmp   REMOVE_SI_Done                  ; All Done

REMOVE_SI_HEAD:
        mov   ecx,fs:[eax].SI_pNext_SI        ; Next Item after Item being Deleted
        mov   SI_LIST,ECX                     ; Next point to new next Item
        Jmp   REMOVE_SI_Done                  ; All Done


REMOVE_SI_DONE:
        POP   EDX
        POP   ECX
        POPFD
        RET
EndProc REMOVE_SI


_TEXT   ENDS
END
