PAGE 60,132 TITLE Sound Blaster (Killer Card) 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 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 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 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 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,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 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 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 DGROUP: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 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