2000 ; 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 PUBLIC reset_dsp PUBLIC val_init DSEG SEGMENT PUBLIC '_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 ? DSEG ENDS CSEG SEGMENT PUBLIC '_CODE' ASSUME CS:CSEG,DS:DSEG 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 dseg:org_int2_addx call setup_interrupt mov al,3 mov dx,OFFSET dummy_dma_int3 mov bx,OFFSET dseg:org_int3_addx call setup_interrupt mov al,5 mov dx,OFFSET dummy_dma_int5 mov bx,OFFSET dseg:org_int5_addx call setup_interrupt mov al,7 mov dx,OFFSET dummy_dma_int7 mov bx,OFFSET dseg: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 sub ax,ax mov cx,wait_time*4 vi10: cmp intr_num,0 jnz vi90 loop vi10 mov ax,3 vi90: push ax mov al,2 mov bx,OFFSET dseg:org_int2_addx call restore_interrupt mov al,3 mov bx,OFFSET dseg:org_int3_addx call restore_interrupt mov al,5 mov bx,OFFSET dseg:org_int5_addx call restore_interrupt mov al,7 mov bx,OFFSET dseg: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 dseg:voice_status sub cx,cx mov dx,io_addx add dl,0ch pdd10: sti cmp cx,[bx] je pdd90 cli in al,dx or al,al jns pdd10 pdd20: in al,dx or al,al js pdd20 mov al,ah out dx,al 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 2000 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 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 mov ah,1 ; enable interrupt control mask-bit shl ah,cl not ah in al,21h and al,ah out 21h,al sti pop dx pop cx pop bx ret setup_interrupt ENDP ;------------------------------------------------- ; entry: AL = INTERRUPT NUM | ; BX = offset to stored addx | ;------------------------------------------------- restore_interrupt PROC 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 sti 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,dseg 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 FAR push ds push es push ax push bx push cx push dx push di push si push bp cld mov ax,dseg 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 al,intr_num mov bx,OFFSET dseg: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 NEAR push ds push es push di push si mov ax,dseg 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 dw ? dw ? spk_flag dw ? spk_stk ENDS ctv_speaker PROC NEAR push bp mov bp,sp push ds mov ax,dseg mov ds,ax mov ax,[bp+spk_flag] call on_off_speaker pop ds pop bp ret ctv_speaker ENDP on_off_speaker PROC NEAR mov dx,io_addx add dx,0ch mov ah,dsp_onspk_cmd or al,al jnz oos10 mov ah,dsp_offspk_cmd oos10: mov 2000 al,ah call write_dsp sub ax,ax ; inidcate no error ret on_off_speaker ENDP out_stk STRUC dw ? ; one word ret addx dw ? sampling dw ? count dw ? buf_off dw ? buf_seg dw ? out_stk ENDS ctv_output PROC NEAR push bp mov bp,sp push ds push es push di push si mov ax,dseg mov ds,ax mov es,ax cmp voice_status,0 jz ov10 mov ax,1 jmp short 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 dseg:org_int_addx call setup_interrupt 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 ctv_halt PROC NEAR push ds push es push di push si mov ax,dseg 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 NEAR push ds push es push di push si mov ax,dseg 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 NEAR push ds push es push di push si mov ax,dseg 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 NEAR push ds push es push di push si mov ax,dseg 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 near push ds ; for multi data model, set DS to mov ax,dseg ; dseg 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 ;----------------------------------------------------------------- initval_stk STRUC dw 2d1 ? ;Return adress on stack dw ? voice_val dw ? intr_val dw ? port_val dw ? initval_stk ENDS val_init PROC NEAR ;Emergency procedure ;Can't find out how to declare ;"global" variables in TP push bp mov bp,sp push ds mov ax,dseg mov ds,ax mov dx,[bp+voice_val] mov voice_status,dx mov dx,[bp+intr_val] mov intr_num,dl mov dx,[bp+port_val] mov io_addx,dx pop ds pop bp ret val_init ENDP ;------------------------------------------------------------------ CSEG ENDS END  . 0