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 EXTRN _io_addx : WORD EXTRN _intr_num : BYTE EXTRN _voice_status : WORD EXTRN org_int_addx : DWORD EXTRN _dma : WORD 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 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_PAGE_REG DW 87h,83h,81h,82h ;--------------------- ; DMA DATA | ;--------------------- DMA_CURRENT_PAGE DB ? DMA_CURRENT_ADDX DW ? DMA_CURRENT_COUNT DW ? _DATA ENDS ifdef small _TEXT SEGMENT WORD PUBLIC 'CODE' else CTV_TEXT SEGMENT WORD PUBLIC 'CODE' endif SUBTTL DSP module WAIT_TIME EQU 16383; 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,BYTE PTR _dma OR AL,4 ; MOV AL,5 ; Disable DMA channel 1 OUT DMA_MASK_REG,AL SUB AL,AL ; Clear LSB/MSB Flip-Flop OUT DMA_FF_REG,AL MOV DH,BYTE PTR _dma OR DH,48h MOV AL,DH ; DH = DMA_VOICE_OUT = 49h OUT DMA_MODE_REG,AL ; single mode, address increment, ; read, channel 1 MOV AL,BL ; Write LSB of address OUT DMA_ADDX_REG,AL MOV AL,BH ; Write MSB of address OUT DMA_ADDX_REG,AL MOV AL,CL ; Write LSB of count OUT DMA_COUNT_REG,AL MOV AL,CH ; Write MSB of count OUT DMA_COUNT_REG,AL MOV AL,DL ; Write page number PUSH DX PUSH BX MOV BX,WORD PTR _dma ADD BX,BX MOV DX,WORD PTR DMA_PAGE_REG[BX] OUT DX,AL ; OUT DMA_PAGE_REG,AL POP BX POP DX MOV AL,BYTE PTR _dma ; MOV AL,1 ; Enable channel 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 always 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 PUSHF CLI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI PUSH BP CLD MOV AX,DGROUP ; Get the data segment MOV DS,AX MOV ES,AX MOV DX,_io_addx ; Read from 2xEh ADD DL,0EH IN AL,DX ; CALL END_DMA_TRANSFER ; JMP SHORT VO_INT90 CALL DMA_OUT_TRANSFER ; Re-send the block ;VO_INT90: MOV AL,20H ; Signal EOI (End-of-interrupt) OUT 20H,AL POP BP POP SI POP DI POP DX POP CX POP BX POP AX POP ES POP DS POPF IRET DMA_OUT_INTR ENDP DMA_OUT_TRANSFER PROC MOV DH,DMA_VOICE_OUT MOV DL,DMA_CURRENT_PAGE MOV AX,DMA_CURRENT_ADDX MOV CX,DMA_CURRENT_COUNT CALL PROG_DMA ; Doesn't change CX ; MOV CX,DMA_CURRENT_COUNT MOV DX,_io_addx ; 2xCh ADD DL,0CH MOV AL,DSP_VO8_CMD ; 14h CALL WRITE_DSP MOV AL,CL ; LSB of length CALL WRITE_DSP MOV AL,CH ; MSB of length CALL WRITE_DSP 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 2 ; WAS "RET" for C; PASCAL needs "RET 2" _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 ? SAMPLING DW ? COUNT DW ? BUF_OFF DW ? BUF_SEG 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 AX,[BP+COUNT] SUB AX,1 MOV DMA_CURRENT_COUNT,AX CALL DMA_OUT_TRANSFER SUB AX,AX OV90: POP SI POP DI POP ES POP DS POP BP RET 8 ; WAS "RET" for C; PASCAL needs "RET 8" _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