;-------------------------------- ; program dma ;-------------------------------- sb_set_dma: mov ebx,[sound_dma] lea eax,[ebx+4] ;mask required channel cmp bl,4 ja .use_second_dma_controller jb @f .dma_setup_error: if DEBUG mov esi,msgErrDMAsetup call SysMsgBoardStr end if mov dword[esp],START.stop ret @@: if use_cli_sti cli ;here to minimize time with disabled ints end if out 0xA,al ;mask required channel xor eax,eax out 0xC,al ;clear byte pointer flip-flop register lea eax,[ebx+0x58] ;auto-init mode for channel (ebx) out 0xB,al ;DMA channel 0-3 mode register movzx edx,byte[ebx+dma_table] ;page register mov al,DMAPage out dx,al lea edx,[ebx*2] ;DMA channel 0-3 base address mov al,0 ;LSB is 0 out dx,al ; mov al,0 ;MSB is 0 too out dx,al inc edx ;DMA channel 0-3 byte count mov al,((sb_buffer_size-1) and 0xff) out dx,al mov al,((sb_buffer_size-1) shr 8) ;it is the same out dx,al mov eax,ebx ;unmask DMA channel out 0xA,al if use_cli_sti sti end if ret .use_second_dma_controller: cmp bl,7 ja .dma_setup_error sub bl,4 sub al,4 if use_cli_sti cli ;here to minimize time with disabled ints end if out 0xD4,al ;mask required channel xor eax,eax out 0xD8,al ;clear byte pointer flip-flop register lea eax,[ebx+0x58] ;auto-init mode for channel (ebx+4) out 0xD6,al ;DMA channel 4-7 mode register movzx edx,byte[ebx+dma_table+4] ;page register mov al,DMAPage out dx,al lea edx,[ebx*4+0xC0] ;DMA channel 4-7 base address mov al,0 ;LSB is 0 ;for 16bit DMA this contains out dx,al ;A1-A8 lines of address bus, A0 is zero ; mov al,0 ;MSB is 0 too ;for 16bit DMA this contains out dx,al ;A9-A16 lines of address bus inc edx inc edx ;DMA channel 4-7 16bit word count mov al,(((sb_buffer_size/2)-1) and 0xff) out dx,al mov al,(((sb_buffer_size/2)-1) shr 8) out dx,al mov eax,ebx ;unmask DMA channel out 0xD4,al if use_cli_sti sti end if ret ;------------------------------------------------------------------------------- ; out byte to SB DSP's write port ;------------------------------------------------------------------------------- macro sb_out data_to_out { @@: in al,dx test al,al ;is DSP busy? js @b ;it's busy mov al,data_to_out ;it's free out dx,al } ;------------------------------------------------------------------------------- ; stop playing ;------------------------------------------------------------------------------- proc sb_stop mov edx,[sb_base_port] add dl,0xC sb_out 0xD3 ;turn the speaker off sb_out 0xDA ;exit 8bit DMA sb_out 0xD9 ;exit 16bit DMA ret endp ;------------------------------------------------------------------------------- ; start playing ;------------------------------------------------------------------------------- proc sb_play and [int_flip_flop],0 mov edx,[sb_base_port] add dl,0xC sb_out 0xD1 ;turn speaker on ; sb_out 0x48 ;set DSP transfer size ;for older cards, not supported ; ;in this version ; mov ax,32767 ;(64k)/2-1 ;@@: ;out the low byte... ; in al,dx ; test al,al ;is DSP busy? ; js @b ;it's busy ; out dx,al ; mov al,ah ;...then the high byte ;@@: ; in al,dx ; test al,al ;is DSP busy? ; js @b ;it's busy ; out dx,al ; sb_out 0x1C ;auto-init 8bit playback ; 0xBXh - 16 bit DMA mode ; |||| sb_out 10110110b ;bCommand ; |||| ; |||+-reserved ; ||+--turn FIFO on (0 for off) ; |+---auto-init mode on (0 for off) ; +----A/D: 0-output, 1-input ; +------stereo on ; |+-----unsigned (1 for signed) ; || sb_out 00110000b ;bMode ; || |||| ; ---------reserved ;wSize is a number of 16bit samples less 1. For auto-init mode each half ;buffer is (64k)/2 bytes long and, obviously, contains ((64k)/2)/2 samples sb_out (((sb_buffer_size/2/2)-1) and 0xFF) ;wSize.LowByte sb_out (((sb_buffer_size/2/2)-1) shr 8) ;wSize.HighByte ret endp ;------------------------------------------------------------------------------- ; reset DSP ;------------------------------------------------------------------------------- proc sb_reset and [int_flip_flop],0 mov edx,[sb_base_port] add dl,6 mov al,1 ;start DSP reset if use_cli_sti cli ;here to minimize time with disabled ints end if out dx,al mov ecx,40 ;wait at least 3 microsec. @@: in al,dx loop @b xor eax,eax ;stop DSP reset if use_cli_sti sti end if out dx,al ret endp ;------------------------------------------------------------------------------- ; set the rate for playing, enable stereo ;------------------------------------------------------------------------------- proc sb_setup mov edx,[sb_base_port] add dl,0xC sb_out 40h ;set time constant, this is for old cards sb_out sb_tc sb_out 41h ;set sound rate, this can only SB16 sb_out (sb_out_rate shr 8) ;first high byte (MSB) sb_out (sb_out_rate and 0xff) ;then low byte (LSB) ; mov al,0xE ;for older cards, not supported in this version ; sub dl,(0xC-4) ;talk to SB's mixer ; out dx,al ;select this register of the mixer ; mov ecx,6 ;wait for the chip ;@@: ; in al,dx ; loop @b ; inc edx ;now read the data port ; in al,dx ; or al,22h ;turn on stereo ; mov ah,al ; mov al,0xE ; dec edx ;talk to SB's mixer ; out dx,al ;select this register of the mixer ; mov ecx,6 ;wait for the chip ;@@: ; in al,dx ; loop @b ; inc edx ;now send data to the data port ; mov al,ah ; out dx,al ; dec edx ; mov ecx,35 ;wait for the chip ;@@: ; in al,dx ; loop @b ret endp ;------------------------------------------------------------------------------- ; set master volume of SB mixer, note, not only SB16 but SBPro and older ; this is the first step to more full support for hardware ;------------------------------------------------------------------------------- ;in: eax in range [-10000;0] - master volume for _both_ channels ;note that x*3*17/2000 and x*3/2000*17 are not the same numbers, ;because we count in integers proc sb_set_master_vol mov [sb_master_vol],eax add eax,10000 ;SB sound level rise from 0 to MAX_LEVEL lea eax,[eax+eax*2] ;*3 mov ebx,2000 ;divisor xor edx,edx cmp byte[sb_DSP_version_int],4 jae @f ;SBPro's MAX_LEVEL is 15, but we *11 because ;volume byte looks like that: 0xLR, where L - left ;channel volume, R - right, 0<=R,L<=15 div ebx imul eax,17 mov edx,[sb_base_port] push eax ;here for optimisation add dl,4 mov al,0x22 ;write mixer register 0x22 out dx,al in al,dx ;wait for the chip ;6 in al,dx ;wait for the chip ;5 in al,dx ;wait for the chip ;4 in al,dx ;wait for the chip ;3 in al,dx ;wait for the chip ;2 in al,dx ;wait for the chip ;1 pop eax ;go! inc edx out dx,al ret @@: ;SB16's MAX_LEVEL is 255 imul eax,17 div ebx mov edx,[sb_base_port] push eax ;here for optimisation add dl,4 mov al,0x30 ;left speaker out dx,al pop eax ;<--+ inc edx ; \/ push eax ;here for optimisation out dx,al ;write dec edx mov al,0x31 ;right speaker out dx,al pop eax inc edx out dx,al ;write ret endp ;-------------------------------------------------------------------------------