       PAGE   ,132
       TITLE  Sound Blaster Object Module
       SUBTTL Main Program


if1
    ifdef small
         %OUT   Small Model
    else
         %OUT   Large Model
    endif
endif



ifdef    small
    _TEXT  SEGMENT  WORD PUBLIC 'CODE'
    _TEXT  ENDS
else
    CTV_TEXT  SEGMENT  WORD PUBLIC 'CODE'
    CTV_TEXT  ENDS
endif


_DATA  SEGMENT  WORD PUBLIC 'DATA'
_DATA  ENDS
CONST  SEGMENT  WORD PUBLIC 'CONST'
CONST  ENDS
_BSS   SEGMENT  WORD PUBLIC 'BSS'
_BSS   ENDS


DGROUP GROUP   CONST, _BSS, _DATA

ifdef  small
       ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP
else
       ASSUME  CS: CTV_TEXT, DS: DGROUP, SS: DGROUP
endif


       PUBLIC      _io_addx, _intr_num, _voice_status
       PUBLIC      _ctv_detect, _ctv_speaker, _ctv_output,
       PUBLIC      _ctv_halt
       PUBLIC      _ctv_pause, _ctv_continue, _ctv_uninstall
       PUBLIC      _ctv_card_here




_DATA  SEGMENT  WORD PUBLIC 'DATA'

ifdef  small
       ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP
else
       ASSUME  CS: CTV_TEXT, DS: DGROUP, SS: DGROUP
endif

_io_addx       DW   220H
_intr_num      DB   0
_voice_status  DW   0

ORG_INT_ADDX       LABEL     DWORD
ORG_INT2_ADDX      LABEL     DWORD
  ORG_INT2_OFF      DW       ?
  ORG_INT2_SEG      DW       ?
ORG_INT3_ADDX      LABEL     DWORD
  ORG_INT3_OFF      DW       ?
  ORG_INT3_SEG      DW       ?
ORG_INT5_ADDX      LABEL     DWORD
  ORG_INT5_OFF      DW       ?
  ORG_INT5_SEG      DW       ?
ORG_INT7_ADDX      LABEL     DWORD
  ORG_INT7_OFF      DW       ?
  ORG_INT7_SEG      DW       ?


;---------------------
;      DMA DATA      |
;---------------------
DMA_CURRENT_PAGE    DB       ?
DMA_CURRENT_ADDX    DW       ?
DMA_CURRENT_COUNT   DW       ?
PAGE_TO_DMA         DB       ?
LEN_L_TO_DMA        DW       ?
LEN_H_TO_DMA        DW       ?
LAST_DMA_OFFSET     DW       ?


_DATA  ENDS


ifdef  small
    _TEXT  SEGMENT     WORD PUBLIC 'CODE'
else
    CTV_TEXT  SEGMENT     WORD PUBLIC 'CODE'
endif

       SUBTTL    DSP module

WAIT_TIME        EQU    0200H
DMA_VOICE_IN     EQU    45H
DMA_VOICE_OUT    EQU    49H

DSP_ID_CMD              EQU    0E0H
DSP_VER_CMD             EQU    0E1H
DSP_VI8_CMD             EQU    24H
DSP_VO8_CMD             EQU    14H
DSP_VO2_CMD             EQU    17H
DSP_VO4_CMD             EQU    75H
DSP_VO25_CMD            EQU    77H
DSP_MDAC1_CMD           EQU    61H
DSP_MDAC2_CMD           EQU    62H
DSP_MDAC3_CMD           EQU    63H
DSP_MDAC4_CMD           EQU    64H
DSP_MDAC5_CMD           EQU    65H
DSP_MDAC6_CMD           EQU    66H
DSP_MDAC7_CMD           EQU    67H
DSP_TIME_CMD            EQU    40H
DSP_SILENCE_CMD         EQU    80H
DSP_PAUSE_DMA_CMD       EQU    0D0H
DSP_ONSPK_CMD           EQU    0D1H
DSP_OFFSPK_CMD          EQU    0D3H
DSP_CONT_DMA_CMD        EQU    0D4H
DSP_INTRQ_CMD           EQU    0F2H

CMS_TEST_CODE            EQU         0C6H
RESET_TEST_CODE          EQU         0AAH

CMS_EXIST                EQU         1
FM_MUSIC_EXIST           EQU         2
CTV_VOICE_EXIST          EQU         4

FM_WAIT_TIME             EQU         40H


WRITE_DSP_TIME   PROC

       PUSH   CX

       MOV    CX,WAIT_TIME
       MOV    AH,AL

WDT10:
       IN     AL,DX
       OR     AL,AL
       JNS    WDT20

       LOOP   WDT10

       STC
       JMP    SHORT WDT90

WDT20:
       MOV    AL,AH
       OUT    DX,AL
       CLC

WDT90:
       POP    CX
       RET

WRITE_DSP_TIME   ENDP



READ_DSP_TIME    PROC

       PUSH   CX
       PUSH   DX

       MOV    DX,_io_addx
       ADD    DL,0EH

       MOV    CX,WAIT_TIME

RDT10:
       IN     AL,DX
       OR     AL,AL
       JS     RDT20

       LOOP   RDT10
       STC
       JMP    SHORT RDT90

RDT20:
       SUB    DL,4
       IN     AL,DX
       CLC

RDT90:
       POP    DX
       POP    CX
       RET

READ_DSP_TIME    ENDP


WRITE_DSP   PROC

       MOV    AH,AL
       MOV    AL,0F0H

WD10:
       IN     AL,DX
       OR     AL,AL
       JS     WD10

       MOV    AL,AH
       OUT    DX,AL
       RET

WRITE_DSP   ENDP



READ_DSP    PROC

       PUSH   DX

       MOV    DX,_io_addx
       ADD    DL,0EH
       SUB    AL,AL

RD10:
       IN     AL,DX
       OR     AL,AL
       JNS    RD10

       SUB    DL,4
       IN     AL,DX

       POP    DX
       RET

READ_DSP    ENDP


RESET_DSP     PROC

       MOV    DX,_io_addx
       ADD    DL,6
       MOV    AL,1
       OUT    DX,AL

       IN     AL,DX
RDSP05:
       INC    AL
       JNZ    RDSP05

       OUT    DX,AL

       MOV    CL,20H

RDSP10:
       CALL   READ_DSP_TIME
       CMP    AL,0AAH
       JE     RDSP20

       DEC    CL
       JNZ    RDSP10
       MOV    AX,2
       JMP    SHORT RDSP90

RDSP20:
       SUB    AX,AX

RDSP90:
       OR     AX,AX
       RET

RESET_DSP     ENDP


VERIFY_IO_CHK      PROC

       MOV    BX,2

       MOV    AL,DSP_ID_CMD
       MOV    DX,_io_addx
       ADD    DX,0CH
       CALL   WRITE_DSP_TIME
       JC     VIO90

       MOV    AL,0AAH
       CALL   WRITE_DSP_TIME
       JC     VIO90

       CALL   READ_DSP_TIME
       JC     VIO90

       CMP    AL,055H
       JNE    VIO90

       SUB    BX,BX

VIO90:
       MOV    AX,BX
       OR     AX,AX
       RET

VERIFY_IO_CHK      ENDP


VERIFY_INTR        PROC

       MOV    AL,2
       MOV    DX,OFFSET DUMMY_DMA_INT2
       MOV    BX,OFFSET DGROUP:ORG_INT2_ADDX
       CALL   SETUP_INTERRUPT

       MOV    AL,3
       MOV    DX,OFFSET DUMMY_DMA_INT3
       MOV    BX,OFFSET DGROUP:ORG_INT3_ADDX
       CALL   SETUP_INTERRUPT

       MOV    AL,5
       MOV    DX,OFFSET DUMMY_DMA_INT5
       MOV    BX,OFFSET DGROUP:ORG_INT5_ADDX
       CALL   SETUP_INTERRUPT

       MOV    AL,7
       MOV    DX,OFFSET DUMMY_DMA_INT7
       MOV    BX,OFFSET DGROUP:ORG_INT7_ADDX
       CALL   SETUP_INTERRUPT

       MOV    _intr_num,0

       MOV    DX,_io_addx
       ADD    DX,0CH
       MOV    AL,DSP_INTRQ_CMD
       CALL   WRITE_DSP


       IN     AL,21H

       PUSH   AX
       AND    AL,01010011B
       OUT    21H,AL

       SUB    AX,AX
       MOV    CX,WAIT_TIME*4

VI10:
       CMP    _intr_num,0
       JNZ    VI90

       LOOP   VI10

       MOV    AX,3

VI90:
       POP    BX

       PUSH   AX

       MOV    AL,BL
       OUT    21H,AL

       MOV    AL,2
       MOV    BX,OFFSET DGROUP:ORG_INT2_ADDX
       CALL   RESTORE_INTERRUPT

       MOV    AL,3
       MOV    BX,OFFSET DGROUP:ORG_INT3_ADDX
       CALL   RESTORE_INTERRUPT

       MOV    AL,5
       MOV    BX,OFFSET DGROUP:ORG_INT5_ADDX
       CALL   RESTORE_INTERRUPT

       MOV    AL,7
       MOV    BX,OFFSET DGROUP:ORG_INT7_ADDX
       CALL   RESTORE_INTERRUPT

       POP    AX

       OR      AX,AX
       RET

VERIFY_INTR        ENDP


CHK_DSP_VERSION    PROC

       MOV    AL,DSP_VER_CMD
       MOV    DX,_io_addx
       ADD    DL,0CH
       CALL   WRITE_DSP
       CALL   READ_DSP
       MOV    AH,AL
       CALL   READ_DSP

       MOV    BX,1
       CMP    AX,101H
       JB     CDV90

       SUB    BX,BX

CDV90:
       MOV    AX,BX
       OR     AX,AX
       RET

CHK_DSP_VERSION    ENDP



PAUSE_DSP_DMA     PROC

       PUSHF

       MOV    AH,DSP_PAUSE_DMA_CMD
       MOV    BX,OFFSET DGROUP:_voice_status
       SUB    CX,CX
       MOV    DX,_io_addx
       ADD    DL,0CH

PDD10:
       STI                             ; enable intr. for voice-out intr
       CMP    CX,[BX]                  ;   to update voice_status
       JE     PDD90

       CLI                             ; disable intr. for following check
       IN     AL,DX                    ;   which is timing critical
       OR     AL,AL
       JNS    PDD10                    ; wait for DSP not ready

PDD20:
       IN     AL,DX
       OR     AL,AL
       JS     PDD20                    ; wait for DSP ready

       MOV    AL,AH
       OUT    DX,AL                    ; write pause dma immediately

PDD90:
       POPF
       RET

PAUSE_DSP_DMA     ENDP


       SUBTTL    DMA module


;--------------------------------------------
; entry: DH = dma mode                      :
;        DL = page                          :
;        AX = current addx                  :
;        CX = current count                 :
;--------------------------------------------

    DMA_ADDX_REG        EQU    02H
    DMA_COUNT_REG       EQU    03H
    DMA_MASK_REG        EQU    0AH
    DMA_MODE_REG        EQU    0BH
    DMA_FF_REG          EQU    0CH
    DMA_PAGE_REG        EQU    83H



PROG_DMA      PROC

       PUSH   BX

       MOV    BX,AX

       MOV    AL,5
       OUT    DMA_MASK_REG,AL

       SUB    AL,AL
       OUT    DMA_FF_REG,AL

       MOV    AL,DH
       OUT    DMA_MODE_REG,AL

       MOV    AL,BL
       OUT    DMA_ADDX_REG,AL

       MOV    AL,BH
       OUT    DMA_ADDX_REG,AL

       MOV    AL,CL
       OUT    DMA_COUNT_REG,AL

       MOV    AL,CH
       OUT    DMA_COUNT_REG,AL

       MOV    AL,DL
       OUT    DMA_PAGE_REG,AL

       MOV    AL,1
       OUT    DMA_MASK_REG,AL

       POP    BX
       RET

PROG_DMA      ENDP


CALC_20BIT_ADDX    PROC

       PUSH   CX

       MOV    CL,4
       ROL    DX,CL
       MOV    CX,DX
       AND    DX,0FH
       AND    CX,0FFF0H
       ADD    AX,CX
       ADC    DX,0

       POP    CX
       RET

CALC_20BIT_ADDX    ENDP


;-------------------------------------------------
; entry: AL = INTERRUPT NUM                      |
;        DX = new vector ofs, seg is alway CS    |
;        BX = offset of store buffer             :
;-------------------------------------------------
SETUP_INTERRUPT    PROC

       PUSHF
       PUSH   BX
       PUSH   CX
       PUSH   DX

       CLI

       MOV    CL,AL                    ; preserve interrupt number for use

       ADD    AL,8                     ; calculate interrupt vector addx
       CBW
       SHL    AL,1
       SHL    AL,1
       MOV    DI,AX

       PUSH   ES                       ; setup and preserve interrupt
       SUB    AX,AX
       MOV    ES,AX
       MOV    AX,ES:[DI]
       MOV    [BX],AX
       MOV    ES:[DI],DX

       MOV    AX,ES:[DI+2]
       MOV    [BX+2],AX
       MOV    ES:[DI+2],CS

       POP    ES

       POP    DX
       POP    CX
       POP    BX
       POPF
       RET

SETUP_INTERRUPT    ENDP


;-------------------------------------------------
; entry: AL = INTERRUPT NUM                      |
;        BX = offset to stored addx              |
;-------------------------------------------------
RESTORE_INTERRUPT  PROC

       PUSHF

       CLI

       MOV    CL,AL

       ADD    AL,8                      ; calculate interrupt vector addx
       CBW
       SHL    AL,1
       SHL    AL,1
       MOV    DI,AX

       PUSH   ES                       ; restore interrupt vector
       SUB    AX,AX
       MOV    ES,AX
       MOV    AX,[BX]
       MOV    ES:[DI],AX

       MOV    AX,[BX+2]
       MOV    ES:[DI+2],AX

       POP    ES

       MOV    AH,1
       SHL    AH,CL

       IN     AL,21H
       OR     AL,AH
       OUT    21H,AL

       POPF
       RET

RESTORE_INTERRUPT  ENDP


DUMMY_DMA_INT2      PROC   FAR

       PUSH   DX
       MOV    DL,2
       JMP    SHORT DUMMY_DMA_ISR

DUMMY_DMA_INT2      ENDP


DUMMY_DMA_INT3      PROC   FAR

       PUSH   DX
       MOV    DL,3
       JMP    SHORT DUMMY_DMA_ISR

DUMMY_DMA_INT3      ENDP


DUMMY_DMA_INT5      PROC   FAR

       PUSH   DX
       MOV    DL,5
       JMP    SHORT DUMMY_DMA_ISR

DUMMY_DMA_INT5      ENDP


DUMMY_DMA_INT7      PROC   FAR

       PUSH   DX
       MOV    DL,7

DUMMY_DMA_ISR:

       PUSH   DS
       PUSH   AX

       MOV    AX,DGROUP
       MOV    DS,AX

       MOV    _intr_num,DL

       MOV    DX,_io_addx
       ADD    DX,0EH
       IN     AL,DX

       MOV    AL,20H
       OUT    20H,AL

       POP    AX
       POP    DS
       POP    DX
       IRET

DUMMY_DMA_INT7      ENDP



DMA_OUT_INTR    PROC

       PUSH   DS
       PUSH   ES
       PUSH   AX
       PUSH   BX
       PUSH   CX
       PUSH   DX
       PUSH   DI
       PUSH   SI
       PUSH   BP

       CLD
       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       MOV    DX,_io_addx
       ADD    DL,0EH
       IN     AL,DX

       MOV    AX,LEN_L_TO_DMA
       OR     AX,AX
       JNZ    VO_INT10

       CALL   END_DMA_TRANSFER

       JMP    SHORT VO_INT90

VO_INT10:
       CALL   DMA_OUT_TRANSFER

VO_INT90:
       MOV    AL,20H
       OUT    20H,AL

       POP    BP
       POP    SI
       POP    DI
       POP    DX
       POP    CX
       POP    BX
       POP    AX
       POP    ES
       POP    DS
       IRET

DMA_OUT_INTR    ENDP



DMA_OUT_TRANSFER     PROC

       MOV    CX,0FFFFH                ; get current page end address

       CMP    PAGE_TO_DMA,0            ; last page to dma ?
       JNZ    DOT10                    ; no, skip

       INC    PAGE_TO_DMA
       MOV    CX,LAST_DMA_OFFSET       ; get end addx

DOT10:
       SUB    CX,DMA_CURRENT_ADDX      ; calcutate current page addx
       MOV    DMA_CURRENT_COUNT,CX
       INC    CX
       JZ     DOT20

       SUB    LEN_L_TO_DMA,CX
       SBB    LEN_H_TO_DMA,0
       JMP    SHORT DOT30

DOT20:                  
       DEC    LEN_H_TO_DMA

DOT30:
       MOV    DH,DMA_VOICE_OUT
       MOV    DL,DMA_CURRENT_PAGE
       MOV    AX,DMA_CURRENT_ADDX
       MOV    CX,DMA_CURRENT_COUNT
       CALL   PROG_DMA

       DEC    PAGE_TO_DMA
       INC    DMA_CURRENT_PAGE
       MOV    DMA_CURRENT_ADDX,0

       MOV    CX,DMA_CURRENT_COUNT

       MOV    DX,_io_addx
       ADD    DL,0CH

       MOV    AL,DSP_VO8_CMD
       CALL   WRITE_DSP

       MOV    AL,CL
       CALL   WRITE_DSP

       MOV    AL,CH
       CALL   WRITE_DSP

DOT90:
       RET

DMA_OUT_TRANSFER     ENDP



END_DMA_TRANSFER     PROC

       MOV    AL,5
       OUT    DMA_MASK_REG,AL

       MOV    CL,_intr_num
       MOV    AH,1
       SHL    AH,CL
       IN     AL,21H
       OR     AL,AH
       OUT    21H,AL

       MOV    AL,_intr_num
       MOV    BX,OFFSET DGROUP:ORG_INT_ADDX
       CALL   RESTORE_INTERRUPT

       MOV    _voice_status,0

       MOV    DX,_io_addx
       ADD    DL,0EH
       IN     AL,DX

       RET

END_DMA_TRANSFER     ENDP


;---------------------------------------
;  WAIT_FM_STATUS                      :
;   entry:    AL = status to wait      :
;                  3 msb is concern    :
;   exit :    AX destroy               :
;             carry set for fail       :
;             carry clr for pass       :
;---------------------------------------
WAIT_FM_STATUS     PROC

       PUSH   CX
       PUSH   DX

       MOV    CX,FM_WAIT_TIME

       MOV    AH,AL
       AND    AH,0E0H                  ; only 3 msb are FM status
       MOV    DX,_io_addx
       ADD    DL,8

WFS10:
       IN     AL,DX
       AND    AL,0E0H
       CMP    AH,AL
       JE     WFS20

       LOOP   WFS10

       STC
       JMP    SHORT WFS90

WFS20:
       CLC

WFS90:
       POP    DX
       POP    CX
       RET

WAIT_FM_STATUS     ENDP


;---------------------------------------
;  WRITE_FM                            :
;   entry:    AH = data value          :
;             AL = addx value          :
;   exit :    AX destroy               :
;             DX destroy               :
;---------------------------------------
WRITE_FM         PROC

       MOV    DX,_io_addx
       ADD    DL,8
       OUT    DX,AL
       CALL   FM_DELAY
       MOV    AL,AH
       INC    DX
       OUT    DX,AL
       CALL   FM_DELAY
       RET

WRITE_FM         ENDP


FM_DELAY         PROC

       PUSH   AX
       PUSH   DX

       MOV    DX,_io_addx
       ADD    DL,8

       IN     AL,DX
       IN     AL,DX
       IN     AL,DX
       IN     AL,DX
       IN     AL,DX

       POP    DX
       POP    AX

       RET

FM_DELAY         ENDP





ifdef  small
    _ctv_detect      PROC
else
    _ctv_detect      PROC    FAR
endif
       PUSH   DS
       PUSH   ES
       PUSH   DI
       PUSH   SI

       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       CALL   RESET_DSP
       JNZ    ID90

       CALL   VERIFY_IO_CHK
       JNZ    ID90

       CALL   CHK_DSP_VERSION
       JNZ    ID90

       CALL   VERIFY_INTR
       JNZ    ID90

       MOV    AL,1                     ; on speaker
       CALL   ON_OFF_SPEAKER

       SUB    AX,AX

ID90:
       POP    SI
       POP    DI
       POP    ES
       POP    DS
       RET

_ctv_detect      ENDP


SPK_STK          STRUC

ifdef    small
                   DW      ?
else
                   DD      ?
endif

                   DW      ?
    SPK_FLAG       DW      ?

SPK_STK          ENDS


ifdef  small
    _ctv_speaker     PROC
else
    _ctv_speaker     PROC    FAR
endif

       PUSH   BP
       MOV    BP,SP

       PUSH   DS

       MOV    AX,DGROUP
       MOV    DS,AX

       MOV    AX,[BP+SPK_FLAG]
       CALL   ON_OFF_SPEAKER


       POP    DS

       POP    BP
       RET

_ctv_speaker  ENDP


ON_OFF_SPEAKER     PROC


       MOV    DX,_io_addx
       ADD    DX,0CH

       MOV    AH,DSP_ONSPK_CMD
       OR     AL,AL
       JNZ    OOS10

       MOV    AH,DSP_OFFSPK_CMD

OOS10:
       MOV    AL,AH
       CALL   WRITE_DSP

       SUB    AX,AX                    ; inidcate no error
       RET

ON_OFF_SPEAKER     ENDP



OUT_STK       STRUC

ifdef  small
           DW      ?                   ; one word ret addx
else                                                      
           DD      ?                   ; double word ret addx
endif
                   DW   ?
    BUF_OFF        DW   ?
    BUF_SEG        DW   ?
    COUNT          DW   ?
    SAMPLING       DW   ?

OUT_STK       ENDS


ifdef  small
    _ctv_output      PROC
else
    _ctv_output      PROC    FAR
endif

       PUSH   BP
       MOV    BP,SP

       PUSH   DS
       PUSH   ES
       PUSH   DI
       PUSH   SI

       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       CMP    _voice_status,0
       JZ     OV10

       MOV    AX,1
       JMP    OV90

OV10:
       MOV    _voice_status,1

       MOV    DX,_io_addx
       ADD    DL,0CH

       MOV    DX,0FH                   ; calculate sampling rate value for
       MOV    AX,4240H                 ; DSP
       MOV    CX,[BP+SAMPLING]
       DIV    CX

       MOV    CL,AL
       NEG    CL

       MOV    DX,_io_addx
       ADD    DL,0CH
       MOV    AL,DSP_TIME_CMD
       CALL   WRITE_DSP

       MOV    AL,CL
       CALL   WRITE_DSP

       MOV    AL,_intr_num
       MOV    DX,OFFSET DMA_OUT_INTR
       MOV    BX,OFFSET DGROUP:ORG_INT_ADDX
       CALL   SETUP_INTERRUPT

       MOV    CL,_intr_num
       MOV    AH,1
       SHL    AH,CL
       NOT    AH
       IN     AL,21H
       AND    AL,AH
       OUT    21H,AL

       MOV    DX,[BP+BUF_SEG]
       MOV    AX,[BP+BUF_OFF]
       CALL   CALC_20BIT_ADDX

       MOV    DMA_CURRENT_PAGE,DL
       MOV    DMA_CURRENT_ADDX,AX

       MOV    CX,[BP+COUNT]
       MOV    LEN_L_TO_DMA,CX
       MOV    LEN_H_TO_DMA,0

       ADD    AX,[BP+COUNT]
       ADC    DL,0
       SUB    AX,1
       SBB    DL,0

       MOV    LAST_DMA_OFFSET,AX
       SUB    DL,DMA_CURRENT_PAGE
       MOV    PAGE_TO_DMA,DL

       CALL   DMA_OUT_TRANSFER

       SUB    AX,AX

OV90:
       POP    SI
       POP    DI
       POP    ES
       POP    DS

       POP    BP
       RET

_ctv_output      ENDP


ifdef  small
    _ctv_halt     PROC
else
    _ctv_halt     PROC     FAR
endif

       PUSH   DS
       PUSH   ES
       PUSH   DI
       PUSH   SI

       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       MOV    AX,1

       CMP    _voice_status,0
       JZ     TVP90

       CALL   PAUSE_DSP_DMA
       CALL   END_DMA_TRANSFER

       SUB    AX,AX

TVP90:
       POP    SI
       POP    DI
       POP    ES
       POP    DS
       RET

_ctv_halt     ENDP


ifdef  small
    _ctv_pause    PROC
else
    _ctv_pause    PROC     FAR
endif

       PUSH   DS
       PUSH   ES
       PUSH   DI
       PUSH   SI

       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       MOV    AX,1

       CMP    _voice_status,1
       JNE    PV90

       CALL   PAUSE_DSP_DMA
       SUB    AX,AX

PV90:
       POP    SI
       POP    DI
       POP    ES
       POP    DS
       RET

_ctv_pause    ENDP



ifdef  small
    _ctv_continue PROC
else
    _ctv_continue PROC  FAR
endif

       PUSH   DS
       PUSH   ES
       PUSH   DI
       PUSH   SI

       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       MOV    AX,1

       CMP    _voice_status,1
       JNE    CV90

       MOV    DX,_io_addx
       ADD    DL,0CH

       MOV    AL,DSP_CONT_DMA_CMD
       CALL   WRITE_DSP
       SUB    AX,AX

CV90:
       POP    SI
       POP    DI
       POP    ES
       POP    DS
       RET

_ctv_continue ENDP


ifdef  small
    _ctv_uninstall     PROC
else
    _ctv_uninstall     PROC  FAR
endif

       PUSH   DS
       PUSH   ES
       PUSH   DI
       PUSH   SI

       MOV    AX,DGROUP
       MOV    DS,AX
       MOV    ES,AX

       CMP    _voice_status,0
       JZ     UI90

       CALL   PAUSE_DSP_DMA
       CALL   END_DMA_TRANSFER


UI90:
       SUB    AL,AL
       CALL   ON_OFF_SPEAKER

       SUB    AX,AX

       POP    SI
       POP    DI
       POP    ES
       POP    DS
       RET

_ctv_uninstall     ENDP


    ;-----------------------------------------------------------
    ; usage:                                                   :
    ;        unint ctv_card_here()                              :
    ; description:                                             :
    ;    detect the Game Blaster or Sound Blaster Card and     :
    ;    the configuration                                     :
    ; entry:                                                   :
    ;   the global i/o addx, ct_io_addx must set to a default  :
    ;   value, 2x0H, before call.                              :
    ; exit:                                                    :
    ;   High Byte = 0                                          :
    ;   Low Byte format -                                      :
    ;      0000 0001  :   C/MS music exists                    :
    ;      0000 0010  :   FM music exists                      :
    ;      0000 0100  :   CTV Voice exists                     :
    ;-----------------------------------------------------------


ifdef small
    _ctv_card_here    PROC     near
else
    _ctv_card_here    PROC     far
endif


       PUSH   DS                       ; for multi data model, set DS to
       MOV    AX,DGROUP                ;  DGROUP segment
       MOV    DS,AX

       SUB    BX,BX                    ; assume Creative Card doesn't exist, 
                                       ;   return 0
    ;-----------------------------
    ;    detect Game Blaster     :
    ;-----------------------------

       MOV    DX,_io_addx           ; get default i/o addx
       ADD    DL,6                     ; write the test code to
       MOV    AL,CMS_TEST_CODE         ;   output port 2x6H
       OUT    DX,AL                    ; ...
       SUB    AL,AL
       ADD    DL,4                     ; read the data back from
       OUT    DX,AL
       IN     AL,DX                    ;   input port 2xAH
       CMP    AL,CMS_TEST_CODE         ; the same data ?
       JNE    CARD10                   ; no, try Sound Blaster Card

       SUB    DL,4                     ; to ensure, try inverse data
       MOV    AL,NOT CMS_TEST_CODE     ; output port 2x6H
       OUT    DX,AL                    ; ...
       SUB    AL,AL
       ADD    DL,4                     ; read the data back from
       OUT    DX,AL
       IN     AL,DX                    ;   input port 2xAH
       CMP    AL,NOT CMS_TEST_CODE     ; the same data ?
       JNE    CARD10                   ; no, try Sound Blaster Card

       MOV    BX,CMS_EXIST
       JMP    SHORT CARD50

    ;--------------------------------------
    ;    detect the Sound Blaster Card    :
    ;--------------------------------------
CARD10:
       CALL   RESET_DSP
       JNZ    CARD50

CARD40:
       MOV    DX,_io_addx
       ADD    DL,0CH

       MOV    AL,DSP_ID_CMD
       CALL   WRITE_DSP_TIME
       JC     CARD50

       MOV    AL,CMS_TEST_CODE
       CALL   WRITE_DSP_TIME
       JC     CARD50

       CALL   READ_DSP_TIME
       JC     CARD50

       CMP    AL,NOT CMS_TEST_CODE
       JNE    CARD50

       MOV    BX,CMS_EXIST + CTV_VOICE_EXIST


    ;-----------------------------
    ;    detect the FM Music     :
    ;-----------------------------
CARD50:
       MOV    AX,0001H                 ; reset the FM chip
       CALL   WRITE_FM

       MOV    AX,6004H                 ; mask of timer 1 & 2
       CALL   WRITE_FM

       MOV    AX,8004H                 ; reset irq and both timer flags
       CALL   WRITE_FM

       MOV    AL,0                     ; read for 3 msb clear
       CALL   WAIT_FM_STATUS
       JC     CARD90

       MOV    AX,0FF02H                ; write timer 1 count
       CALL   WRITE_FM

       MOV    AX,2104H                 ; start timer 1
       CALL   WRITE_FM

       MOV    AL,0C0H                  ; wait for irq and timer 1 end
       CALL   WAIT_FM_STATUS
       JC     CARD90

       MOV    AX,6004H                 ; mask of timer 1 & 2
       CALL   WRITE_FM

       MOV    AX,8004H                 ; reset irq and both timer flags
       CALL   WRITE_FM

       ADD    BX,FM_MUSIC_EXIST

CARD90:
       MOV    AX,BX

       POP    DS
       RET

_ctv_card_here    ENDP

ifdef  small

    _TEXT  ENDS
else
    CTV_TEXT     ENDS
endif
       END




