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

format MS COFF

include 'CONFIG.INC'

;structs----------------------------------------------------------
struc IOCTL
{  .handle	dd ?
   .io_code	dd ?
   .input	dd ?
   .inp_size	dd ?
   .output	dd ?
   .out_size	dd ?
}

virtual at 0
  IOCTL IOCTL
end virtual

;something--------------------------------------------------------
public START
public service_proc
public version

include '..\proc32.inc'
include '..\imports.inc'

section '.flat' code readable align 16

include 'SB16.INC'

;-------------------------------------------------------------------------------
proc START stdcall, state:dword
	   cmp	[state], 1
	   jne	.stop
.entry:

if DEBUG
	   mov	esi, msgInit
	   call SysMsgBoardStr
end if

	   call detect		 ;returns DSP version or zero if
	   test eax,eax 	 ;SB card not found
	   jz	.exit

if DEBUG
	   movzx eax,al 	 ;major version
	   mov esi,sb_DSP_description
	   dec eax
	   jz  .sb_say_about_found_dsp
	   mov dword[esi],'2.x '
	   dec eax
	   jz  .sb_say_about_found_dsp
	   mov dword[esi],'Pro '
	   dec eax
	   jz  .sb_say_about_found_dsp
	   mov dword[esi],'16  '
.sb_say_about_found_dsp:
	   mov esi,msgDSPFound
	   call SysMsgBoardStr
end if
;	   xor	eax,eax
;	   mov	ebx,[sb_base_port]
;	   lea	ecx,[ebx+0xF]
	   xor	ebx,ebx
	   mov	ecx,[sb_base_port]
	   lea	edx,[ebx+0xF]

	   call ReservePortArea  ;these ports must be my!
if DEBUG
	   dec	eax
	   jnz	@f
	   mov	esi,msgErrRsrvPorts
	   call SysMsgBoardStr
@@:
end if

	   call sb_setup	 ;clock it, etc

	   stdcall AttachIntHandler, sb_irq_num, sb_irq, 0

if DEBUG
	   test eax,eax
	   jnz	@f

	   mov	esi,msgErrAtchIRQ
	   call SysMsgBoardStr

	   stdcall GetIntHandler, sb_irq_num
	   call SysMsgBoardNum

	   jmp	.stop
@@:
	   mov	esi,msgSucAtchIRQ
	   call SysMsgBoardStr
end if
	   stdcall RegService, my_service, service_proc
	   ret
.stop:
	   call sb_reset
.exit:

if DEBUG
	   mov esi,msgExit
	   call SysMsgBoardStr
end if

	   xor	eax, eax
	   ret
endp
;-------------------------------------------------------------------------------

handle	   equ	IOCTL.handle
io_code    equ	IOCTL.io_code
input	   equ	IOCTL.input
inp_size   equ	IOCTL.inp_size
output	   equ	IOCTL.output
out_size   equ	IOCTL.out_size

align 4
proc service_proc stdcall, ioctl:dword
	   mov	edi,[ioctl]
	   mov	eax,[edi+io_code]
	   cmp	eax,SRV_GETVERSION
	   jne	@F

	   mov	eax,[edi+output]
	   cmp	[edi+out_size],4
	   jne	.fail
	   mov	[eax],dword API_VERSION
	   xor	eax,eax
	   ret
@@:
	   cmp	eax,DEV_PLAY
	   jne	@f
if DEBUG
	   mov	esi,msgPlay
	   call SysMsgBoardStr
end if
	   call sb_stop 	 ;to play smth new we must stop smth old

	   call pre_fill_data	 ;fill first and second half of the buffer
	   call pre_fill_data	 ;

	   call sb_set_dma	 ;is it really needed here? Paranoia.
	   call sb_play
	   xor	eax,eax 	  ;set maximum volume
	   call sb_set_master_vol
	   xor	eax,eax
	   ret
;@@:                             ;all this commented stuff in service proc
;           cmp  eax,DEV_STOP    ;is never used. Mixer do this virtually,
;           jne  @f              ;e.g. instead of stopping driver it
;if DEBUG                        ;outputs silence
;           mov  esi,msgStop
;           call SysMsgBoardStr
;end if
;           call sb_stop
;           xor  eax,eax
;           ret
@@:
	   cmp	eax,DEV_CALLBACK
	   jne	@f
if DEBUG
	   mov	esi,msgCallback
	   call SysMsgBoardStr
end if
	   mov	edi,[edi+input]
	   mov	eax,[edi]
	   mov	[callback],eax
if DEBUG
	   call SysMsgBoardNum
end if
	   xor	eax,eax
	   ret
@@:
	   cmp	eax,DEV_SET_MASTERVOL ;Serge asked me to unlock
	   jne	@F ;DEV_SET(GET)_MASTERVOL, although mixer doesn't use it.
	   ;It doesn't use it _in current version_ - but in the future...

if DEBUG
	   mov	esi,msgSetVol
	   call SysMsgBoardStr
end if
	   mov	eax,[edi+input]
	   mov	eax,[eax]
	   call sb_set_master_vol
	   xor	eax,eax
	   ret
@@:
	   cmp	eax,DEV_GET_MASTERVOL
	   jne	@F
if DEBUG
	   mov	esi,msgGetVol
	   call SysMsgBoardStr
end if
	   mov	eax,[edi+output]
	   mov	edx,[sb_master_vol]
	   mov	[eax],edx
	   xor	eax,eax
	   ret

.fail:
	   or eax, -1
	   ret
endp

restore   handle
restore   io_code
restore   input
restore   inp_size
restore   output
restore   out_size

;-------------------------------------------------------------------------------
align 4
proc sb_irq
	   mov	edx,[sb_base_port] ;tell the DSP that we have processed IRQ
	   add	dl,0xF		   ;0xF for 16 bit sound, 0xE for 8 bit sound
	   in	al,dx		   ;for non-stop sound

pre_fill_data:
	   mov	eax,int_flip_flop
	   not	dword[eax]
	   mov	eax,[eax]
	   test eax,eax
	   jns	.fill_second_half

if sb_buffer_size eq small_buffer
	   stdcall [callback],SB16Buffer0 ;for 32k buffer
else if sb_buffer_size eq full_buffer
	   stdcall [callback],SB16Buffer0 ;for 64k buffer
	   stdcall [callback],SB16Buffer1 ;for 64k buffer
end if
	   xor	eax,eax
	   ret

.fill_second_half:
if sb_buffer_size eq small_buffer
	   stdcall [callback],SB16Buffer1 ;for 32k buffer
else if sb_buffer_size eq full_buffer
	   stdcall [callback],SB16Buffer2 ;for 64k buffer
	   stdcall [callback],SB16Buffer3 ;for 64k buffer
end if
	   xor	eax,eax
	   ret
endp
;-------------------------------------------------------------------------------
align 4
proc detect
.sb_detect_next_port:
if DEBUG
	   inc	dword[port_second_digit_num]
end if
	   mov	edx,sb_base_port
	   add	byte[edx],10h
	   cmp	byte[edx],80h
	   jbe	.sb_try_to_detect_at_specified_port
;error - no SB card detected
.sb_not_found_err:
	   xor	eax, eax
	   ret

.sb_try_to_detect_at_specified_port:
	   call sb_reset
	   add	dl,8
	   mov	ecx,100
.sb_check_port:
	   in	al,dx
	   test al,al		 ;is DSP port ready to be read?
	   jns	.sb_port_not_ready

	   sub	dl,4
	   in	al,dx		 ;check for AAh response
	   add	dl,4
	   cmp	al,0xAA
	   jne	.sb_port_not_ready
.sb_card_found:
	   and	dl,0xF0
	   add	dl,0xC
	   sb_out 0xE1		 ;get DSP version
	   add	dl,2
@@:
	   in	al,dx
	   test al,al		 ;is DSP port ready to be read?
	   jns	@b
	   sub	dl,4
	   in	al,dx		 ;get major version
	   ror	eax,16
	   add	dl,4
@@:
	   in	al,dx
	   test al,al		 ;is DSP port ready to be read?
	   jns	@b
	   sub	dl,4
	   in	al,dx		 ;get minor version
	   xor	edx,edx
	   mov	dl,10
	   div	dl
	   ror	eax,16
	   xor	ah,ah
	   mov	[sb_DSP_version_int],eax ;for internal usage
if DEBUG
	   add	[sb_DSP_version],eax
end if
	   ret

.sb_port_not_ready:
	   loop .sb_check_port	 ;100 retries (~100 microsec.)
	   jmp	.sb_detect_next_port
endp
;-------------------------------------------------------------------------------
if DEBUG
proc SysMsgBoardNum ;warning: destroys eax,ebx,ecx,esi
	   mov	ebx,eax
	   mov	ecx,8
	   mov	esi,(number_to_out+1)
.1:
	   mov	eax,ebx
	   and	eax,0xF
	   add	al,'0'
	   cmp	al,(10+'0')
	   jb	@f
	   add	al,('A'-'0'-10)
@@:
	   mov	[esi+ecx],al
	   shr	ebx,4
	   loop .1
	   dec	esi
	   call SysMsgBoardStr
	   ret
endp
end if
;all initialized data place here
align 4
version       dd (5 shl 16) or (API_VERSION and 0xFFFF)

sb_base_port: dd 200h ;don't ask me why - see the code&docs

sound_dma     dd sb_dma_num

;note that 4th DMA channel doesn't exist, it is used for cascade
;plugging the first DMA controler to the second
dma_table     db 0x87,0x83,0x81,0x82,0xFF,0x8B,0x89,0x8A

my_service    db 'SOUND',0  ;max 16 chars include zero

if DEBUG
number_to_out db '0x00000000',13,10,0

msgInit       db 'detecting hardware...',13,10,0
msgExit       db 'exiting... May be some problems found?',13,10,0
msgPlay       db 'start play',13,10,0
;msgStop       db 'stop play',13,10,0
msgCallback   db 'set_callback received from the mixer!',13,10
	      db 'callback handler is: ',0
msgErrAtchIRQ db 'failed to attach IRQ',(sb_irq_num+'0'),13,10
	      db 'owner',39,'s handler: ',0
msgSucAtchIRQ db 'succesfully attached IRQ',(sb_irq_num+'0')
	      db ' as hardcoded',13,10,0
msgErrRsrvPorts db 'failed to reserve needed ports.',13,10
	      db 'Driver may work unstable',13,10,0
msgSetVol     db 'DEV_SET_MASTERVOL call came',13,10,0
msgGetVol     db 'DEV_GET_MASTERVOL call came',13,10,0
msgErrDMAsetup db 'failed to setup DMA - bad channel',13,10,0
;-------------------------------------------------------------------------------
msgDSPFound   db 'DSP found at port 2'
label port_second_digit_num dword at $
	      db '00h',13,10,'DSP version '
sb_DSP_version: db '0.00 - SB'
sb_DSP_description: db 32,32,32,32,13,10,0
;-------------------------------------------------------------------------------
end if

section '.data' data readable writable align 16
;all uninitialized data place here

;pTempBuf          rd 1

callback	   rd 1

int_flip_flop	   rd 1

sb_master_vol	   rd 1

sb_DSP_version_int rd 1