Please consider a donation to the Higher Intellect project. See https://preterhuman.net/donate.php or the Donate to Higher Intellect page for more info.

CTV-MOD.ASM

From Higher Intellect Vintage Wiki
Jump to navigation Jump to search
       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


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


	DOSSEG
	.MODEL SMALL


	.DATA


_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       ?


	.CODE


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


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


_ctv_detect      PROC

       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



_ctv_speaker     PROC

       push   bp
       mov    bp,sp

       push   ds

       mov    ax,DGROUP
       mov    ds,ax

       mov    ax,[bp+4]
       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



_ctv_output  PROC

       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+10]
       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+6]
       mov    ax,[bp+4]
       call   calc_20bit_addx

       mov    dma_current_page,dl
       mov    dma_current_addx,ax

       mov    cx,[bp+8]
       mov    len_l_to_dma,cx
       mov    len_h_to_dma,0

       add    ax,[bp+8]
       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


_ctv_halt     PROC

       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


_ctv_pause    PROC

       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



_ctv_continue PROC

       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


_ctv_uninstall     PROC

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


_ctv_card_here    PROC

       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


       END