;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

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